Skip to content

Commit f9fb930

Browse files
Add UTF-8
1 parent ba2e33a commit f9fb930

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

packages/firestore/src/index/ordered_code_writer.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { ByteString } from '../util/byte_string';
19+
20+
/** These constants are taken from the backend. */
21+
const MIN_SURROGATE = '\uD800';
22+
const MAX_SURROGATE = '\uDBFF';
23+
24+
const ESCAPE1 = 0x00;
25+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
26+
const SEPARATOR = 0x01; // Combined with ESCAPE1
27+
28+
const ESCAPE2 = 0xff;
29+
const INFINITY = 0xff; // Combined with ESCAPE2
30+
const FF_BYTE = 0x00; // Combined with ESCAPE2
31+
1832
const LONG_SIZE = 64;
1933
const BYTE_SIZE = 8;
2034

@@ -96,6 +110,70 @@ export class OrderedCodeWriter {
96110
buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
97111
position = 0;
98112

113+
/**
114+
* Writes utf8 bytes into this byte sequence, ascending.
115+
*
116+
* This is a more efficent version of writeBytesAscending(str.getBytes(UTF_8));
117+
*/
118+
writeUtf8Ascending(sequence: string): void {
119+
const utf16Length = sequence.length;
120+
let i = 0;
121+
while (i < utf16Length) {
122+
const char = sequence[i];
123+
const charCode = sequence.charCodeAt(i);
124+
if (charCode < 0x80) {
125+
this.writeByteAscending(charCode);
126+
} else if (charCode < 0x800) {
127+
this.writeByteAscending((0xf >> 6) | (charCode >>> 6));
128+
this.writeByteAscending(0x80 | (0x3f & charCode));
129+
} else if (char < MIN_SURROGATE || MAX_SURROGATE < char) {
130+
this.writeByteAscending((0xf >> 5) | (charCode >>> 12));
131+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
132+
this.writeByteAscending(0x80 | (0x3f & charCode));
133+
} else {
134+
++i; // Skip the low surrogate.
135+
this.writeByteAscending((0xf >> 4) | (charCode >>> 18));
136+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 12)));
137+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
138+
this.writeByteAscending(0x80 | (0x3f & charCode));
139+
}
140+
++i;
141+
}
142+
this.writeSeparatorAscending();
143+
}
144+
145+
/**
146+
* Writes utf8 bytes into this byte sequence, descending.
147+
*
148+
* This is a more efficent version of writeBytesDescending(str.getBytes(UTF_8));
149+
*/
150+
writeUtf8Descending(sequence: string): void {
151+
const utf16Length = sequence.length;
152+
let i = 0;
153+
while (i < utf16Length) {
154+
const char = sequence[i];
155+
const charCode = sequence.charCodeAt(i);
156+
if (charCode < 0x80) {
157+
this.writeByteDescending(charCode);
158+
} else if (charCode < 0x800) {
159+
this.writeByteDescending((0xf >> 6) | (charCode >>> 6));
160+
this.writeByteDescending(0x80 | (0x3f & charCode));
161+
} else if (char < MIN_SURROGATE || MAX_SURROGATE < char) {
162+
this.writeByteDescending((0xf >> 5) | (charCode >>> 12));
163+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
164+
this.writeByteDescending(0x80 | (0x3f & charCode));
165+
} else {
166+
++i; // Skip the low surrogate.
167+
this.writeByteDescending((0xf >> 4) | (charCode >>> 18));
168+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 12)));
169+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
170+
this.writeByteDescending(0x80 | (0x3f & charCode));
171+
}
172+
++i;
173+
}
174+
this.writeSeparatorDescending();
175+
}
176+
99177
writeNumberAscending(val: number): void {
100178
const value = this.toOrderedBits(val);
101179
const len = unsignedNumLength(value);
@@ -143,6 +221,56 @@ export class OrderedCodeWriter {
143221
return this.buffer.slice(0, this.position);
144222
}
145223

224+
/**
225+
* Writes a single byte ascending to the buffer, doing proper escaping as described in [ ].
226+
*/
227+
private writeByteAscending(b: number): void {
228+
if (b === ESCAPE1) {
229+
this.writeEscapedByteAscending(ESCAPE1);
230+
this.writeEscapedByteAscending(NULL_BYTE);
231+
} else if (b === ESCAPE2) {
232+
this.writeEscapedByteAscending(ESCAPE2);
233+
this.writeEscapedByteAscending(FF_BYTE);
234+
} else {
235+
this.writeEscapedByteAscending(b);
236+
}
237+
}
238+
239+
/**
240+
* Writes a single byte descending to the buffer, doing proper escaping as described in [ ].
241+
*/
242+
private writeByteDescending(b: number): void {
243+
if (b === ESCAPE1) {
244+
this.writeEscapedByteDescending(ESCAPE1);
245+
this.writeEscapedByteDescending(NULL_BYTE);
246+
} else if (b === ESCAPE2) {
247+
this.writeEscapedByteDescending(ESCAPE2);
248+
this.writeEscapedByteDescending(FF_BYTE);
249+
} else {
250+
this.writeEscapedByteDescending(b);
251+
}
252+
}
253+
254+
private writeSeparatorAscending(): void {
255+
this.writeEscapedByteAscending(ESCAPE1);
256+
this.writeEscapedByteAscending(SEPARATOR);
257+
}
258+
259+
private writeSeparatorDescending(): void {
260+
this.writeEscapedByteDescending(ESCAPE1);
261+
this.writeEscapedByteDescending(SEPARATOR);
262+
}
263+
264+
private writeEscapedByteAscending(b: number): void {
265+
this.ensureAvailable(1);
266+
this.buffer[this.position++] = b;
267+
}
268+
269+
private writeEscapedByteDescending(b: number): void {
270+
this.ensureAvailable(1);
271+
this.buffer[this.position++] = ~b;
272+
}
273+
146274
private ensureAvailable(bytes: number): void {
147275
const minCapacity = bytes + this.position;
148276
if (minCapacity <= this.buffer.length) {

0 commit comments

Comments
 (0)