Skip to content

Commit d1e2f53

Browse files
committed
Improved decompression performance for .NET 8+
1 parent 5957fa8 commit d1e2f53

File tree

3 files changed

+469
-12
lines changed

3 files changed

+469
-12
lines changed

src/ZstdSharp/Unsafe/Bitstream.cs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,5 +378,187 @@ private static uint BIT_endOfDStream(BIT_DStream_t* DStream)
378378
{
379379
return DStream->ptr == DStream->start && DStream->bitsConsumed == (uint)(sizeof(nuint) * 8) ? 1U : 0U;
380380
}
381+
382+
/*-********************************************************
383+
* bitStream decoding
384+
**********************************************************/
385+
/*! BIT_initDStream() :
386+
* Initialize a BIT_DStream_t.
387+
* `bitD` : a pointer to an already allocated BIT_DStream_t structure.
388+
* `srcSize` must be the *exact* size of the bitStream, in bytes.
389+
* @return : size of stream (== srcSize), or an errorCode if a problem is detected
390+
*/
391+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
392+
private static nuint BIT_initDStream(ref BIT_DStream_t bitD, void* srcBuffer, nuint srcSize)
393+
{
394+
if (srcSize < 1)
395+
{
396+
memset(ref bitD, 0, (uint)sizeof(BIT_DStream_t));
397+
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
398+
}
399+
400+
bitD.start = (sbyte*)srcBuffer;
401+
bitD.limitPtr = bitD.start + sizeof(nuint);
402+
if (srcSize >= (nuint)sizeof(nuint))
403+
{
404+
bitD.ptr = (sbyte*)srcBuffer + srcSize - sizeof(nuint);
405+
bitD.bitContainer = MEM_readLEST(bitD.ptr);
406+
{
407+
byte lastByte = ((byte*)srcBuffer)[srcSize - 1];
408+
bitD.bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0;
409+
if (lastByte == 0)
410+
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
411+
}
412+
}
413+
else
414+
{
415+
bitD.ptr = bitD.start;
416+
bitD.bitContainer = *(byte*)bitD.start;
417+
switch (srcSize)
418+
{
419+
case 7:
420+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[6] << sizeof(nuint) * 8 - 16;
421+
goto case 6;
422+
case 6:
423+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[5] << sizeof(nuint) * 8 - 24;
424+
goto case 5;
425+
case 5:
426+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[4] << sizeof(nuint) * 8 - 32;
427+
goto case 4;
428+
case 4:
429+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[3] << 24;
430+
goto case 3;
431+
case 3:
432+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[2] << 16;
433+
goto case 2;
434+
case 2:
435+
bitD.bitContainer += (nuint)((byte*)srcBuffer)[1] << 8;
436+
goto default;
437+
default:
438+
break;
439+
}
440+
441+
{
442+
byte lastByte = ((byte*)srcBuffer)[srcSize - 1];
443+
bitD.bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0;
444+
if (lastByte == 0)
445+
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
446+
}
447+
448+
bitD.bitsConsumed += (uint)((nuint)sizeof(nuint) - srcSize) * 8;
449+
}
450+
451+
return srcSize;
452+
}
453+
454+
/*! BIT_lookBits() :
455+
* Provides next n bits from local register.
456+
* local register is not modified.
457+
* On 32-bits, maxNbBits==24.
458+
* On 64-bits, maxNbBits==56.
459+
* @return : value extracted */
460+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
461+
private static nuint BIT_lookBits(ref BIT_DStream_t bitD, uint nbBits)
462+
{
463+
return BIT_getMiddleBits(bitD.bitContainer, (uint)(sizeof(nuint) * 8) - bitD.bitsConsumed - nbBits, nbBits);
464+
}
465+
466+
/*! BIT_lookBitsFast() :
467+
* unsafe version; only works if nbBits >= 1 */
468+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
469+
[InlineMethod.Inline]
470+
private static nuint BIT_lookBitsFast(ref BIT_DStream_t bitD, uint nbBits)
471+
{
472+
uint regMask = (uint)(sizeof(nuint) * 8 - 1);
473+
assert(nbBits >= 1);
474+
return bitD.bitContainer << (int)(bitD.bitsConsumed & regMask) >> (int)(regMask + 1 - nbBits & regMask);
475+
}
476+
477+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
478+
[InlineMethod.Inline]
479+
private static void BIT_skipBits(ref BIT_DStream_t bitD, uint nbBits)
480+
{
481+
bitD.bitsConsumed += nbBits;
482+
}
483+
484+
/*! BIT_readBits() :
485+
* Read (consume) next n bits from local register and update.
486+
* Pay attention to not read more than nbBits contained into local register.
487+
* @return : extracted value. */
488+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
489+
private static nuint BIT_readBits(ref BIT_DStream_t bitD, uint nbBits)
490+
{
491+
nuint value = BIT_lookBits(ref bitD, nbBits);
492+
BIT_skipBits(ref bitD, nbBits);
493+
return value;
494+
}
495+
496+
/*! BIT_readBitsFast() :
497+
* unsafe version; only works if nbBits >= 1 */
498+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
499+
private static nuint BIT_readBitsFast(ref BIT_DStream_t bitD, uint nbBits)
500+
{
501+
nuint value = BIT_lookBitsFast(ref bitD, nbBits);
502+
assert(nbBits >= 1);
503+
BIT_skipBits(ref bitD, nbBits);
504+
return value;
505+
}
506+
507+
/*! BIT_reloadDStreamFast() :
508+
* Similar to BIT_reloadDStream(), but with two differences:
509+
* 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold!
510+
* 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this
511+
* point you must use BIT_reloadDStream() to reload.
512+
*/
513+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
514+
[InlineMethod.Inline]
515+
private static BIT_DStream_status BIT_reloadDStreamFast(ref BIT_DStream_t bitD)
516+
{
517+
if (bitD.ptr < bitD.limitPtr)
518+
return BIT_DStream_status.BIT_DStream_overflow;
519+
assert(bitD.bitsConsumed <= (uint)(sizeof(nuint) * 8));
520+
bitD.ptr -= bitD.bitsConsumed >> 3;
521+
bitD.bitsConsumed &= 7;
522+
bitD.bitContainer = MEM_readLEST(bitD.ptr);
523+
return BIT_DStream_status.BIT_DStream_unfinished;
524+
}
525+
526+
/*! BIT_reloadDStream() :
527+
* Refill `bitD` from buffer previously set in BIT_initDStream() .
528+
* This function is safe, it guarantees it will not read beyond src buffer.
529+
* @return : status of `BIT_DStream_t` internal register.
530+
* when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
531+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
532+
private static BIT_DStream_status BIT_reloadDStream(ref BIT_DStream_t bitD)
533+
{
534+
if (bitD.bitsConsumed > (uint)(sizeof(nuint) * 8))
535+
return BIT_DStream_status.BIT_DStream_overflow;
536+
if (bitD.ptr >= bitD.limitPtr)
537+
{
538+
return BIT_reloadDStreamFast(ref bitD);
539+
}
540+
541+
if (bitD.ptr == bitD.start)
542+
{
543+
if (bitD.bitsConsumed < (uint)(sizeof(nuint) * 8))
544+
return BIT_DStream_status.BIT_DStream_endOfBuffer;
545+
return BIT_DStream_status.BIT_DStream_completed;
546+
}
547+
548+
{
549+
uint nbBytes = bitD.bitsConsumed >> 3;
550+
BIT_DStream_status result = BIT_DStream_status.BIT_DStream_unfinished;
551+
if (bitD.ptr - nbBytes < bitD.start)
552+
{
553+
nbBytes = (uint)(bitD.ptr - bitD.start);
554+
result = BIT_DStream_status.BIT_DStream_endOfBuffer;
555+
}
556+
557+
bitD.ptr -= nbBytes;
558+
bitD.bitsConsumed -= nbBytes * 8;
559+
bitD.bitContainer = MEM_readLEST(bitD.ptr);
560+
return result;
561+
}
562+
}
381563
}
382564
}

0 commit comments

Comments
 (0)