Comb UTF-8 literal processing

PiperOrigin-RevId: 770672448
This commit is contained in:
Evgenii Kliuchnikov
2025-06-12 08:47:30 -07:00
committed by Copybara-Service
parent 405b434d85
commit 3efb30f96b
7 changed files with 66 additions and 21 deletions

View File

@@ -39,6 +39,7 @@ final class DictionaryData {
String skipFlip, int[] sizeBits, String sizeBitsData) {
// Initialize lower 7 bits of every byte in the dictionary.
final byte[] dict = Utils.toUsAsciiBytes(data0 + data1);
final int[] skipFlipRunes = Utils.toUtf8Runes(skipFlip);
if (DICTIONARY_DATA_DEBUG != 0) {
if (dict.length != dictionary.capacity()) {
throw new RuntimeException("Corrupted brotli dictionary");
@@ -47,10 +48,10 @@ final class DictionaryData {
// Toggle high bit using run-length delta encoded "skipFlip".
int offset = 0;
final int n = skipFlip.length() >> 1;
final int n = skipFlipRunes.length >> 1;
for (int i = 0; i < n; ++i) {
final int skip = (int) skipFlip.charAt(2 * i) - 36;
final int flip = (int) skipFlip.charAt(2 * i + 1) - 36;
final int skip = skipFlipRunes[2 * i] - 36;
final int flip = skipFlipRunes[2 * i + 1] - 36;
for (int j = 0; j < skip; ++j) {
dict[offset] = (byte) ((int) dict[offset] ^ 3);
offset++;

View File

@@ -76,11 +76,12 @@ final class Transform {
private static void unpackTransforms(byte[] prefixSuffix,
int[] prefixSuffixHeads, int[] transforms, String prefixSuffixSrc, String transformsSrc) {
final int n = prefixSuffixSrc.length();
final int[] prefixSuffixBytes = Utils.toUtf8Runes(prefixSuffixSrc);
final int n = prefixSuffixBytes.length;
int index = 1;
int j = 0;
for (int i = 0; i < n; ++i) {
final int c = (int) prefixSuffixSrc.charAt(i);
final int c = prefixSuffixBytes[i];
if (c == 35) { // == #
prefixSuffixHeads[index++] = j;
} else {

View File

@@ -99,6 +99,14 @@ final class Utils {
}
}
static int[] toUtf8Runes(String src) {
int[] result = new int[src.length()];
for (int i = 0; i < src.length(); i++) {
result[i] = (int) src.charAt(i);
}
return result;
}
static ByteBuffer asReadOnlyBuffer(ByteBuffer src) {
return src.asReadOnlyBuffer();
}

View File

@@ -1312,11 +1312,12 @@ internal class Transforms {
}
private fun unpackTransforms(prefixSuffix: ByteArray, prefixSuffixHeads: IntArray, transforms: IntArray, prefixSuffixSrc: String, transformsSrc: String): Unit {
val n: Int = prefixSuffixSrc.length;
val prefixSuffixBytes: IntArray = toUtf8Runes(prefixSuffixSrc);
val n: Int = prefixSuffixBytes.size;
var index: Int = 1;
var j: Int = 0;
for (i: Int in 0 until n) {
val c: Int = prefixSuffixSrc[i].code.toInt();
val c: Int = prefixSuffixBytes[i];
if (c == 35) {
prefixSuffixHeads[index++] = j;
} else {
@@ -1945,11 +1946,12 @@ fun setData(newData: ByteBuffer, newSizeBits: IntArray): Unit {
private fun unpackDictionaryData(dictionary: ByteBuffer, data0: String, data1: String, skipFlip: String, sizeBits: IntArray, sizeBitsData: String): Unit {
val dict: ByteArray = toUsAsciiBytes(data0 + data1);
val skipFlipRunes: IntArray = toUtf8Runes(skipFlip);
var offset: Int = 0;
val n: Int = skipFlip.length shr 1;
val n: Int = skipFlipRunes.size shr 1;
for (i: Int in 0 until n) {
val skip: Int = skipFlip[2 * i].code.toInt() - 36;
val flip: Int = skipFlip[2 * i + 1].code.toInt() - 36;
val skip: Int = skipFlipRunes[2 * i] - 36;
val flip: Int = skipFlipRunes[2 * i + 1] - 36;
for (j: Int in 0 until skip) {
dict[offset] = (dict[offset].toInt() xor 3).toByte();
offset++;
@@ -2015,6 +2017,15 @@ internal fun toUsAsciiBytes(src: String): ByteArray {
return src.toByteArray(Charsets.US_ASCII);
}
internal fun toUtf8Runes(src: String): IntArray {
var len: Int = src.length;
var result: IntArray = IntArray(size = len);
for (i: Int in 0 until len) {
result[i] = src[i].code.toInt();
}
return result;
}
internal fun flipBuffer(buffer: Buffer): Unit {
buffer.flip();
}

View File

@@ -1574,11 +1574,12 @@ let makeBrotliDecode = () => {
* @return {void}
*/
function unpackTransforms(prefixSuffix, prefixSuffixHeads, transforms, prefixSuffixSrc, transformsSrc) {
const /** @type {number} */ n = prefixSuffixSrc.length;
const /** @type {!Int32Array} */ prefixSuffixBytes = toUtf8Runes(prefixSuffixSrc);
const /** @type {number} */ n = prefixSuffixBytes.length;
let /** @type {number} */ index = 1;
let /** @type {number} */ j = 0;
for (let /** @type {number} */ i = 0; i < n; ++i) {
const /** @type {number} */ c = prefixSuffixSrc.charCodeAt(i);
const /** @type {number} */ c = prefixSuffixBytes[i];
if (c === 35) {
prefixSuffixHeads[index++] = j;
} else {
@@ -2290,11 +2291,12 @@ let makeBrotliDecode = () => {
*/
function unpackDictionaryData(dictionary, data0, data1, skipFlip, sizeBits, sizeBitsData) {
const /** @type {!Int8Array} */ dict = toUsAsciiBytes(data0 + data1);
const /** @type {!Int32Array} */ skipFlipRunes = toUtf8Runes(skipFlip);
let /** @type {number} */ offset = 0;
const /** @type {number} */ n = skipFlip.length >> 1;
const /** @type {number} */ n = skipFlipRunes.length >> 1;
for (let /** @type {number} */ i = 0; i < n; ++i) {
const /** @type {number} */ skip = skipFlip.charCodeAt(2 * i) - 36;
const /** @type {number} */ flip = skipFlip.charCodeAt(2 * i + 1) - 36;
const /** @type {number} */ skip = skipFlipRunes[2 * i] - 36;
const /** @type {number} */ flip = skipFlipRunes[2 * i + 1] - 36;
for (let /** @type {number} */ j = 0; j < skip; ++j) {
dict[offset] = dict[offset] ^ 3;
offset++;
@@ -2374,6 +2376,18 @@ let makeBrotliDecode = () => {
}
return result;
}
/**
* @param {string} src
* @return {!Int32Array}
*/
function toUtf8Runes(src) {
const /** @type {number} */ n = src.length;
const /** @type {!Int32Array} */ result = new Int32Array(n);
for (let /** @type {number} */ i = 0; i < n; ++i) {
result[i] = src.charCodeAt(i);
}
return result;
}
/**
* @param {!State} s
* @param {number} code

2
js/decode.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1328,11 +1328,12 @@ class Transforms {
const RFC_TRANSFORMS = new Transforms(121, 167, 50);
function unpackTransforms(prefixSuffix: Int8Array, prefixSuffixHeads: Int32Array, transforms: Int32Array, prefixSuffixSrc: string, transformsSrc: string): void {
const n: number = prefixSuffixSrc.length;
const prefixSuffixBytes: Int32Array = toUtf8Runes(prefixSuffixSrc);
const n: number = prefixSuffixBytes.length;
let index = 1;
let j = 0;
for (let i = 0; i < n; ++i) {
const c: number = prefixSuffixSrc.charCodeAt(i);
const c: number = prefixSuffixBytes[i];
if (c === 35) {
prefixSuffixHeads[index++] = j;
} else {
@@ -1858,11 +1859,12 @@ function setData(newData: ByteBuffer, newSizeBits: Int32Array): void {
function unpackDictionaryData(dictionary: ByteBuffer, data0: string, data1: string, skipFlip: string, sizeBits: Int32Array, sizeBitsData: string): void {
const dict: Int8Array = toUsAsciiBytes(data0 + data1);
const skipFlipRunes: Int32Array = toUtf8Runes(skipFlip);
let offset = 0;
const n: number = skipFlip.length >> 1;
const n: number = skipFlipRunes.length >> 1;
for (let i = 0; i < n; ++i) {
const skip: number = skipFlip.charCodeAt(2 * i) - 36;
const flip: number = skipFlip.charCodeAt(2 * i + 1) - 36;
const skip: number = skipFlipRunes[2 * i] - 36;
const flip: number = skipFlipRunes[2 * i + 1] - 36;
for (let j = 0; j < skip; ++j) {
dict[offset] = dict[offset] ^ 3;
offset++;
@@ -1918,6 +1920,14 @@ function toUsAsciiBytes(src: string): Int8Array {
}
return result;
}
function toUtf8Runes(src: string): Int32Array {
const n: number = src.length;
const result = new Int32Array(n);
for (let i = 0; i < n; ++i) {
result[i] = src.charCodeAt(i);
}
return result;
}
function makeError(s: State, code: number): number {
if (code >= 0) {
return code;