@@ -39,6 +39,14 @@ const token = (v: number): number => {
3939 return SPLIT + ( ( n - SPLIT_EXPONENT ) << ( MSB + LSB ) ) + ( ( m >>> ( n - MSB ) ) << LSB ) + ( m & ( ( 1 << LSB ) - 1 ) ) ;
4040} ;
4141
42+ /** The number of tokens (of 32-bit values) */
43+ export const MAX_TOKEN = token ( 0xffffffff ) + 1 ;
44+
45+ /** Counts the tokens of values (from to to) by context, into counts (MAX_TOKEN per context) */
46+ export function countTokens ( counts : Float64Array , ctx : Uint32Array , value : Uint32Array , from : number , to : number ) : void {
47+ for ( let i = from ; i < to ; i ++ ) counts [ ctx [ i ] * MAX_TOKEN + token ( value [ i ] ) ] ++ ;
48+ }
49+
4250// ---- ANS
4351
4452const ANS_LOG_TAB_SIZE = 12 , ANS_TAB_SIZE = 1 << ANS_LOG_TAB_SIZE , LOG_ALPHABET_SIZE = 8 ;
@@ -250,22 +258,19 @@ export class EntropyCode {
250258 private readonly ctxMap : Uint8Array ;
251259 private readonly ans : AnsCode [ ] ;
252260
253- constructor ( streams : Tokens [ ] , readonly numContexts : number , { maxClusters = 64 } : EntropyOptions = { } ) {
254- // the histograms of the contexts' tokens
255- let alphabet = 1 ;
256- for ( const t of streams ) {
257- for ( let i = 0 ; i < t . length ; i ++ ) {
258- const tk = token ( t . value [ i ] ) ;
259- if ( tk + 1 > alphabet ) alphabet = tk + 1 ;
260- }
261- }
262- const hist = Array . from ( { length : numContexts } , ( ) => new Float64Array ( alphabet ) ) , totals = new Float64Array ( numContexts ) ;
263- for ( const t of streams ) {
264- for ( let i = 0 ; i < t . length ; i ++ ) {
265- hist [ t . ctx [ i ] ] [ token ( t . value [ i ] ) ] ++ ;
266- totals [ t . ctx [ i ] ] ++ ;
267- }
261+ /** the code of streams' tokens, or of their counts (see countTokens) */
262+ constructor ( streams : Tokens [ ] | Float64Array , readonly numContexts : number , { maxClusters = 64 } : EntropyOptions = { } ) {
263+ // the histograms of the contexts' tokens (up to the largest token used)
264+ let counts : Float64Array ;
265+ if ( streams instanceof Float64Array ) counts = streams ;
266+ else {
267+ counts = new Float64Array ( numContexts * MAX_TOKEN ) ;
268+ for ( const t of streams ) countTokens ( counts , t . ctx , t . value , 0 , t . length ) ;
268269 }
270+ let alphabet = 1 ;
271+ for ( let i = 0 ; i < counts . length ; i ++ ) if ( counts [ i ] && i % MAX_TOKEN + 1 > alphabet ) alphabet = i % MAX_TOKEN + 1 ;
272+ const hist = Array . from ( { length : numContexts } , ( _ , c ) => counts . slice ( c * MAX_TOKEN , c * MAX_TOKEN + alphabet ) ) ;
273+ const totals = Float64Array . from ( hist , h => h . reduce ( ( sum , v ) => sum + v , 0 ) ) ;
269274 this . ctxMap = new Uint8Array ( numContexts ) ;
270275 const clusters = cluster ( hist , totals , Math . min ( maxClusters , 255 , numContexts ) , this . ctxMap ) . map ( h => {
271276 let size = h . length ;
@@ -293,29 +298,45 @@ export class EntropyCode {
293298
294299 /** writes the tokens of a stream */
295300 writeTokens ( w : BitWriter , t : Tokens ) : void {
296- const map = this . ctxMap ;
297- // ANS: the tokens in reverse, the 16 bits of each renormalization read after the token's symbol (before its
298- // raw bits); the final state first
299- const codes = this . ans , n = t . length , chunks = new Int32Array ( n ) ;
300- let state = 0x130000 ;
301- for ( let i = n - 1 ; i >= 0 ; i -- ) {
302- const code = codes [ map [ t . ctx [ i ] ] ] , tk = token ( t . value [ i ] ) , freq = code . freq [ tk ] ;
303- chunks [ i ] = - 1 ;
304- if ( state >>> 20 >= freq ) {
305- chunks [ i ] = state & 0xffff ;
306- state >>>= 16 ;
307- }
308- const v = Math . floor ( state / freq ) ;
309- state = v * ANS_TAB_SIZE + code . reverse [ tk ] [ state - v * freq ] ;
301+ writeTokens ( w , this . tables ( ) , t . ctx , t . value , 0 , t . length ) ;
302+ }
303+
304+ /** what writing tokens needs (for threads, which get copies) */
305+ tables ( ) : CodeTables {
306+ return { ctxMap : this . ctxMap , freq : this . ans . map ( c => c . freq ) , reverse : this . ans . map ( c => c . reverse ) } ;
307+ }
308+ }
309+
310+ /** An entropy code's tables for writing tokens: the context map, per cluster the symbols' frequencies and table slots */
311+ export interface CodeTables {
312+ ctxMap : Uint8Array ;
313+ freq : Int32Array [ ] ;
314+ reverse : Int32Array [ ] [ ] ;
315+ }
316+
317+ /** Writes tokens (from to to) with a code */
318+ export function writeTokens ( w : BitWriter , { ctxMap : map , freq : freqs , reverse } : CodeTables , ctx : Uint32Array , value : Uint32Array , from : number , to : number ) : void {
319+ // ANS: the tokens in reverse, the 16 bits of each renormalization read after the token's symbol (before its raw
320+ // bits); the final state first
321+ const chunks = new Int32Array ( to - from ) ;
322+ let state = 0x130000 ;
323+ for ( let i = to - 1 ; i >= from ; i -- ) {
324+ const c = map [ ctx [ i ] ] , tk = token ( value [ i ] ) , freq = freqs [ c ] [ tk ] ;
325+ chunks [ i - from ] = - 1 ;
326+ if ( state >>> 20 >= freq ) {
327+ chunks [ i - from ] = state & 0xffff ;
328+ state >>>= 16 ;
310329 }
311- w . write ( 32 , state ) ;
312- for ( let i = 0 ; i < n ; i ++ ) {
313- if ( chunks [ i ] >= 0 ) w . write ( 16 , chunks [ i ] ) ;
314- const v = t . value [ i ] ;
315- if ( v >= SPLIT ) {
316- const nbits = 31 - Math . clz32 ( v ) - MSB - LSB ;
317- w . write ( nbits , ( v >>> LSB ) & ( ( 1 << nbits ) - 1 ) ) ;
318- }
330+ const v = Math . floor ( state / freq ) ;
331+ state = v * ANS_TAB_SIZE + reverse [ c ] [ tk ] [ state - v * freq ] ;
332+ }
333+ w . write ( 32 , state ) ;
334+ for ( let i = from ; i < to ; i ++ ) {
335+ if ( chunks [ i - from ] >= 0 ) w . write ( 16 , chunks [ i - from ] ) ;
336+ const v = value [ i ] ;
337+ if ( v >= SPLIT ) {
338+ const nbits = 31 - Math . clz32 ( v ) - MSB - LSB ;
339+ w . write ( nbits , ( v >>> LSB ) & ( ( 1 << nbits ) - 1 ) ) ;
319340 }
320341 }
321342}
0 commit comments