author jdiaz5513
  import { PACK_SPAN_THRESHOLD } from "../constants" ; import { MSG_PACK_NOT_WORD_ALIGNED } from "../errors" ; enum { number } const enum PackedTag { /**
     * The tag is followed by a single byte which indicates a count of consecutive zero-valued words, minus 1. E.g. if the
     * tag 0x00 is followed by 0x05, the sequence unpacks to 6 words of zero.
     *
     * Or, put another way: the tag is first decoded as if it were not special. Since none of the bits are set, it is
     * followed by no bytes and expands to a word full of zeros. After that, the next byte is interpreted as a count of
     * additional words that are also all-zero.
     */ ZERO = 0x00 , /**
     * The tag is followed by the bytes of the word (as if it weren’t special), but after those bytes is another byte with
     * value N. Following that byte is N unpacked words that should be copied directly.
     *
     * These unpacked words may contain zeroes; in this implementation PACK_SPAN_THRESHOLD (or more) zero bytes within a
     * single word of a span terminates that span.
     *
     * The purpose of this rule is to minimize the impact of packing on data that doesn’t contain any zeros – in
     * particular, long text blobs. Because of this rule, the worst-case space overhead of packing is 2 bytes per 2 KiB of
     * input (256 words = 2KiB).
     */ SPAN = 0xff , /**
     * A randomly chosen non-ZERO, non-SPAN tag that proves useful in state initiation.
     *
     */ NONZERO_NONSPAN = 0x77 } param { number } x returns { number } export function getHammingWeight ( x ) { let w = x - ( ( x >> 1 ) & 0x55555555 ) ; w = ( w & 0x33333333 ) + ( ( w >> 2 ) & 0x33333333 ) ; return ( ( ( w + ( w >> 4 ) ) & 0x0f0f0f0f ) * 0x01010101 ) >> 24 ; } export type byte = number ; param { byte } a param { byte } b param { byte } c param { byte } d param { byte } e param { byte } f param { byte } g param { byte } h returns { number } export function getTagByte ( a , b , c , d , e , f , g , h ) { return ( ( a === 0 ? 0 : 0b00000001 ) | ( b === 0 ? 0 : 0b00000010 ) | ( c === 0 ? 0 : 0b00000100 ) | ( d === 0 ? 0 : 0b00001000 ) | ( e === 0 ? 0 : 0b00010000 ) | ( f === 0 ? 0 : 0b00100000 ) | ( g === 0 ? 0 : 0b01000000 ) | ( h === 0 ? 0 : 0b10000000 ) ) ; } export param { ArrayBuffer } packed returns { number } export function getUnpackedByteLength ( packed ) { const p = new Uint8Array ( packed ) ; let wordCount = 0 ; let lastTag = PackedTag . NONZERO_NONSPAN ; for ( let i = 0 ; i < p . byteLength ; ) { const tag = p [ i ] ; if ( lastTag === PackedTag . ZERO ) { wordCount += tag ; i ++ ; lastTag = PackedTag . NONZERO_NONSPAN ; } else if ( lastTag === PackedTag . SPAN ) { wordCount += tag ; i += tag * 8 + 1 ; lastTag = PackedTag . NONZERO_NONSPAN ; } else { wordCount ++ ; i += getHammingWeight ( tag ) + 1 ; lastTag = tag ; } } return wordCount * 8 ; } param { byte } a param { byte } b param { byte } c param { byte } d param { byte } e param { byte } f param { byte } g param { byte } h returns { number } export function getZeroByteCount ( a , b , c , d , e , f , g , h ) { return ( ( a === 0 ? 1 : 0 ) + ( b === 0 ? 1 : 0 ) + ( c === 0 ? 1 : 0 ) + ( d === 0 ? 1 : 0 ) + ( e === 0 ? 1 : 0 ) + ( f === 0 ? 1 : 0 ) + ( g === 0 ? 1 : 0 ) + ( h === 0 ? 1 : 0 ) ) ; } export param { ArrayBuffer } unpacked param { number } byteOffset param { number } byteLength returns { ArrayBuffer } export function pack ( unpacked , byteOffset = 0 , byteLength ? ) { if ( unpacked . byteLength % 8 !== 0 ) throw new Error ( MSG_PACK_NOT_WORD_ALIGNED ) ; const src = new Uint8Array ( unpacked , byteOffset , byteLength ) ; const dst = [ ] ; let lastTag = PackedTag . NONZERO_NONSPAN ; /** This is where we need to remember to write the SPAN tag (0xff). */ let spanWordCountOffset = 0 ; /** How many words have been copied during the current range. */ let rangeWordCount = 0 ; for ( let srcByteOffset = 0 ; srcByteOffset < src . byteLength ; srcByteOffset += 8 ) { /** Read in the entire word. Yes, this feels silly but it's fast! */ const a = src [ srcByteOffset ] ; const b = src [ srcByteOffset + 1 ] ; const c = src [ srcByteOffset + 2 ] ; const d = src [ srcByteOffset + 3 ] ; const e = src [ srcByteOffset + 4 ] ; const f = src [ srcByteOffset + 5 ] ; const g = src [ srcByteOffset + 6 ] ; const h = src [ srcByteOffset + 7 ] ; const tag = getTagByte ( a , b , c , d , e , f , g , h ) ; /** If this is true we'll skip the normal word write logic after the switch statement. */ let skipWriteWord = true ; switch ( lastTag ) { case PackedTag . ZERO : if ( tag !== PackedTag . ZERO || rangeWordCount >= 0xff ) { dst . push ( rangeWordCount ) ; rangeWordCount = 0 ; skipWriteWord = false ; } else { rangeWordCount ++ ; } break ; case PackedTag . SPAN : { const zeroCount = getZeroByteCount ( a , b , c , d , e , f , g , h ) ; if ( zeroCount >= PACK_SPAN_THRESHOLD || rangeWordCount >= 0xff ) { dst [ spanWordCountOffset ] = rangeWordCount ; rangeWordCount = 0 ; skipWriteWord = false ; } else { dst . push ( a , b , c , d , e , f , g , h ) ; rangeWordCount ++ ; } break ; } default : skipWriteWord = false ; break ; } if ( skipWriteWord ) continue ; dst . push ( tag ) ; lastTag = tag ; if ( a !== 0 ) dst . push ( a ) ; if ( b !== 0 ) dst . push ( b ) ; if ( c !== 0 ) dst . push ( c ) ; if ( d !== 0 ) dst . push ( d ) ; if ( e !== 0 ) dst . push ( e ) ; if ( f !== 0 ) dst . push ( f ) ; if ( g !== 0 ) dst . push ( g ) ; if ( h !== 0 ) dst . push ( h ) ; if ( tag === PackedTag . SPAN ) { spanWordCountOffset = dst . length ; dst . push ( 0 ) ; } } if ( lastTag === PackedTag . ZERO ) { dst . push ( rangeWordCount ) ; } else if ( lastTag === PackedTag . SPAN ) { dst [ spanWordCountOffset ] = rangeWordCount ; } return new Uint8Array ( dst ) . buffer ; } export param { ArrayBuffer } packed returns { ArrayBuffer } export function unpack ( packed ) { const src = new Uint8Array ( packed ) ; const dst = new Uint8Array ( new ArrayBuffer ( getUnpackedByteLength ( packed ) ) ) ; /** The last tag byte that we've seen - it starts at a "neutral" value. */ let lastTag = PackedTag . NONZERO_NONSPAN ; for ( let srcByteOffset = 0 , dstByteOffset = 0 ; srcByteOffset < src . byteLength ; ) { const tag = src [ srcByteOffset ] ; if ( lastTag === PackedTag . ZERO ) { dstByteOffset += tag * 8 ; srcByteOffset ++ ; lastTag = PackedTag . NONZERO_NONSPAN ; } else if ( lastTag === PackedTag . SPAN ) { const spanByteLength = tag * 8 ; dst . set ( src . subarray ( srcByteOffset + 1 , srcByteOffset + 1 + spanByteLength ) , dstByteOffset ) ; dstByteOffset += spanByteLength ; srcByteOffset += 1 + spanByteLength ; lastTag = PackedTag . NONZERO_NONSPAN ; } else { srcByteOffset ++ ; for ( let i = 1 ; i <= 0b10000000 ; i <<= 1 ) { if ( ( tag & i ) !== 0 ) dst [ dstByteOffset ] = src [ srcByteOffset ++ ] ; dstByteOffset ++ ; } lastTag = tag ; } } return dst . buffer ; }