Skip to content

Commit e4eb986

Browse files
authored
Don't pass around references to the scrach buffer (#90)
Motivation ---------- The scratch buffer is primarily used on cold paths (split reads during streaming or handling the last few tags in a block). Therefore, the value in reducing range checks, etc, by passing the reference around is minimal. This is mostly a holdover from when we pinned the reference, which was more important due to pinning cost. Modifications ------------- Stop passing the reference to the subroutines, just make a reference as needed.
1 parent 11be394 commit e4eb986

File tree

1 file changed

+15
-19
lines changed

1 file changed

+15
-19
lines changed

Snappier/Internal/SnappyDecompressor.cs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,6 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
213213
ref byte bufferEnd = ref Unsafe.Add(ref buffer, _lookbackBuffer.Length);
214214
ref byte op = ref Unsafe.Add(ref buffer, _lookbackPosition);
215215

216-
// Get a reference to the first byte in the scratch buffer, we'll reuse this so that we don't repeat range checks every time
217-
ref byte scratch = ref _scratch[0];
218-
219216
if (_scratchLength > 0)
220217
{
221218
// Have partial tag remaining from a previous decompress run
@@ -227,7 +224,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
227224
// so that the stack size of this method is smaller and JIT can produce better results
228225

229226
(uint inputUsed, uint bytesWritten) =
230-
DecompressTagFromScratch(ref input, ref inputEnd, ref op, ref buffer, ref bufferEnd, ref scratch);
227+
DecompressTagFromScratch(ref input, ref inputEnd, ref op, ref buffer, ref bufferEnd);
231228
op = ref Unsafe.Add(ref op, bytesWritten);
232229

233230
if (inputUsed == 0)
@@ -251,7 +248,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
251248
{
252249
if (!Unsafe.IsAddressLessThan(ref input, ref inputLimitMinMaxTagLength))
253250
{
254-
uint newScratchLength = RefillTag(ref input, ref inputEnd, ref scratch);
251+
uint newScratchLength = RefillTag(ref input, ref inputEnd);
255252
if (newScratchLength == uint.MaxValue)
256253
{
257254
break;
@@ -260,7 +257,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
260257
if (newScratchLength > 0)
261258
{
262259
// Data has been moved to the scratch buffer
263-
input = ref scratch;
260+
input = ref _scratch[0];
264261
inputEnd = ref Unsafe.Add(ref input, newScratchLength);
265262
inputLimitMinMaxTagLength = ref Unsafe.Subtract(ref inputEnd,
266263
Math.Min(newScratchLength, Constants.MaximumTagLength - 1));
@@ -366,10 +363,10 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
366363
// Some of the input may have been used if 0 is returned, but it isn't relevant because
367364
// DecompressAllTags will short circuit.
368365
private (uint inputUsed, uint bytesWritten) DecompressTagFromScratch(ref byte input, ref byte inputEnd, ref byte op,
369-
ref byte buffer, ref byte bufferEnd, ref byte scratch)
366+
ref byte buffer, ref byte bufferEnd)
370367
{
371368
// scratch will be the scratch buffer with only the tag if true is returned
372-
uint inputUsed = RefillTagFromScratch(ref input, ref inputEnd, ref scratch);
369+
uint inputUsed = RefillTagFromScratch(ref input, ref inputEnd);
373370
if (inputUsed == 0)
374371
{
375372
return (0, 0);
@@ -379,8 +376,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
379376
// No more scratch for next cycle, we have a full buffer we're about to use
380377
_scratchLength = 0;
381378

382-
byte c = scratch;
383-
scratch = ref Unsafe.Add(ref scratch, 1);
379+
byte c = _scratch[0];
384380

385381
if ((c & 0x03) == Constants.Literal)
386382
{
@@ -389,7 +385,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
389385
{
390386
// Long literal.
391387
uint literalLengthLength = literalLength - 60;
392-
uint literalLengthTemp = Helpers.UnsafeReadUInt32(ref scratch);
388+
uint literalLengthTemp = Helpers.UnsafeReadUInt32(ref _scratch[1]);
393389

394390
literalLength = Helpers.ExtractLowBytes(literalLengthTemp,
395391
(int) literalLengthLength) + 1;
@@ -412,7 +408,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
412408
}
413409
else if ((c & 3) == Constants.Copy4ByteOffset)
414410
{
415-
uint copyOffset = Helpers.UnsafeReadUInt32(ref scratch);
411+
uint copyOffset = Helpers.UnsafeReadUInt32(ref _scratch[1]);
416412

417413
nint length = (c >> 2) + 1;
418414

@@ -423,7 +419,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
423419
else
424420
{
425421
ushort entry = Constants.CharTable[c];
426-
uint data = Helpers.UnsafeReadUInt32(ref scratch);
422+
uint data = Helpers.UnsafeReadUInt32(ref _scratch[1]);
427423

428424
uint trailer = Helpers.ExtractLowBytes(data, c & 3);
429425
nint length = entry & 0xff;
@@ -442,7 +438,7 @@ internal void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
442438
// Returns the amount of the input used, 0 indicates there was insufficient data.
443439
// Some of the input may have been used if 0 is returned, but it isn't relevant because
444440
// DecompressAllTags will short circuit.
445-
private uint RefillTagFromScratch(ref byte input, ref byte inputEnd, ref byte scratch)
441+
private uint RefillTagFromScratch(ref byte input, ref byte inputEnd)
446442
{
447443
Debug.Assert(_scratchLength > 0);
448444

@@ -452,11 +448,11 @@ private uint RefillTagFromScratch(ref byte input, ref byte inputEnd, ref byte sc
452448
}
453449

454450
// Read the tag character
455-
uint entry = Constants.CharTable[scratch];
451+
uint entry = Constants.CharTable[_scratch[0]];
456452
uint needed = (entry >> 11) + 1; // +1 byte for 'c'
457453

458454
uint toCopy = Math.Min((uint)Unsafe.ByteOffset(ref input, ref inputEnd), needed - _scratchLength);
459-
Unsafe.CopyBlockUnaligned(ref Unsafe.Add(ref scratch, _scratchLength), ref input, toCopy);
455+
Unsafe.CopyBlockUnaligned(ref _scratch[(int)_scratchLength], ref input, toCopy);
460456

461457
_scratchLength += toCopy;
462458

@@ -478,7 +474,7 @@ private uint RefillTagFromScratch(ref byte input, ref byte inputEnd, ref byte sc
478474
// Returns a small number if we have enough data for this tag but not enough to safely load preload without a buffer
479475
// overrun. In this case, further reads should be from scratch with a length up to the returned number. Scratch will
480476
// always have some extra bytes on the end so we don't risk buffer overruns.
481-
private uint RefillTag(ref byte input, ref byte inputEnd, ref byte scratch)
477+
private uint RefillTag(ref byte input, ref byte inputEnd)
482478
{
483479
if (!Unsafe.IsAddressLessThan(ref input, ref inputEnd))
484480
{
@@ -493,7 +489,7 @@ private uint RefillTag(ref byte input, ref byte inputEnd, ref byte scratch)
493489
if (inputLength < needed)
494490
{
495491
// Data is insufficient, copy to scratch
496-
Unsafe.CopyBlockUnaligned(ref scratch, ref input, inputLength);
492+
Unsafe.CopyBlockUnaligned(ref _scratch[0], ref input, inputLength);
497493

498494
_scratchLength = inputLength;
499495
return uint.MaxValue;
@@ -503,7 +499,7 @@ private uint RefillTag(ref byte input, ref byte inputEnd, ref byte scratch)
503499
{
504500
// Have enough bytes, but copy to scratch so that we do not
505501
// read past end of input
506-
Unsafe.CopyBlockUnaligned(ref scratch, ref input, inputLength);
502+
Unsafe.CopyBlockUnaligned(ref _scratch[0], ref input, inputLength);
507503

508504
return inputLength;
509505
}

0 commit comments

Comments
 (0)