Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 97eb530

Browse files
Experimental cache string decoder for map keys
1 parent cdcc306 commit 97eb530

File tree

2 files changed

+129
-10
lines changed

2 files changed

+129
-10
lines changed

‎src/Decoder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { utf8DecodeJs, TEXT_DECODER_AVAILABLE, TEXT_DECODER_THRESHOLD, utf8Decod
55
import { createDataView, ensureUint8Array } from "./utils/typedArrays";
66
import { WASM_AVAILABLE, WASM_STR_THRESHOLD, utf8DecodeWasm } from "./wasmFunctions";
77

8-
enum State {
8+
exportenum State {
99
ARRAY,
1010
MAP_KEY,
1111
MAP_VALUE,

‎src/decode.ts

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ExtensionCodecType } from "./ExtensionCodec";
2-
import { Decoder } from "./Decoder";
2+
import { Decoder, State } from "./Decoder";
3+
import { utf8DecodeJs } from "./utils/utf8";
34

45
export type DecodeOptions = Partial<
56
Readonly<{
@@ -35,15 +36,133 @@ export type DecodeOptions = Partial<
3536

3637
export const defaultDecodeOptions: DecodeOptions = {};
3738

39+
interface KeyCacheRecord {
40+
readonly bytes: Uint8Array;
41+
readonly key: string;
42+
hits: number;
43+
}
44+
45+
class CachedKeyDecoder {
46+
private caches: Array<Array<KeyCacheRecord>>;
47+
48+
constructor(private maxKeyLength: number = 32) {
49+
this.caches = new Array<Array<KeyCacheRecord>>(this.maxKeyLength + 1);
50+
}
51+
52+
public accepts(keyLength: number) {
53+
return keyLength > 0 && keyLength <= this.maxKeyLength;
54+
}
55+
56+
public get(bytes: Uint8Array): string | null {
57+
const chunks = this.caches[bytes.length];
58+
59+
if (chunks) {
60+
return this.findKey(bytes, chunks);
61+
} else {
62+
return null;
63+
}
64+
}
65+
66+
private findKey(bytes: Uint8Array, chunks: Array<KeyCacheRecord>): string | null {
67+
const size = bytes.length;
68+
let prevHits = 0;
69+
let result: string | null = null;
70+
FIND_CHUNK: for (let i = 0; i < chunks.length; i++) {
71+
const chunk = chunks[i];
72+
73+
if (i > 0 && prevHits < chunk.hits) {
74+
// Sort chunks by number of hits
75+
// in order to improve search speed for most used keys
76+
const prevChunk = chunks[i - 1];
77+
chunks[i] = prevChunk;
78+
chunks[i - 1] = chunk;
79+
prevHits = prevChunk.hits;
80+
} else {
81+
prevHits = chunk.hits;
82+
}
83+
84+
for (let j = 0; j < size / 2; j++) {
85+
if (chunk.bytes[j] !== bytes[j]) {
86+
continue FIND_CHUNK;
87+
}
88+
89+
if (chunk.bytes[size - j - 1] !== bytes[size - j - 1]) {
90+
continue FIND_CHUNK;
91+
}
92+
}
93+
94+
chunk.hits++;
95+
96+
result = chunk.key;
97+
98+
break;
99+
}
100+
101+
return result;
102+
}
103+
104+
public cache(bytes: Uint8Array, value: string) {
105+
let chunks: Array<KeyCacheRecord> = this.caches[bytes.length];
106+
107+
if (!chunks) {
108+
chunks = [];
109+
this.caches[bytes.length] = chunks;
110+
}
111+
112+
chunks.push({
113+
bytes: bytes,
114+
key: value,
115+
hits: 1,
116+
});
117+
}
118+
119+
public decode(bytes: Uint8Array, inputOffset: number, byteLength: number): string {
120+
const stringBytes = bytes.subarray(inputOffset, inputOffset + byteLength);
121+
let value = this.get(stringBytes);
122+
123+
if (!value) {
124+
value = utf8DecodeJs(bytes.slice(inputOffset, inputOffset + byteLength), inputOffset, byteLength);
125+
this.cache(stringBytes, value);
126+
}
127+
128+
return value;
129+
}
130+
}
131+
132+
class CustomDecoder extends Decoder {
133+
private maxCachedKeyLength = 32;
134+
public cachedKeyDecoder = new CachedKeyDecoder(this.maxCachedKeyLength);
135+
136+
public decodeUtf8String(byteLength: number, headerOffset: number): string {
137+
let isKey = false;
138+
139+
if (this.stack.length > 0) {
140+
const state = this.stack[this.stack.length - 1];
141+
142+
isKey = state.type === State.MAP_KEY;
143+
}
144+
145+
if (isKey && byteLength > 0 && byteLength < this.maxCachedKeyLength) {
146+
const offset = this.pos + headerOffset;
147+
const value = this.cachedKeyDecoder.decode(this.bytes, offset, byteLength);
148+
this.pos += headerOffset + byteLength;
149+
return value;
150+
} else {
151+
return super.decodeUtf8String(byteLength, headerOffset);
152+
}
153+
}
154+
}
155+
156+
const decoder = new CustomDecoder(
157+
defaultDecodeOptions.extensionCodec,
158+
defaultDecodeOptions.maxStrLength,
159+
defaultDecodeOptions.maxBinLength,
160+
defaultDecodeOptions.maxArrayLength,
161+
defaultDecodeOptions.maxMapLength,
162+
defaultDecodeOptions.maxExtLength,
163+
);
164+
38165
export function decode(buffer: ArrayLike<number>, options: DecodeOptions = defaultDecodeOptions): unknown {
39-
const decoder = new Decoder(
40-
options.extensionCodec,
41-
options.maxStrLength,
42-
options.maxBinLength,
43-
options.maxArrayLength,
44-
options.maxMapLength,
45-
options.maxExtLength,
46-
);
47166
decoder.setBuffer(buffer); // decodeSync() requires only one buffer
48167
return decoder.decodeOneSync();
49168
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /