mirror of
https://github.com/asadbek064/hyparquet-compressors.git
synced 2026-01-11 05:16:36 +00:00
Move stuff to brotli.blocks
This commit is contained in:
parent
83718cff45
commit
8e4d71376c
@ -31,7 +31,7 @@ const kBitMask = new Uint32Array([
|
||||
* @typedef {import('./brotli.streams.js').BrotliInput} BrotliInput
|
||||
* @param {BrotliInput} input
|
||||
*/
|
||||
function BrotliBitReader(input) {
|
||||
export function BrotliBitReader(input) {
|
||||
this.buf_ = new Uint8Array(BROTLI_IBUF_SIZE)
|
||||
this.input_ = input /* input callback */
|
||||
|
||||
@ -132,5 +132,3 @@ BrotliBitReader.prototype.readBits = function(n_bits) {
|
||||
this.bit_pos_ += n_bits
|
||||
return val
|
||||
}
|
||||
|
||||
export default BrotliBitReader
|
||||
|
||||
235
src/brotli.blocks.js
Normal file
235
src/brotli.blocks.js
Normal file
@ -0,0 +1,235 @@
|
||||
import { readSymbol } from './brotli.huffman.js'
|
||||
import { BrotliBitReader } from './brotli.bitreader.js'
|
||||
import { kBlockLengthPrefixCode } from './brotli.prefix.js'
|
||||
import { HUFFMAN_MAX_TABLE_SIZE } from './gzip.huffman.js'
|
||||
|
||||
/**
|
||||
* @import {HuffmanCode} from './brotli.huffman.js'
|
||||
* @param {number} max_block_type
|
||||
* @param {HuffmanCode[]} trees
|
||||
* @param {number} tree_type
|
||||
* @param {number[]} block_types
|
||||
* @param {number[]} ringbuffers
|
||||
* @param {number[]} indexes
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
export function decodeBlockType(max_block_type, trees, tree_type, block_types, ringbuffers, indexes, br) {
|
||||
const ringbuffer = tree_type * 2
|
||||
const index = tree_type
|
||||
const type_code = readSymbol(trees, tree_type * HUFFMAN_MAX_TABLE_SIZE, br)
|
||||
let block_type
|
||||
if (type_code === 0) {
|
||||
block_type = ringbuffers[ringbuffer + (indexes[index] & 1)]
|
||||
} else if (type_code === 1) {
|
||||
block_type = ringbuffers[ringbuffer + (indexes[index] - 1 & 1)] + 1
|
||||
} else {
|
||||
block_type = type_code - 2
|
||||
}
|
||||
if (block_type >= max_block_type) {
|
||||
block_type -= max_block_type
|
||||
}
|
||||
block_types[tree_type] = block_type
|
||||
ringbuffers[ringbuffer + (indexes[index] & 1)] = block_type
|
||||
indexes[index]++
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{ input_end: number, is_metadata: boolean, meta_block_length: number, is_uncompressed: number }} MetaBlockLength
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {MetaBlockLength}
|
||||
*/
|
||||
export function decodeMetaBlockLength(br) {
|
||||
const out = {
|
||||
meta_block_length: 0,
|
||||
input_end: 0,
|
||||
is_uncompressed: 0,
|
||||
is_metadata: false,
|
||||
}
|
||||
|
||||
out.input_end = br.readBits(1)
|
||||
if (out.input_end && br.readBits(1)) {
|
||||
return out
|
||||
}
|
||||
|
||||
const size_nibbles = br.readBits(2) + 4
|
||||
if (size_nibbles === 7) {
|
||||
out.is_metadata = true
|
||||
|
||||
if (br.readBits(1) !== 0)
|
||||
throw new Error('Invalid reserved bit')
|
||||
|
||||
const size_bytes = br.readBits(2)
|
||||
if (size_bytes === 0)
|
||||
return out
|
||||
|
||||
for (let i = 0; i < size_bytes; i++) {
|
||||
const next_byte = br.readBits(8)
|
||||
if (i + 1 === size_bytes && size_bytes > 1 && next_byte === 0)
|
||||
throw new Error('Invalid size byte')
|
||||
|
||||
out.meta_block_length |= next_byte << i * 8
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < size_nibbles; i++) {
|
||||
const next_nibble = br.readBits(4)
|
||||
if (i + 1 === size_nibbles && size_nibbles > 4 && next_nibble === 0)
|
||||
throw new Error('Invalid size nibble')
|
||||
|
||||
out.meta_block_length |= next_nibble << i * 4
|
||||
}
|
||||
}
|
||||
|
||||
out.meta_block_length++
|
||||
|
||||
if (!out.input_end && !out.is_metadata) {
|
||||
out.is_uncompressed = br.readBits(1)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @import {BrotliOutput} from './brotli.streams.js'
|
||||
* @param {BrotliOutput} output
|
||||
* @param {number} len
|
||||
* @param {number} pos
|
||||
* @param {Uint8Array} ringbuffer
|
||||
* @param {number} ringbuffer_mask
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
export function copyUncompressedBlockToOutput(output, len, pos, ringbuffer, ringbuffer_mask, br) {
|
||||
const rb_size = ringbuffer_mask + 1
|
||||
let rb_pos = pos & ringbuffer_mask
|
||||
let br_pos = br.pos_ & BrotliBitReader.IBUF_MASK
|
||||
|
||||
// For short lengths copy byte-by-byte
|
||||
if (len < 8 || br.bit_pos_ + (len << 3) < br.bit_end_pos_) {
|
||||
while (len-- > 0) {
|
||||
br.readMoreInput()
|
||||
ringbuffer[rb_pos++] = br.readBits(8)
|
||||
if (rb_pos === rb_size) {
|
||||
output.write(ringbuffer, rb_size)
|
||||
rb_pos = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (br.bit_end_pos_ < 32) {
|
||||
throw new Error('copyUncompressedBlockToOutput: br.bit_end_pos_ < 32')
|
||||
}
|
||||
|
||||
// Copy remaining 0-4 bytes from br.val_ to ringbuffer
|
||||
while (br.bit_pos_ < 32) {
|
||||
ringbuffer[rb_pos] = br.val_ >>> br.bit_pos_
|
||||
br.bit_pos_ += 8
|
||||
rb_pos++
|
||||
len--
|
||||
}
|
||||
|
||||
// Copy remaining bytes from br.buf_ to ringbuffer
|
||||
let nbytes = br.bit_end_pos_ - br.bit_pos_ >> 3
|
||||
if (br_pos + nbytes > BrotliBitReader.IBUF_MASK) {
|
||||
const tail = BrotliBitReader.IBUF_MASK + 1 - br_pos
|
||||
for (let x = 0; x < tail; x++)
|
||||
ringbuffer[rb_pos + x] = br.buf_[br_pos + x]
|
||||
|
||||
nbytes -= tail
|
||||
rb_pos += tail
|
||||
len -= tail
|
||||
br_pos = 0
|
||||
}
|
||||
|
||||
for (let x = 0; x < nbytes; x++)
|
||||
ringbuffer[rb_pos + x] = br.buf_[br_pos + x]
|
||||
|
||||
rb_pos += nbytes
|
||||
len -= nbytes
|
||||
|
||||
// If we wrote past the logical end of the ringbuffer, copy the tail of the
|
||||
// ringbuffer to its beginning and flush the ringbuffer to the output
|
||||
if (rb_pos >= rb_size) {
|
||||
output.write(ringbuffer, rb_size)
|
||||
rb_pos -= rb_size
|
||||
for (let x = 0; x < rb_pos; x++)
|
||||
ringbuffer[x] = ringbuffer[rb_size + x]
|
||||
}
|
||||
|
||||
// If we have more to copy than the remaining size of the ringbuffer, then we
|
||||
// first fill the ringbuffer from the input and then flush the ringbuffer
|
||||
while (rb_pos + len >= rb_size) {
|
||||
nbytes = rb_size - rb_pos
|
||||
if (br.input_.read(ringbuffer, rb_pos, nbytes) < nbytes) {
|
||||
throw new Error('copyUncompressedBlockToOutput: not enough bytes')
|
||||
}
|
||||
output.write(ringbuffer, rb_size)
|
||||
len -= nbytes
|
||||
rb_pos = 0
|
||||
}
|
||||
|
||||
// Copy straight from the input onto the ringbuffer
|
||||
// Ringbuffer will be flushed to output later
|
||||
if (br.input_.read(ringbuffer, rb_pos, len) < len) {
|
||||
throw new Error('copyUncompressedBlockToOutput: not enough bytes')
|
||||
}
|
||||
|
||||
// Restore the state of the bit reader
|
||||
br.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a number in the range [0..255], by reading 1 - 11 bits.
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
export function decodeVarLenUint8(br) {
|
||||
if (br.readBits(1)) {
|
||||
const nbits = br.readBits(3)
|
||||
if (nbits === 0) {
|
||||
return 1
|
||||
} else {
|
||||
return br.readBits(nbits) + (1 << nbits)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
export function decodeWindowBits(br) {
|
||||
if (br.readBits(1) === 0) return 16
|
||||
|
||||
let n = br.readBits(3)
|
||||
if (n > 0) return 17 + n
|
||||
|
||||
n = br.readBits(3)
|
||||
if (n > 0) return 8 + n
|
||||
|
||||
return 17
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the bit reader position to the next byte boundary and verifies
|
||||
* that any skipped bits are set to zero.
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function jumpToByteBoundary(br) {
|
||||
const new_bit_pos = br.bit_pos_ + 7 & ~7
|
||||
return !br.readBits(new_bit_pos - br.bit_pos_)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HuffmanCode[]} table
|
||||
* @param {number} index
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
export function readBlockLength(table, index, br) {
|
||||
const code = readSymbol(table, index, br)
|
||||
const { offset, nbits } = kBlockLengthPrefixCode[code]
|
||||
return offset + br.readBits(nbits)
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
import { decodeVarLenUint8 } from './brotli.blocks.js'
|
||||
import { HuffmanCode, readHuffmanCode, readSymbol } from './brotli.huffman.js'
|
||||
import { decodeVarLenUint8 } from './brotli.js'
|
||||
import { HUFFMAN_MAX_TABLE_SIZE } from './gzip.huffman.js'
|
||||
|
||||
/**
|
||||
* @import {BrotliBitReader} from './brotli.bitreader.js'
|
||||
* @param {number} context_map_size
|
||||
* @param {import('./brotli.huffman.js').BrotliBitReader} br
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {[number, Uint8Array]} // num_htrees, context_map
|
||||
*/
|
||||
export function decodeContextMap(context_map_size, br) {
|
||||
|
||||
@ -1,17 +1,5 @@
|
||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Collection of static dictionary words.
|
||||
*/
|
||||
|
||||
|
||||
@ -9,13 +9,18 @@ const kCodeLengthCodeOrder = new Uint8Array([
|
||||
1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
])
|
||||
|
||||
const kMaxHuffmanTableSize = new Uint16Array([
|
||||
256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822,
|
||||
854, 886, 920, 952, 984, 1016, 1048, 1080,
|
||||
])
|
||||
|
||||
/**
|
||||
* @param {number} bits
|
||||
* @param {number} value
|
||||
*/
|
||||
export function HuffmanCode(bits, value) {
|
||||
this.bits = bits /* number of bits used for this symbol */
|
||||
this.value = value /* symbol value or table offset */
|
||||
this.bits = bits // number of bits used for this symbol
|
||||
this.value = value // symbol value or table offset
|
||||
}
|
||||
|
||||
const kCodeLengthRepeatCode = 16
|
||||
@ -80,24 +85,24 @@ function nextTableBitSize(count, len, root_bits) {
|
||||
* @param {number} code_lengths_size
|
||||
* @returns {number}
|
||||
*/
|
||||
export function buildHuffmanTable(root_table, table, root_bits, code_lengths, code_lengths_size) {
|
||||
function buildHuffmanTable(root_table, table, root_bits, code_lengths, code_lengths_size) {
|
||||
const start_table = table
|
||||
const count = new Int32Array(MAX_LENGTH + 1) /* number of codes of each length */
|
||||
const offset = new Int32Array(MAX_LENGTH + 1) /* offsets in sorted table for each length */
|
||||
const sorted = new Int32Array(code_lengths_size) /* symbols sorted by code length */
|
||||
const count = new Int32Array(MAX_LENGTH + 1) // number of codes of each length
|
||||
const offset = new Int32Array(MAX_LENGTH + 1) // offsets in sorted table for each length
|
||||
const sorted = new Int32Array(code_lengths_size) // symbols sorted by code length
|
||||
|
||||
/* build histogram of code lengths */
|
||||
// build histogram of code lengths
|
||||
for (let i = 0; i < code_lengths_size; i++) {
|
||||
count[code_lengths[i]]++
|
||||
}
|
||||
|
||||
/* generate offsets into sorted symbol table by code length */
|
||||
// generate offsets into sorted symbol table by code length
|
||||
offset[1] = 0
|
||||
for (let i = 1; i < MAX_LENGTH; i++) {
|
||||
offset[i + 1] = offset[i] + count[i]
|
||||
}
|
||||
|
||||
/* sort symbols by length, by symbol order within each length */
|
||||
// sort symbols by length, by symbol order within each length
|
||||
for (let i = 0; i < code_lengths_size; i++) {
|
||||
if (code_lengths[i] !== 0) {
|
||||
sorted[offset[code_lengths[i]]++] = i
|
||||
@ -108,7 +113,7 @@ export function buildHuffmanTable(root_table, table, root_bits, code_lengths, co
|
||||
let table_size = 1 << table_bits
|
||||
let total_size = table_size // sum of root table size and 2nd level table sizes
|
||||
|
||||
/* special case code with only one value */
|
||||
// special case code with only one value
|
||||
if (offset[MAX_LENGTH] === 1) {
|
||||
for (let key = 0; key < total_size; ++key) {
|
||||
root_table[table + key] = new HuffmanCode(0, sorted[0] & 0xffff)
|
||||
@ -117,7 +122,7 @@ export function buildHuffmanTable(root_table, table, root_bits, code_lengths, co
|
||||
return total_size
|
||||
}
|
||||
|
||||
/* fill in root table */
|
||||
// fill in root table
|
||||
let key = 0 // reversed prefix code
|
||||
let symbol = 0 // symbol index in original or sorted table
|
||||
for (let len = 1, step = 2; len <= root_bits; ++len, step <<= 1) {
|
||||
@ -128,7 +133,7 @@ export function buildHuffmanTable(root_table, table, root_bits, code_lengths, co
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in 2nd level tables and add pointers to root table */
|
||||
// fill in 2nd level tables and add pointers to root table
|
||||
const mask = total_size - 1
|
||||
let low = -1 // low bits for current root entry
|
||||
for (let len = root_bits + 1, step = 2; len <= MAX_LENGTH; ++len, step <<= 1) {
|
||||
@ -151,7 +156,7 @@ export function buildHuffmanTable(root_table, table, root_bits, code_lengths, co
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('./brotli.bitreader.js').default} BrotliBitReader
|
||||
* @import {BrotliBitReader} from './brotli.bitreader.js'
|
||||
* @param {number} alphabet_size
|
||||
* @param {HuffmanCode[]} tables
|
||||
* @param {number} table
|
||||
@ -163,12 +168,12 @@ export function readHuffmanCode(alphabet_size, tables, table, br) {
|
||||
|
||||
br.readMoreInput()
|
||||
|
||||
/* simple_code_or_skip is used as follows:
|
||||
1 for simple code;
|
||||
0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */
|
||||
// simple_code_or_skip is used as follows:
|
||||
// - 1 for simple code;
|
||||
// - 0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths
|
||||
const simple_code_or_skip = br.readBits(2)
|
||||
if (simple_code_or_skip === 1) {
|
||||
/* Read symbols, codes & code lengths directly. */
|
||||
// Read symbols, codes & code lengths directly
|
||||
let max_bits_counter = alphabet_size - 1
|
||||
let max_bits = 0
|
||||
const symbols = new Int32Array(4)
|
||||
@ -218,11 +223,11 @@ export function readHuffmanCode(alphabet_size, tables, table, br) {
|
||||
}
|
||||
break
|
||||
}
|
||||
} else { /* Decode Huffman-coded code lengths. */
|
||||
} else { // Decode Huffman-coded code lengths
|
||||
const code_length_code_lengths = new Uint8Array(CODE_LENGTH_CODES)
|
||||
let space = 32
|
||||
let num_codes = 0
|
||||
/* Static Huffman code for the code length code lengths */
|
||||
// Static Huffman code for the code length code lengths
|
||||
const huff = [
|
||||
new HuffmanCode(2, 0), new HuffmanCode(2, 4), new HuffmanCode(2, 3), new HuffmanCode(3, 2),
|
||||
new HuffmanCode(2, 0), new HuffmanCode(2, 4), new HuffmanCode(2, 3), new HuffmanCode(4, 1),
|
||||
@ -347,3 +352,28 @@ function readHuffmanCodeLengths(code_length_code_lengths, num_symbols, code_leng
|
||||
for (; symbol < num_symbols; symbol++)
|
||||
code_lengths[symbol] = 0
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contains a collection of huffman trees with the same alphabet size.
|
||||
*
|
||||
* @param {number} alphabet_size
|
||||
* @param {number} num_htrees
|
||||
*/
|
||||
export function HuffmanTreeGroup(alphabet_size, num_htrees) {
|
||||
this.alphabet_size = alphabet_size
|
||||
this.num_htrees = num_htrees
|
||||
this.codes = new Array(num_htrees + num_htrees * kMaxHuffmanTableSize[alphabet_size + 31 >>> 5])
|
||||
this.htrees = new Uint32Array(num_htrees)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
HuffmanTreeGroup.prototype.decode = function(br) {
|
||||
let next = 0
|
||||
for (let i = 0; i < this.num_htrees; i++) {
|
||||
this.htrees[i] = next
|
||||
next += readHuffmanCode(this.alphabet_size, this.codes, next, br)
|
||||
}
|
||||
}
|
||||
|
||||
264
src/brotli.js
264
src/brotli.js
@ -3,11 +3,12 @@
|
||||
* Copyright 2013 Google Inc, Apache License 2.0
|
||||
*/
|
||||
|
||||
import BrotliBitReader from './brotli.bitreader.js'
|
||||
import { BrotliBitReader } from './brotli.bitreader.js'
|
||||
import { copyUncompressedBlockToOutput, decodeBlockType, decodeMetaBlockLength, decodeVarLenUint8, decodeWindowBits, jumpToByteBoundary, readBlockLength } from './brotli.blocks.js'
|
||||
import { lookup, lookupOffsets } from './brotli.context.js'
|
||||
import { decodeContextMap } from './brotli.contextmap.js'
|
||||
import { HuffmanCode, readHuffmanCode, readSymbol } from './brotli.huffman.js'
|
||||
import { kBlockLengthPrefixCode, kCopyLengthPrefixCode, kCopyRangeLut, kInsertLengthPrefixCode, kInsertRangeLut } from './brotli.prefix.js'
|
||||
import { HuffmanCode, HuffmanTreeGroup, readHuffmanCode, readSymbol } from './brotli.huffman.js'
|
||||
import { kCopyLengthPrefixCode, kCopyRangeLut, kInsertLengthPrefixCode, kInsertRangeLut } from './brotli.prefix.js'
|
||||
import { BrotliInput, BrotliOutput } from './brotli.streams.js'
|
||||
import { kNumTransforms, transformDictionaryWord } from './brotli.transform.js'
|
||||
import { HUFFMAN_MAX_TABLE_SIZE } from './gzip.huffman.js'
|
||||
@ -27,11 +28,6 @@ const kDistanceShortCodeValueOffset = new Int8Array([
|
||||
0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3,
|
||||
])
|
||||
|
||||
const kMaxHuffmanTableSize = new Uint16Array([
|
||||
256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822,
|
||||
854, 886, 920, 952, 984, 1016, 1048, 1080,
|
||||
])
|
||||
|
||||
// Brotli dictionary
|
||||
const offsetsByLength = new Uint32Array([
|
||||
0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032,
|
||||
@ -345,87 +341,6 @@ function brotli(input, output) {
|
||||
output.write(ringbuffer, pos & ringbuffer_mask)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
function decodeWindowBits(br) {
|
||||
if (br.readBits(1) === 0) return 16
|
||||
|
||||
let n = br.readBits(3)
|
||||
if (n > 0) return 17 + n
|
||||
|
||||
n = br.readBits(3)
|
||||
if (n > 0) return 8 + n
|
||||
|
||||
return 17
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} max_block_type
|
||||
* @param {HuffmanCode[]} trees
|
||||
* @param {number} tree_type
|
||||
* @param {number[]} block_types
|
||||
* @param {number[]} ringbuffers
|
||||
* @param {number[]} indexes
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
function decodeBlockType(max_block_type, trees, tree_type, block_types, ringbuffers, indexes, br) {
|
||||
const ringbuffer = tree_type * 2
|
||||
const index = tree_type
|
||||
const type_code = readSymbol(trees, tree_type * HUFFMAN_MAX_TABLE_SIZE, br)
|
||||
let block_type
|
||||
if (type_code === 0) {
|
||||
block_type = ringbuffers[ringbuffer + (indexes[index] & 1)]
|
||||
} else if (type_code === 1) {
|
||||
block_type = ringbuffers[ringbuffer + (indexes[index] - 1 & 1)] + 1
|
||||
} else {
|
||||
block_type = type_code - 2
|
||||
}
|
||||
if (block_type >= max_block_type) {
|
||||
block_type -= max_block_type
|
||||
}
|
||||
block_types[tree_type] = block_type
|
||||
ringbuffers[ringbuffer + (indexes[index] & 1)] = block_type
|
||||
++indexes[index]
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains a collection of huffman trees with the same alphabet size.
|
||||
*
|
||||
* @param {number} alphabet_size
|
||||
* @param {number} num_htrees
|
||||
*/
|
||||
function HuffmanTreeGroup(alphabet_size, num_htrees) {
|
||||
this.alphabet_size = alphabet_size
|
||||
this.num_htrees = num_htrees
|
||||
this.codes = new Array(num_htrees + num_htrees * kMaxHuffmanTableSize[alphabet_size + 31 >>> 5])
|
||||
this.htrees = new Uint32Array(num_htrees)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
HuffmanTreeGroup.prototype.decode = function(br) {
|
||||
let next = 0
|
||||
for (let i = 0; i < this.num_htrees; i++) {
|
||||
this.htrees[i] = next
|
||||
next += readHuffmanCode(this.alphabet_size, this.codes, next, br)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HuffmanCode[]} table
|
||||
* @param {number} index
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
function readBlockLength(table, index, br) {
|
||||
const code = readSymbol(table, index, br)
|
||||
const { offset, nbits } = kBlockLengthPrefixCode[code]
|
||||
return offset + br.readBits(nbits)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code
|
||||
* @param {number[]} ringbuffer
|
||||
@ -441,174 +356,3 @@ function translateShortCodes(code, ringbuffer, index) {
|
||||
return code - NUM_DISTANCE_SHORT_CODES + 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} output
|
||||
* @param {number} len
|
||||
* @param {number} pos
|
||||
* @param {Uint8Array} ringbuffer
|
||||
* @param {number} ringbuffer_mask
|
||||
* @param {BrotliBitReader} br
|
||||
*/
|
||||
function copyUncompressedBlockToOutput(output, len, pos, ringbuffer, ringbuffer_mask, br) {
|
||||
const rb_size = ringbuffer_mask + 1
|
||||
let rb_pos = pos & ringbuffer_mask
|
||||
let br_pos = br.pos_ & BrotliBitReader.IBUF_MASK
|
||||
|
||||
// For short lengths copy byte-by-byte
|
||||
if (len < 8 || br.bit_pos_ + (len << 3) < br.bit_end_pos_) {
|
||||
while (len-- > 0) {
|
||||
br.readMoreInput()
|
||||
ringbuffer[rb_pos++] = br.readBits(8)
|
||||
if (rb_pos === rb_size) {
|
||||
output.write(ringbuffer, rb_size)
|
||||
rb_pos = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (br.bit_end_pos_ < 32) {
|
||||
throw new Error('copyUncompressedBlockToOutput: br.bit_end_pos_ < 32')
|
||||
}
|
||||
|
||||
// Copy remaining 0-4 bytes from br.val_ to ringbuffer
|
||||
while (br.bit_pos_ < 32) {
|
||||
ringbuffer[rb_pos] = br.val_ >>> br.bit_pos_
|
||||
br.bit_pos_ += 8
|
||||
rb_pos++
|
||||
len--
|
||||
}
|
||||
|
||||
// Copy remaining bytes from br.buf_ to ringbuffer
|
||||
let nbytes = br.bit_end_pos_ - br.bit_pos_ >> 3
|
||||
if (br_pos + nbytes > BrotliBitReader.IBUF_MASK) {
|
||||
const tail = BrotliBitReader.IBUF_MASK + 1 - br_pos
|
||||
for (let x = 0; x < tail; x++)
|
||||
ringbuffer[rb_pos + x] = br.buf_[br_pos + x]
|
||||
|
||||
nbytes -= tail
|
||||
rb_pos += tail
|
||||
len -= tail
|
||||
br_pos = 0
|
||||
}
|
||||
|
||||
for (let x = 0; x < nbytes; x++)
|
||||
ringbuffer[rb_pos + x] = br.buf_[br_pos + x]
|
||||
|
||||
rb_pos += nbytes
|
||||
len -= nbytes
|
||||
|
||||
// If we wrote past the logical end of the ringbuffer, copy the tail of the
|
||||
// ringbuffer to its beginning and flush the ringbuffer to the output
|
||||
if (rb_pos >= rb_size) {
|
||||
output.write(ringbuffer, rb_size)
|
||||
rb_pos -= rb_size
|
||||
for (let x = 0; x < rb_pos; x++)
|
||||
ringbuffer[x] = ringbuffer[rb_size + x]
|
||||
}
|
||||
|
||||
// If we have more to copy than the remaining size of the ringbuffer, then we
|
||||
// first fill the ringbuffer from the input and then flush the ringbuffer
|
||||
while (rb_pos + len >= rb_size) {
|
||||
nbytes = rb_size - rb_pos
|
||||
if (br.input_.read(ringbuffer, rb_pos, nbytes) < nbytes) {
|
||||
throw new Error('copyUncompressedBlockToOutput: not enough bytes')
|
||||
}
|
||||
output.write(ringbuffer, rb_size)
|
||||
len -= nbytes
|
||||
rb_pos = 0
|
||||
}
|
||||
|
||||
// Copy straight from the input onto the ringbuffer
|
||||
// Ringbuffer will be flushed to output later
|
||||
if (br.input_.read(ringbuffer, rb_pos, len) < len) {
|
||||
throw new Error('copyUncompressedBlockToOutput: not enough bytes')
|
||||
}
|
||||
|
||||
// Restore the state of the bit reader
|
||||
br.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a number in the range [0..255], by reading 1 - 11 bits.
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {number}
|
||||
*/
|
||||
export function decodeVarLenUint8(br) {
|
||||
if (br.readBits(1)) {
|
||||
const nbits = br.readBits(3)
|
||||
if (nbits === 0) {
|
||||
return 1
|
||||
} else {
|
||||
return br.readBits(nbits) + (1 << nbits)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{ input_end: number, is_metadata: boolean, meta_block_length: number, is_uncompressed: number }} MetaBlockLength
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {MetaBlockLength}
|
||||
*/
|
||||
function decodeMetaBlockLength(br) {
|
||||
const out = {
|
||||
meta_block_length: 0,
|
||||
input_end: 0,
|
||||
is_uncompressed: 0,
|
||||
is_metadata: false,
|
||||
}
|
||||
|
||||
out.input_end = br.readBits(1)
|
||||
if (out.input_end && br.readBits(1)) {
|
||||
return out
|
||||
}
|
||||
|
||||
const size_nibbles = br.readBits(2) + 4
|
||||
if (size_nibbles === 7) {
|
||||
out.is_metadata = true
|
||||
|
||||
if (br.readBits(1) !== 0)
|
||||
throw new Error('Invalid reserved bit')
|
||||
|
||||
const size_bytes = br.readBits(2)
|
||||
if (size_bytes === 0)
|
||||
return out
|
||||
|
||||
for (let i = 0; i < size_bytes; i++) {
|
||||
const next_byte = br.readBits(8)
|
||||
if (i + 1 === size_bytes && size_bytes > 1 && next_byte === 0)
|
||||
throw new Error('Invalid size byte')
|
||||
|
||||
out.meta_block_length |= next_byte << i * 8
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < size_nibbles; i++) {
|
||||
const next_nibble = br.readBits(4)
|
||||
if (i + 1 === size_nibbles && size_nibbles > 4 && next_nibble === 0)
|
||||
throw new Error('Invalid size nibble')
|
||||
|
||||
out.meta_block_length |= next_nibble << i * 4
|
||||
}
|
||||
}
|
||||
|
||||
out.meta_block_length++
|
||||
|
||||
if (!out.input_end && !out.is_metadata) {
|
||||
out.is_uncompressed = br.readBits(1)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the bit reader position to the next byte boundary and verifies
|
||||
* that any skipped bits are set to zero.
|
||||
* @param {BrotliBitReader} br
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function jumpToByteBoundary(br) {
|
||||
const new_bit_pos = br.bit_pos_ + 7 & ~7
|
||||
return !br.readBits(new_bit_pos - br.bit_pos_)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user