@ -5,15 +5,23 @@ const TINF_DATA_ERROR = -3;
class Tree {
table = new Uint16Array ( 16 ) ; /* table of code length counts */
trans = new Uint16Array ( 288 ) ; /* code -> symbol translation table */
}
type HCtree = [ number | HCtree , ( number | HCtree ) ? ] ;
let log = ( x , . . . n ) = > {
//console.log('t2', x, ...n);
}
// these two functions are a big bottleneck because im not clever enough to figure out how to encode
// something directly by using the sorted code length/value tables, haha
const getPathTo = ( tree : HCtree , value : number ) : string | undefined = > {
let pathMap = new Map < HCtree , Map < number , {
length : number ,
val : number
} >> ( ) ;
let getPathTo = ( tree : HCtree , value : number ) : string | undefined = > {
if ( tree [ 0 ] === value )
return '0' ;
if ( tree [ 1 ] === value )
@ -35,7 +43,6 @@ const getPathTo = (tree: HCtree, value: number): string | undefined => {
// from jpeg-js, in turns this means that jpeg-js decoding could be faster
// if they decoded directly from the symbol tables instead of building a tree
function buildHuffmanTable ( codeLengths : ArrayLike < number > , values : ArrayLike < number > ) {
// eslint-disable-next-line prefer-const
let k = 0 , code : any = [ ] , i , j , length = 16 ;
while ( length > 0 && ! codeLengths [ length - 1 ] )
length -- ;
@ -77,11 +84,9 @@ class Data {
}
ltree : Tree ;
dtree : Tree ;
rltree ! : HCtree ;
rdtree ! : HCtree ;
adists ! : Set < number > ;
@ -98,34 +103,31 @@ class Data {
* -- uninitialized global data ( static structures ) -- *
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
cons t sltree = new Tree ( ) ;
cons t sdtree = new Tree ( ) ;
le t sltree = new Tree ( ) ;
le t sdtree = new Tree ( ) ;
// eslint-disable-next-line prefer-const
let rltree : HCtree ;
// eslint-disable-next-line prefer-const
let rdtree : HCtree ;
// eslint-disable-next-line prefer-const
let sadist : Set < number > ;
/* extra bits and base tables for length codes */
cons t length_bits = new Uint8Array ( 30 ) ;
cons t length_base = new Uint16Array ( 30 ) ;
le t length_bits = new Uint8Array ( 30 ) ;
le t length_base = new Uint16Array ( 30 ) ;
/* extra bits and base tables for distance codes */
cons t dist_bits = new Uint8Array ( 30 ) ;
cons t dist_base = new Uint16Array ( 30 ) ;
le t dist_bits = new Uint8Array ( 30 ) ;
le t dist_base = new Uint16Array ( 30 ) ;
/* special ordering of code length codes */
cons t clcidx = new Uint8Array ( [
le t clcidx = new Uint8Array ( [
16 , 17 , 18 , 0 , 8 , 7 , 9 , 6 ,
10 , 5 , 11 , 4 , 12 , 3 , 13 , 2 ,
14 , 1 , 15
] ) ;
/* used by tinf_decode_trees, avoids allocations every call */
cons t code_tree = new Tree ( ) ;
cons t lengths = new Uint8Array ( 288 + 32 ) ;
le t code_tree = new Tree ( ) ;
le t lengths = new Uint8Array ( 288 + 32 ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - *
* -- utility functions -- *
@ -171,7 +173,7 @@ function tinf_build_fixed_trees(lt: Tree, dt: Tree) {
}
/* given an array of code lengths, build a tree */
cons t offs = new Uint16Array ( 16 ) ;
le t offs = new Uint16Array ( 16 ) ;
function tinf_build_tree ( t : Tree , lengths : Uint8Array , off : number , num : number ) {
let i , sum ;
@ -202,15 +204,25 @@ function tinf_build_tree(t: Tree, lengths: Uint8Array, off: number, num: number)
/* get one bit from source stream */
function tinf_getbit ( d : Data ) {
const v = d . source . readSync ( 1 ) ;
let v = d . source . readSync ( 1 ) ;
log ( v , 'getbit' ) ;
return v ;
}
let loff = 0 ;
let loffs : number [ ] = [ ] ;
/* read a num bit value from a stream and add base */
function tinf_read_bits ( d : Data , num : number , base : number ) {
if ( ! num )
return base ;
const v = d . source . readSync ( num ) + base ;
let v = d . source . readSync ( num ) + base ;
loff = v ;
//console.log(v);
loffs . push ( v ) ;
if ( loffs . length > 4 ) {
loffs . shift ( ) ;
}
return v ;
}
@ -221,7 +233,7 @@ function tinf_decode_symbol(d: Data, t: Tree, copy = true, ext: any = {}) {
/* get more bits while code value is above sum */
let s = 0 ;
do {
cons t b = d . source . readSync ( 1 ) ;
le t b = d . source . readSync ( 1 ) ;
copy && d . hidden . write ( 1 , b ) ;
s = ( s << 1 ) | b ;
cur = 2 * cur + b ;
@ -235,29 +247,45 @@ function tinf_decode_symbol(d: Data, t: Tree, copy = true, ext: any = {}) {
return t . trans [ sum + cur ] ;
}
function tinf_decode_symbol2 ( d : Data , t : Tree ) {
let sum = 0 , cur = 0 , len = 0 ;
/* get more bits while code value is above sum */
do {
let b = d . source . readSync ( 1 ) ;
//d.hidden.write(1, b);
cur = 2 * cur + b ;
++ len ;
sum += t . table [ len ] ;
cur -= t . table [ len ] ;
} while ( cur >= 0 ) ;
return t . trans [ sum + cur ] ;
}
/* given a data stream, decode dynamic trees from it */
function tinf_decode_trees ( d : Data , lt : Tree , dt : Tree ) {
function tinf_decode_trees ( d : Data , lt : Tree , dt : Tree , copy = true ) {
let i , num , length ;
/* get 5 bits HLIT (257-286) */
const hlit = tinf_read_bits ( d , 5 , 257 ) ;
d . hidden ? . write ( 5 , hlit - 257 ) ;
le t hlit = tinf_read_bits ( d , 5 , 257 ) ;
copy && d . hidden ? . write ( 5 , hlit - 257 ) ;
/* get 5 bits HDIST (1-32) */
const hdist = tinf_read_bits ( d , 5 , 1 ) ;
d . hidden ? . write ( 5 , hdist - 1 ) ;
le t hdist = tinf_read_bits ( d , 5 , 1 ) ;
copy && d . hidden ? . write ( 5 , hdist - 1 ) ;
/* get 4 bits HCLEN (4-19) */
const hclen = tinf_read_bits ( d , 4 , 4 ) ;
d . hidden ? . write ( 4 , hclen - 4 ) ;
le t hclen = tinf_read_bits ( d , 4 , 4 ) ;
copy && d . hidden ? . write ( 4 , hclen - 4 ) ;
for ( i = 0 ; i < 19 ; ++ i ) lengths [ i ] = 0 ;
/* read code lengths for code length alphabet */
for ( i = 0 ; i < hclen ; ++ i ) {
/* get 3 bits code length (0-7) */
cons t clen = tinf_read_bits ( d , 3 , 0 ) ;
d . hidden ? . write ( 3 , clen ) ;
le t clen = tinf_read_bits ( d , 3 , 0 ) ;
copy && d . hidden ? . write ( 3 , clen ) ;
lengths [ clcidx [ i ] ] = clen ;
}
@ -267,14 +295,14 @@ function tinf_decode_trees(d: Data, lt: Tree, dt: Tree) {
/* decode code lengths for the dynamic trees */
for ( num = 0 ; num < hlit + hdist ; ) {
cons t sym = tinf_decode_symbol ( d , code_tree ) ;
let prev : number ;
le t sym = tinf_decode_symbol ( d , code_tree , copy ) ;
switch ( sym ) {
case 16 :
/* copy previous code length 3-6 times (read 2 bits) */
prev = lengths [ num - 1 ] ;
let prev = lengths [ num - 1 ] ;
length = tinf_read_bits ( d , 2 , 3 ) ;
d . hidden ? . write ( 2 , length - 3 ) ;
copy && d . hidden ? . write ( 2 , length - 3 ) ;
for ( ; length ; -- length ) {
lengths [ num ++ ] = prev ;
}
@ -282,7 +310,7 @@ function tinf_decode_trees(d: Data, lt: Tree, dt: Tree) {
case 17 :
/* repeat code length 0 for 3-10 times (read 3 bits) */
length = tinf_read_bits ( d , 3 , 3 ) ;
d . hidden ? . write ( 3 , length - 3 ) ;
copy && d . hidden ? . write ( 3 , length - 3 ) ;
for ( ; length ; -- length ) {
lengths [ num ++ ] = 0 ;
}
@ -290,7 +318,7 @@ function tinf_decode_trees(d: Data, lt: Tree, dt: Tree) {
case 18 :
/* repeat code length 0 for 11-138 times (read 7 bits) */
length = tinf_read_bits ( d , 7 , 11 ) ;
d . hidden ? . write ( 7 , length - 11 ) ;
copy && d . hidden ? . write ( 7 , length - 11 ) ;
for ( ; length ; -- length ) {
lengths [ num ++ ] = 0 ;
}
@ -307,51 +335,46 @@ function tinf_decode_trees(d: Data, lt: Tree, dt: Tree) {
tinf_build_tree ( dt , lengths , hlit , hdist ) ;
}
const bufferEq = ( a : Uint8Array , b : Uint8Array ) = > {
// this is assumed
// if (a.byteLength != b.byteLength)
// return false;
for ( let i = 0 ; i < a . byteLength ; ++ i )
if ( a [ i ] != b [ i ] )
return i ;
return - 1 ;
} ;
const get_symbol = ( value : number , bits_table : Uint8Array , base_table : Uint16Array ) : [ number , number , number ] = > {
let i = 0 ;
for ( i = 0 ; i < base_table . length ; ++ i ) {
if ( base_table [ i ] > value ) {
i -- ;
return [ i , bits_table [ i ] , value - base_table [ i ] ] ;
return [ i , bits_table [ i ] , value - base_table [ i ] ]
}
}
i -- ;
return [ i , bits_table [ i ] , value - base_table [ i ] ] ;
return [ i , bits_table [ i ] , value - base_table [ i ] ]
} ;
const encode_symbol = ( sym : number , tree : HCtree ) = > {
let m : Map < number , { length : number , val : number } > | undefined ;
if ( ( m = pathMap . get ( tree ) ) ) {
let v = m . get ( sym ) ;
if ( v ) return v ;
} else {
m = new Map ;
pathMap . set ( tree , m ) ;
}
const code = getPathTo ( tree , sym ) ! ;
return {
let v = {
length : code?.length ,
val : parseInt ( code , 2 )
} ;
m . set ( sym , v ) ;
return v ;
} ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
* -- block inflate functions -- *
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
/* given a stream and two trees, inflate a block of data */
export let capacity = 0 ;
function tinf_inflate_block_data ( d : Data , lt : Tree , dt : Tree ) {
let finished = false ;
// eslint-disable-next-line no-constant-condition
while ( 1 ) {
if ( finished ) {
return true ;
}
let sym = tinf_decode_symbol ( d , lt ) ; // copy
/* check for end of block */
@ -363,28 +386,31 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
d . dest . push ( sym ) ;
// same
} else {
let length , dist , offs ;
sym -= 257 ;
/* possibly get more bits from length code */
const length = tinf_read_bits ( d , length_bits [ sym ] , length_base [ sym ] ) ;
length = tinf_read_bits ( d , length_bits [ sym ] , length_base [ sym ] ) ;
//d.hidden.write(length_bits[sym], length - length_base[sym]);
// length is unchanged, so copy as is
if ( length_bits [ sym ] )
d . hidden . write ( length_bits [ sym ] , length - length_base [ sym ] ) ;
cons t ext = { length : 0 , sym : 0 } ;
const dist = tinf_decode_symbol ( d , dt , false , ext ) ; // don't copy immediately, we may change the code
le t ext = { length : 0 , sym : 0 } ;
dist = tinf_decode_symbol ( d , dt , false , ext ) ; // don't copy immediately, we may change the code
//ext.sym = revbyte(ext.sym, ext.length);
//d.hidden.write(ext.length, ext.sym);
/* possibly get more bits from distance code */
let backoffset = tinf_read_bits ( d , dist_bits [ dist ] , dist_base [ dist ] ) ;
const offs = d . dest . length - backoffset ;
const match = Buffer . from ( d . dest . slice ( offs , offs + length ) ) ;
offs = d . dest . length - backoffset ;
let match : Buffer ;
// don't consider matches that could be in the lookahead buffer
if ( match . length == length ) {
let skip = d . to_hide instanceof BitstreamReader && d . to_hide . available == 0 ;
if ( ! skip && ( match = Buffer . from ( d . dest . slice ( offs , offs + length ) ) ) . length == length ) {
let begin = d . dest . length - 32768 ;
if ( begin < 0 )
begin = 0 ;
@ -392,7 +418,7 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
let o = 0 ;
const slic = Buffer . from ( d . dest . slice ( begin + o , d . dest . length ) ) ;
while ( begin + o < d . dest . length ) {
cons t r = slic . slice ( o , d . dest . length ) . indexOf ( match ) ;
le t r = slic . slice ( o , d . dest . length ) . indexOf ( match ) ;
if ( r >= 0 ) {
matches . push ( r + begin + o ) ;
o += r ;
@ -406,7 +432,7 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
matches = matches . map ( e = > - ( e - d . dest . length ) ) . filter ( e = > {
const [ dsym ] = get_symbol ( e , dist_bits , dist_base ) ;
return d . adists . has ( dsym ) ;
} ) ;
} )
matches . reverse ( ) ;
const v = Math . floor ( Math . log2 ( matches . length ) ) ;
capacity += v ;
@ -416,8 +442,6 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
if ( d . to_hide . available ) {
const s = d . to_hide . readSync ( Math . min ( d . to_hide . available , v ) ) ;
backoffset = matches [ s ] ;
} else {
finished = true ;
}
}
// extract hidden bit
@ -432,15 +456,12 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
//let enclen = encode_symbol(lsym, d.rltree);
//d.hidden.write(enclen.length, enclen.val);
//d.hidden.write(llen, loff);
cons t [ dsym , dlen , doff ] = get_symbol ( backoffset , dist_bits , dist_base ) ;
le t [ dsym , dlen , doff ] = get_symbol ( backoffset , dist_bits , dist_base ) ;
let encdist = encode_symbol ( dsym , d . rdtree ) ;
if ( isNaN ( encdist . val ) ) {
debugger ;
encdist = encode_symbol ( dsym , d . rdtree ) ;
}
d . hidden . write ( encdist . length , revbyte ( encdist . val , encdist . length ) ) ;
d . hidden . write ( dlen , doff ) ;
/* copy match */
for ( let i = offs ; i < offs + length ; ++ i ) {
d . dest . push ( d . dest [ i ] ) ;
@ -451,17 +472,23 @@ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree) {
/* inflate an uncompressed block of data */
function tinf_inflate_uncompressed_block ( d : Data ) {
/* get length */
const length = d . source . readSync ( 16 ) ;
if ( d . source . offset & 7 )
d . source . readSync ( 8 - d . source . offset & 7 ) ;
if ( d . hidden . offset & 7 )
d . hidden . write ( 8 - d . hidden . offset & 7 , 0 ) ;
let length = d . source . readSync ( 16 ) ;
d . hidden . write ( 16 , length ) ;
/* get one's complement of length */
const invlength = d . source . readSync ( 16 ) ;
le t invlength = d . source . readSync ( 16 ) ;
d . hidden . write ( 16 , invlength ) ;
/* check length */
if ( length !== ( ~ invlength & 0x0000ffff ) )
if ( length !== ( ~ invlength & 0x0000ffff ) ) {
//console.log(length, invlength, loff)
return - 4 ;
}
for ( let i = length ; i ; -- i ) {
const v = d . source . readSync ( 8 ) ;
d . dest . push ( v ) ;
@ -470,6 +497,49 @@ function tinf_inflate_uncompressed_block(d: Data) {
return TINF_OK ;
}
function copy_block_data ( d : Data , lt : Tree , dt : Tree ) {
while ( 1 ) {
let sym = tinf_decode_symbol ( d , lt , false ) ; // copy
/* check for end of block */
if ( sym === 256 ) {
break ;
}
if ( sym < 256 ) {
//d.dest.push(sym);
// same
} else {
let length , dist , offs ;
sym -= 257 ;
length = tinf_read_bits ( d , length_bits [ sym ] , length_base [ sym ] ) ;
dist = tinf_decode_symbol2 ( d , dt ) ;
tinf_read_bits ( d , dist_bits [ dist ] , dist_base [ dist ] ) ;
}
}
}
function copy_uncompressed_block ( d : Data ) {
// align
if ( d . source . offset & 7 )
d . source . readSync ( 8 - d . source . offset & 7 ) ;
if ( d . hidden . offset & 7 )
d . hidden . write ( 8 - d . hidden . offset & 7 , 0 ) ;
let length : number ;
d . hidden . write ( 16 , length = d . source . readSync ( 16 ) ) ;
d . hidden . write ( 16 , d . source . readSync ( 16 ) ) ;
let total = length * 8 ;
while ( total ) {
const r = Math . min ( 32 , total ) ;
d . hidden . write ( r , d . source . readSync ( r ) ) ;
total -= r ;
}
return TINF_OK ;
}
/* inflate stream from source to dest */
export function tinf_uncompress ( source : BitstreamReader ,
// normal decompressed data
@ -477,28 +547,16 @@ export function tinf_uncompress(source: BitstreamReader,
// stream of data to hide
to_hide : BitstreamReader | BitstreamWriter ,
// compressed stream containing hidden data
hidden : ( chunk : Uint8Array ) = > void ) {
hidden : ( chunk : Uint8Array ) = > void , opt = 0 ) {
cons t decomp = new BitstreamWriter ( { write : ( decompressed || ( ( ) = > { /** */ } ) ) } ) ;
cons t hid = new BitstreamWriter ( { write : ( hidden || ( ( ) = > { /** */ } ) ) } ) ;
le t decomp = new BitstreamWriter ( { write : ( decompressed || ( ( ) = > { } ) ) } ) ;
le t hid = new BitstreamWriter ( { write : ( hidden || ( ( ) = > { } ) ) } , 4 ) ;
cons t d = new Data ( source , decomp , to_hide , hid ) ;
le t d = new Data ( source , decomp , to_hide , hid ) ;
let res : number | undefined | true ;
let bfinal : number , btype : number ;
do {
if ( to_hide instanceof BitstreamReader ) {
if ( to_hide . available == 0 ) {
// copy until we're byte-aligned
while ( source . available ) {
const r = 1 ;
hid . write ( r , source . readSync ( r ) ) ;
}
// nothing left to embed, we are byte aligned, so we just "memcpy" the rest
return source . offset >> 3 ; // this is block aligned, so this doesn't tell us where the last hidden bit is, just an upper bound
}
}
// TODO: truncate to_hide if we get close to 70k?
/* read final block flag */
@ -522,16 +580,12 @@ export function tinf_uncompress(source: BitstreamReader,
d . rltree = rltree ;
d . adists = sadist ;
res = tinf_inflate_block_data ( d , sltree , sdtree ) ;
if ( res === true )
continue ;
break ;
case 2 :
/* decompress block with dynamic huffman trees */
tinf_decode_trees ( d , d . ltree , d . dtree ) ;
d . computeReverse ( ) ;
res = tinf_inflate_block_data ( d , d . ltree , d . dtree ) ;
if ( res === true )
continue ;
break ;
default :
res = - 2 ;
@ -543,7 +597,8 @@ export function tinf_uncompress(source: BitstreamReader,
} while ( ! bfinal ) ;
decomp . end ( ) ;
hid . end ( ) ;
hid . end ( )
//if (d.dest.byteOffset < d.dest.length) {
// if (typeof d.dest.slice === 'function')