// Return true if it is suitable for continuing writing new transactions bool LoadTransactionLog(uint fileId, uint logOffset) { var inlineValueBuf = new byte[MaxValueSizeInlineInMemory]; var stack = new List <NodeIdxPair>(); var collectionFile = FileCollection.GetFile(fileId); var reader = collectionFile.GetExclusiveReader(); try { if (logOffset == 0) { FileTransactionLog.SkipHeader(reader); } else { reader.SkipBlock(logOffset); } if (reader.Eof) { return(true); } var afterTemporaryEnd = false; while (!reader.Eof) { var command = (KVCommandType)reader.ReadUInt8(); if (command == 0 && afterTemporaryEnd) { collectionFile.SetSize(reader.GetCurrentPosition() - 1); return(true); } afterTemporaryEnd = false; switch (command & KVCommandType.CommandMask) { case KVCommandType.CreateOrUpdateDeprecated: case KVCommandType.CreateOrUpdate: { if (_nextRoot == null) { return(false); } var keyLen = reader.ReadVInt32(); var valueLen = reader.ReadVInt32(); var key = new byte[keyLen]; reader.ReadBlock(key); var keyBuf = ByteBuffer.NewAsync(key); if ((command & KVCommandType.FirstParamCompressed) != 0) { _compression.DecompressKey(ref keyBuf); } var ctx = new CreateOrUpdateCtx { KeyPrefix = BitArrayManipulation.EmptyByteArray, Key = keyBuf, ValueFileId = fileId, ValueOfs = (uint)reader.GetCurrentPosition(), ValueSize = (command & KVCommandType.SecondParamCompressed) != 0 ? -valueLen : valueLen }; if (valueLen <= MaxValueSizeInlineInMemory && (command & KVCommandType.SecondParamCompressed) == 0) { reader.ReadBlock(inlineValueBuf, 0, valueLen); StoreValueInlineInMemory(ByteBuffer.NewSync(inlineValueBuf, 0, valueLen), out ctx.ValueOfs, out ctx.ValueSize); ctx.ValueFileId = 0; } else { reader.SkipBlock(valueLen); } _nextRoot.CreateOrUpdate(ctx); } break; case KVCommandType.EraseOne: { if (_nextRoot == null) { return(false); } var keyLen = reader.ReadVInt32(); var key = new byte[keyLen]; reader.ReadBlock(key); var keyBuf = ByteBuffer.NewAsync(key); if ((command & KVCommandType.FirstParamCompressed) != 0) { _compression.DecompressKey(ref keyBuf); } long keyIndex; var findResult = _nextRoot.FindKey(stack, out keyIndex, BitArrayManipulation.EmptyByteArray, keyBuf); if (findResult == FindResult.Exact) { _nextRoot.EraseRange(keyIndex, keyIndex); } } break; case KVCommandType.EraseRange: { if (_nextRoot == null) { return(false); } var keyLen1 = reader.ReadVInt32(); var keyLen2 = reader.ReadVInt32(); var key = new byte[keyLen1]; reader.ReadBlock(key); var keyBuf = ByteBuffer.NewAsync(key); if ((command & KVCommandType.FirstParamCompressed) != 0) { _compression.DecompressKey(ref keyBuf); } long keyIndex1; var findResult = _nextRoot.FindKey(stack, out keyIndex1, BitArrayManipulation.EmptyByteArray, keyBuf); if (findResult == FindResult.Previous) { keyIndex1++; } key = new byte[keyLen2]; reader.ReadBlock(key); keyBuf = ByteBuffer.NewAsync(key); if ((command & KVCommandType.SecondParamCompressed) != 0) { _compression.DecompressKey(ref keyBuf); } long keyIndex2; findResult = _nextRoot.FindKey(stack, out keyIndex2, BitArrayManipulation.EmptyByteArray, keyBuf); if (findResult == FindResult.Next) { keyIndex2--; } _nextRoot.EraseRange(keyIndex1, keyIndex2); } break; case KVCommandType.TransactionStart: if (!reader.CheckMagic(MagicStartOfTransaction)) { return(false); } _nextRoot = LastCommited.NewTransactionRoot(); break; case KVCommandType.CommitWithDeltaUlong: unchecked // overflow is expected in case commitUlong is decreasing but that should be rare { _nextRoot.CommitUlong += reader.ReadVUInt64(); } goto case KVCommandType.Commit; case KVCommandType.Commit: _nextRoot.TrLogFileId = fileId; _nextRoot.TrLogOffset = (uint)reader.GetCurrentPosition(); _lastCommited = _nextRoot; _nextRoot = null; break; case KVCommandType.Rollback: _nextRoot = null; break; case KVCommandType.EndOfFile: return(false); case KVCommandType.TemporaryEndOfFile: _lastCommited.TrLogFileId = fileId; _lastCommited.TrLogOffset = (uint)reader.GetCurrentPosition(); afterTemporaryEnd = true; break; default: _nextRoot = null; return(false); } } return(afterTemporaryEnd); } catch (EndOfStreamException) { _nextRoot = null; return(false); } }