/// <summary> /// Returns the next non-deleted record from the file, optionally marking it for deletion. /// </summary> /// <param name="deleteRecord">Pass <c>true</c> to mark the record for deletion.</param> /// <returns>The next record or <c>null</c> if there are no more records to be read.</returns> /// <exception cref="LogCorruptedException">Thrown when the log file is determined to be corrupted.</exception> private AppLogRecord Read(bool deleteRecord) { int magic; int cbRecord; int flags; int cFields; bool deleted; int nameIndex; bool isByteArray; byte[] data; int cbData; AppLogRecord record; long flagPos; long nextPos; if (!readMode) { throw new LogAccessException(readMode); } tryAgain : try { magic = file.ReadInt16(); if (magic == AppLog.RecordEnd) { // Seek back 2 bytes to keep the file position // at the RecordEnd marker. file.Seek(-2, SeekOrigin.Current); return(null); } if (magic != AppLog.RecordMagic) { throw new LogCorruptedException(); } cbRecord = file.ReadInt32(); flagPos = file.Position; deleted = (file.ReadByte() & 0x80) != 0; cFields = file.ReadInt16(); if (deleted) { record = null; } else { record = new AppLogRecord(schemaName, schemaVersion); } for (int i = 0; i < cFields; i++) { flags = file.ReadByte(); isByteArray = (flags & 0x80) != 0; if ((flags & 0x40) != 0) { nameIndex = ((flags & 0x3F) << 8) | file.ReadByte(); } else { nameIndex = flags & 0x3F; } if (nameIndex >= id2Field.Length) { throw new LogCorruptedException(); } cbData = file.ReadByte(); if ((cbData & 0x80) != 0) { // cbData holds the most significant 7-bits of the field // length and the next three bytes hold the least // significant 24-bits. cbData = ((cbData & 0x7F) << 24) | (file.ReadByte() << 16) | (file.ReadByte() << 8) | file.ReadByte(); } data = file.ReadBytes(cbData); if (record != null) { if (isByteArray) { record.Add(id2Field[nameIndex], data); } else { record.Add(id2Field[nameIndex], Helper.FromUTF8(data)); } } } if (record != null) { if (deleteRecord) { recDeleted = true; nextPos = file.Position; file.Position = flagPos; file.WriteByte(0x80); file.Position = nextPos; } if (file.Position != flagPos + cbRecord) { throw new LogCorruptedException(); } return(record); } else { goto tryAgain; } } catch { throw new LogCorruptedException(); } }
/// <summary> /// Appends the record to the log file. /// </summary> /// <param name="record">The record to be appended.</param> public void Write(AppLogRecord record) { long recLenPos; long nextPos; if (readMode) { throw new LogAccessException(readMode); } file.WriteInt16(AppLog.RecordMagic); recLenPos = file.Position; file.WriteInt32(0); // Leave space for the record length file.WriteByte(0); // Flags = 0 file.WriteInt16(record.Count); // Field Count foreach (DictionaryEntry entry in record) { string fieldName = ((string)entry.Key).ToLowerInvariant(); int fieldID; int flags; byte[] data; string s; // Map the field name to an ID, adding the field to // the map table if necessary if (!field2ID.TryGetValue(fieldName, out fieldID)) { fieldID = field2ID.Count; field2ID.Add(fieldName, fieldID); if (field2ID.Count > MaxFieldNames) { throw new LogException("Too many unique field names."); } } // Convert strings to UTF-8 byte arrays and set the // flag 0x80 bit if the data is a byte array. s = entry.Value as string; if (s == null) { flags = 0x80; data = (byte[])entry.Value; } else { flags = 0; data = Helper.ToUTF8(s); } // Combine the field ID into the flag bits and write the // flags out as well as the extended portion of the field // name ID (if necessary). if (fieldID > 0x3F) { flags |= 0x40; flags |= (fieldID >> 8) & 0x3F; file.WriteByte((byte)flags); file.WriteByte((byte)(fieldID & 0xFF)); } else { file.WriteByte((byte)(flags | fieldID)); } // Write out the variable length data length field. if (data.Length <= 127) { file.WriteByte((byte)data.Length); } else { file.WriteByte((byte)(0x80 | (data.Length >> 24))); file.WriteByte((byte)(data.Length >> 16)); file.WriteByte((byte)(data.Length >> 8)); file.WriteByte((byte)(data.Length)); } // Write out the value bytes file.WriteBytesNoLen(data); // Compute the overall length of the record and // go back and write it after the magic number. nextPos = file.Position; file.Position = recLenPos; file.WriteInt32((int)(nextPos - recLenPos - 4)); file.Position = nextPos; } cRecWritten++; }
/// <summary> /// Appends a record to the cached section of the log. If the cache has become /// large enough, then the method will commit records in the cache. /// </summary> /// <param name="record">The record to be written.</param> public void Write(AppLogRecord record) { appLog.Write(record); }