Move stuff to brotli.blocks

This commit is contained in:
Kenny Daniel 2025-03-20 02:32:22 -07:00
parent 83718cff45
commit 8e4d71376c
No known key found for this signature in database
GPG Key ID: 90AB653A8CAD7E45
6 changed files with 292 additions and 296 deletions

@ -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

@ -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)
}
}

@ -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_)
}