// 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);
}
}