/// <summary> /// Writes the specified <paramref name="database"/> into the specified <paramref name="stream"/>. /// </summary> /// <param name="database">The palm database.</param> /// <param name="stream">The stream to write the database to.</param> public static async Task WriteAsync(PalmDatabase database, Stream stream) { Guard.NotNull(stream, nameof(stream)); Guard.Not(stream.CanSeek == false, nameof(stream.CanSeek)); Guard.Not(stream.CanWrite == false, nameof(stream.CanWrite)); var writer = new AsyncBinaryWriter(stream); var nameValue = new StringPalmValue(32, Encoding.UTF8, zeroTerminated: true); await nameValue.WriteValueAsync(writer, database.Name); var attributesValue = new EnumPalmValue <PalmDatabaseAttributes>(2); await attributesValue.WriteValueAsync(writer, database.Attributes); var versionValue = new UIntPalmValue(2); await versionValue.WriteValueAsync(writer, (uint)database.Version); var creationDateValue = new DateTimeOffsetPalmValue(); await creationDateValue.WriteValueAsync(writer, database.CreationDate); var modificationDateValue = new DateTimeOffsetPalmValue(); await modificationDateValue.WriteValueAsync(writer, database.ModificationDate); var lastBackupDateValue = new DateTimeOffsetPalmValue(); await lastBackupDateValue.WriteValueAsync(writer, database.LastBackupDate); var modificationNumberValue = new UIntPalmValue(4); await modificationNumberValue.WriteValueAsync(writer, database.ModificationNumber); var appInfoIdValue = new UIntPalmValue(4); var offsetOfAppInfoId = writer.Stream.Position; await appInfoIdValue.WriteValueAsync(writer, 0); var sortInfoIdValue = new UIntPalmValue(4); var offsetOfSortInfoId = writer.Stream.Position; await sortInfoIdValue.WriteValueAsync(writer, 0); var typeValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); await typeValue.WriteValueAsync(writer, database.Type); var creatorValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); await creatorValue.WriteValueAsync(writer, database.Creator); var uniqueIdSeedValue = new UIntPalmValue(4); await uniqueIdSeedValue.WriteValueAsync(writer, database.UniqueIdSeed); var nextRecordListIdValue = new UIntPalmValue(4); await nextRecordListIdValue.WriteValueAsync(writer, database.NextRecordListId); var numberOfRecordsValue = new UIntPalmValue(2); await numberOfRecordsValue.WriteValueAsync(writer, (uint)database.Records.Count); var recordUniqueIdsAndOffsetOfDataOffsets = new Dictionary <uint, long>(); //Write the records uint uniqueId = 0; foreach (var record in database.Records) { recordUniqueIdsAndOffsetOfDataOffsets.Add(uniqueId, stream.Position); var recordDataOffsetValue = new UIntPalmValue(4); await recordDataOffsetValue.WriteValueAsync(writer, 0); var recordAttributeValue = new ByteArrayPalmValue(1); await recordAttributeValue.WriteValueAsync(writer, new[] { record.Attributes }); var uniqueIdValue = new UIntPalmValue(3); await uniqueIdValue.WriteValueAsync(writer, uniqueId); record.UniqueId = uniqueId; uniqueId++; } var fillerValue = new ByteArrayPalmValue(2); await fillerValue.WriteValueAsync(writer, new byte[2]); //Write appInfo if (database.AppInfo.Any()) { var appInfoId = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(database.AppInfo.Length); await dataValue.WriteValueAsync(writer, database.AppInfo); var previousOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfAppInfoId, SeekOrigin.Begin); await appInfoIdValue.WriteValueAsync(writer, appInfoId); writer.Stream.Seek(previousOffset, SeekOrigin.Begin); } //Write sortInfo if (database.SortInfo.Any()) { var sortInfoId = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(database.SortInfo.Length); await dataValue.WriteValueAsync(writer, database.SortInfo); var previousOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfSortInfoId, SeekOrigin.Begin); await sortInfoIdValue.WriteValueAsync(writer, sortInfoId); writer.Stream.Seek(previousOffset, SeekOrigin.Begin); } //Write the record content foreach (var record in database.Records) { var offsetOfDataOffset = recordUniqueIdsAndOffsetOfDataOffsets[record.UniqueId]; var dataOffset = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(record.Data.Length); await dataValue.WriteValueAsync(writer, record.Data); var endOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfDataOffset, SeekOrigin.Begin); var recordDataOffsetValue = new UIntPalmValue(4); await recordDataOffsetValue.WriteValueAsync(writer, dataOffset); writer.Stream.Seek(endOffset, SeekOrigin.Begin); } }
/// <summary> /// Reads a <see cref="PalmDatabase"/> from the specified <paramref name="stream"/>. /// </summary> /// <param name="stream">The stream to read the database from.</param> public static async Task <PalmDatabase> ReadAsync(Stream stream) { Guard.NotNull(stream, nameof(stream)); Guard.Not(stream.CanSeek == false, nameof(stream.CanSeek)); Guard.Not(stream.CanRead == false, nameof(stream.CanRead)); var database = new PalmDatabase(); var reader = new AsyncBinaryReader(stream); var nameValue = new StringPalmValue(32, Encoding.UTF8, zeroTerminated: true); database.Name = await nameValue.ReadValueAsync(reader); var attributesValue = new EnumPalmValue <PalmDatabaseAttributes>(2); database.Attributes = await attributesValue.ReadValueAsync(reader); var versionValue = new UIntPalmValue(2); database.Version = (short)await versionValue.ReadValueAsync(reader); var creationDateValue = new DateTimeOffsetPalmValue(); database.CreationDate = await creationDateValue.ReadValueAsync(reader); var modificationDateValue = new DateTimeOffsetPalmValue(); database.ModificationDate = await modificationDateValue.ReadValueAsync(reader); var lastBackupDateValue = new DateTimeOffsetPalmValue(); database.LastBackupDate = await lastBackupDateValue.ReadValueAsync(reader); var modificationNumberValue = new UIntPalmValue(4); database.ModificationNumber = await modificationNumberValue.ReadValueAsync(reader); var appInfoIdValue = new UIntPalmValue(4); var appInfoOffset = await appInfoIdValue.ReadValueAsync(reader); var sortInfoIdValue = new UIntPalmValue(4); var sortInfoOffset = await sortInfoIdValue.ReadValueAsync(reader); var typeValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); database.Type = await typeValue.ReadValueAsync(reader); var creatorValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); database.Creator = await creatorValue.ReadValueAsync(reader); var uniqueIdSeedValue = new UIntPalmValue(4); database.UniqueIdSeed = await uniqueIdSeedValue.ReadValueAsync(reader); var nextRecordListIdValue = new UIntPalmValue(4); database.NextRecordListId = await nextRecordListIdValue.ReadValueAsync(reader); uint numberOfRecords = await new UIntPalmValue(2).ReadValueAsync(reader); var recordAndDataOffsets = new List <Tuple <PalmDatabaseRecord, uint> >(); //Read the records for (int i = 0; i < numberOfRecords; i++) { var recordDataOffset = await new UIntPalmValue(4).ReadValueAsync(reader); var recordAttribute = await new ByteArrayPalmValue(1).ReadValueAsync(reader); var uniqueId = await new UIntPalmValue(3).ReadValueAsync(reader); var record = new PalmDatabaseRecord { UniqueId = uniqueId, Attributes = recordAttribute[0], }; recordAndDataOffsets.Add(Tuple.Create(record, recordDataOffset)); } //Read appInfo and sortInfo var offsets = new List <uint> { appInfoOffset, sortInfoOffset, recordAndDataOffsets.Any() ? recordAndDataOffsets[0].Item2 : (uint)reader.Stream.Length }; offsets = offsets .Where(f => f > 0) .OrderBy(f => f) .ToList(); for (int i = 0; i < offsets.Count - 1; i++) { var startOffset = offsets[i]; var endOffset = offsets[i + 1]; reader.Stream.Seek(startOffset, SeekOrigin.Begin); var data = await new ByteArrayPalmValue((int)(endOffset - startOffset)).ReadValueAsync(reader); if (startOffset == appInfoOffset) { database.AppInfo = data; } else if (startOffset == sortInfoOffset) { database.SortInfo = data; } } if (database.AppInfo == null) { database.AppInfo = new byte[0]; } if (database.SortInfo == null) { database.SortInfo = new byte[0]; } //Fill all records with their content for (int i = 0; i < numberOfRecords; i++) { var currentRecord = recordAndDataOffsets[i]; var nextRecord = i == numberOfRecords - 1 ? null : recordAndDataOffsets[i + 1]; var startOffset = currentRecord.Item2; var nextStartOffset = nextRecord?.Item2 ?? (uint)reader.Stream.Length; reader.Stream.Seek(startOffset, SeekOrigin.Begin); var data = await new ByteArrayPalmValue((int)(nextStartOffset - startOffset)).ReadValueAsync(reader); currentRecord.Item1.Data = data; } database.Records = recordAndDataOffsets .Select(f => f.Item1) .ToList(); return(database); }
/// <summary> /// Writes the specified <paramref name="database"/> into the specified <paramref name="stream"/>. /// </summary> /// <param name="database">The palm database.</param> /// <param name="stream">The stream to write the database to.</param> public static async Task WriteAsync(PalmDatabase database, Stream stream) { Guard.NotNull(stream, nameof(stream)); Guard.Not(stream.CanSeek == false, nameof(stream.CanSeek)); Guard.Not(stream.CanWrite == false, nameof(stream.CanWrite)); var writer = new AsyncBinaryWriter(stream); var nameValue = new StringPalmValue(32, Encoding.UTF8, zeroTerminated:true); await nameValue.WriteValueAsync(writer, database.Name); var attributesValue = new EnumPalmValue<PalmDatabaseAttributes>(2); await attributesValue.WriteValueAsync(writer, database.Attributes); var versionValue = new UIntPalmValue(2); await versionValue.WriteValueAsync(writer, (uint)database.Version); var creationDateValue = new DateTimeOffsetPalmValue(); await creationDateValue.WriteValueAsync(writer, database.CreationDate); var modificationDateValue = new DateTimeOffsetPalmValue(); await modificationDateValue.WriteValueAsync(writer, database.ModificationDate); var lastBackupDateValue = new DateTimeOffsetPalmValue(); await lastBackupDateValue.WriteValueAsync(writer, database.LastBackupDate); var modificationNumberValue = new UIntPalmValue(4); await modificationNumberValue.WriteValueAsync(writer, database.ModificationNumber); var appInfoIdValue = new UIntPalmValue(4); var offsetOfAppInfoId = writer.Stream.Position; await appInfoIdValue.WriteValueAsync(writer, 0); var sortInfoIdValue = new UIntPalmValue(4); var offsetOfSortInfoId = writer.Stream.Position; await sortInfoIdValue.WriteValueAsync(writer, 0); var typeValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); await typeValue.WriteValueAsync(writer, database.Type); var creatorValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); await creatorValue.WriteValueAsync(writer, database.Creator); var uniqueIdSeedValue = new UIntPalmValue(4); await uniqueIdSeedValue.WriteValueAsync(writer, database.UniqueIdSeed); var nextRecordListIdValue = new UIntPalmValue(4); await nextRecordListIdValue.WriteValueAsync(writer, database.NextRecordListId); var numberOfRecordsValue = new UIntPalmValue(2); await numberOfRecordsValue.WriteValueAsync(writer, (uint)database.Records.Count); var recordUniqueIdsAndOffsetOfDataOffsets = new Dictionary<uint, long>(); //Write the records uint uniqueId = 0; foreach (var record in database.Records) { recordUniqueIdsAndOffsetOfDataOffsets.Add(uniqueId, stream.Position); var recordDataOffsetValue = new UIntPalmValue(4); await recordDataOffsetValue.WriteValueAsync(writer, 0); var recordAttributeValue = new ByteArrayPalmValue(1); await recordAttributeValue.WriteValueAsync(writer, new[] {record.Attributes}); var uniqueIdValue = new UIntPalmValue(3); await uniqueIdValue.WriteValueAsync(writer, uniqueId); record.UniqueId = uniqueId; uniqueId++; } var fillerValue = new ByteArrayPalmValue(2); await fillerValue.WriteValueAsync(writer, new byte[2]); //Write appInfo if (database.AppInfo.Any()) { var appInfoId = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(database.AppInfo.Length); await dataValue.WriteValueAsync(writer, database.AppInfo); var previousOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfAppInfoId, SeekOrigin.Begin); await appInfoIdValue.WriteValueAsync(writer, appInfoId); writer.Stream.Seek(previousOffset, SeekOrigin.Begin); } //Write sortInfo if (database.SortInfo.Any()) { var sortInfoId = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(database.SortInfo.Length); await dataValue.WriteValueAsync(writer, database.SortInfo); var previousOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfSortInfoId, SeekOrigin.Begin); await sortInfoIdValue.WriteValueAsync(writer, sortInfoId); writer.Stream.Seek(previousOffset, SeekOrigin.Begin); } //Write the record content foreach (var record in database.Records) { var offsetOfDataOffset = recordUniqueIdsAndOffsetOfDataOffsets[record.UniqueId]; var dataOffset = (uint)writer.Stream.Position; var dataValue = new ByteArrayPalmValue(record.Data.Length); await dataValue.WriteValueAsync(writer, record.Data); var endOffset = writer.Stream.Position; writer.Stream.Seek(offsetOfDataOffset, SeekOrigin.Begin); var recordDataOffsetValue = new UIntPalmValue(4); await recordDataOffsetValue.WriteValueAsync(writer, dataOffset); writer.Stream.Seek(endOffset, SeekOrigin.Begin); } }
/// <summary> /// Reads a <see cref="PalmDatabase"/> from the specified <paramref name="stream"/>. /// </summary> /// <param name="stream">The stream to read the database from.</param> public static async Task<PalmDatabase> ReadAsync(Stream stream) { Guard.NotNull(stream, nameof(stream)); Guard.Not(stream.CanSeek == false, nameof(stream.CanSeek)); Guard.Not(stream.CanRead == false, nameof(stream.CanRead)); var database = new PalmDatabase(); var reader = new AsyncBinaryReader(stream); var nameValue = new StringPalmValue(32, Encoding.UTF8, zeroTerminated: true); database.Name = await nameValue.ReadValueAsync(reader); var attributesValue = new EnumPalmValue<PalmDatabaseAttributes>(2); database.Attributes = await attributesValue.ReadValueAsync(reader); var versionValue = new UIntPalmValue(2); database.Version = (short)await versionValue.ReadValueAsync(reader); var creationDateValue = new DateTimeOffsetPalmValue(); database.CreationDate = await creationDateValue.ReadValueAsync(reader); var modificationDateValue = new DateTimeOffsetPalmValue(); database.ModificationDate = await modificationDateValue.ReadValueAsync(reader); var lastBackupDateValue = new DateTimeOffsetPalmValue(); database.LastBackupDate = await lastBackupDateValue.ReadValueAsync(reader); var modificationNumberValue = new UIntPalmValue(4); database.ModificationNumber = await modificationNumberValue.ReadValueAsync(reader); var appInfoIdValue = new UIntPalmValue(4); var appInfoOffset = await appInfoIdValue.ReadValueAsync(reader); var sortInfoIdValue = new UIntPalmValue(4); var sortInfoOffset = await sortInfoIdValue.ReadValueAsync(reader); var typeValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); database.Type = await typeValue.ReadValueAsync(reader); var creatorValue = new StringPalmValue(4, Encoding.UTF8, zeroTerminated: false); database.Creator = await creatorValue.ReadValueAsync(reader); var uniqueIdSeedValue = new UIntPalmValue(4); database.UniqueIdSeed = await uniqueIdSeedValue.ReadValueAsync(reader); var nextRecordListIdValue = new UIntPalmValue(4); database.NextRecordListId = await nextRecordListIdValue.ReadValueAsync(reader); uint numberOfRecords = await new UIntPalmValue(2).ReadValueAsync(reader); var recordAndDataOffsets = new List<Tuple<PalmDatabaseRecord, uint>>(); //Read the records for (int i = 0; i < numberOfRecords; i++) { var recordDataOffset = await new UIntPalmValue(4).ReadValueAsync(reader); var recordAttribute = await new ByteArrayPalmValue(1).ReadValueAsync(reader); var uniqueId = await new UIntPalmValue(3).ReadValueAsync(reader); var record = new PalmDatabaseRecord { UniqueId = uniqueId, Attributes = recordAttribute[0], }; recordAndDataOffsets.Add(Tuple.Create(record, recordDataOffset)); } //Read appInfo and sortInfo var offsets = new List<uint> { appInfoOffset, sortInfoOffset, recordAndDataOffsets.Any() ? recordAndDataOffsets[0].Item2 : (uint)reader.Stream.Length }; offsets = offsets .Where(f => f > 0) .OrderBy(f => f) .ToList(); for (int i = 0; i < offsets.Count - 1; i++) { var startOffset = offsets[i]; var endOffset = offsets[i + 1]; reader.Stream.Seek(startOffset, SeekOrigin.Begin); var data = await new ByteArrayPalmValue((int)(endOffset - startOffset)).ReadValueAsync(reader); if (startOffset == appInfoOffset) { database.AppInfo = data; } else if (startOffset == sortInfoOffset) { database.SortInfo = data; } } if (database.AppInfo == null) database.AppInfo = new byte[0]; if (database.SortInfo == null) database.SortInfo = new byte[0]; //Fill all records with their content for (int i = 0; i < numberOfRecords; i++) { var currentRecord = recordAndDataOffsets[i]; var nextRecord = i == numberOfRecords - 1 ? null : recordAndDataOffsets[i + 1]; var startOffset = currentRecord.Item2; var nextStartOffset = nextRecord?.Item2 ?? (uint)reader.Stream.Length; reader.Stream.Seek(startOffset, SeekOrigin.Begin); var data = await new ByteArrayPalmValue((int)(nextStartOffset - startOffset)).ReadValueAsync(reader); currentRecord.Item1.Data = data; } database.Records = recordAndDataOffsets .Select(f => f.Item1) .ToList(); return database; }