Skip to content

Commit a14ffc8

Browse files
Finishd OrderedCode
1 parent eb13a86 commit a14ffc8

File tree

3 files changed

+108
-2
lines changed

3 files changed

+108
-2
lines changed

packages/firestore/src/index/ordered_code_writer.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717
import { debugAssert } from '../util/assert';
18+
import { ByteString } from '../util/byte_string';
1819

1920
/** These constants are taken from the backend. */
2021
const MIN_SURROGATE = '\uD800';
@@ -25,6 +26,7 @@ const NULL_BYTE = 0xff; // Combined with ESCAPE1
2526
const SEPARATOR = 0x01; // Combined with ESCAPE1
2627

2728
const ESCAPE2 = 0xff;
29+
const INFINITY = 0xff; // Combined with ESCAPE2
2830
const FF_BYTE = 0x00; // Combined with ESCAPE2
2931

3032
const LONG_SIZE = 64;
@@ -110,6 +112,26 @@ export class OrderedCodeWriter {
110112
buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
111113
position = 0;
112114

115+
writeBytesAscending(value: ByteString): void {
116+
const it = value[Symbol.iterator]();
117+
let byte = it.next();
118+
while (!byte.done) {
119+
this.writeByteAscending(byte.value);
120+
byte = it.next();
121+
}
122+
this.writeSeparatorAscending();
123+
}
124+
125+
writeBytesDescending(value: ByteString): void {
126+
const it = value[Symbol.iterator]();
127+
let byte = it.next();
128+
while (!byte.done) {
129+
this.writeByteDescending(byte.value);
130+
byte = it.next();
131+
}
132+
this.writeSeparatorDescending();
133+
}
134+
113135
/** Writes utf8 bytes into this byte sequence, ascending. */
114136
writeUtf8Ascending(sequence: string): void {
115137
for (const c of sequence) {
@@ -182,6 +204,24 @@ export class OrderedCodeWriter {
182204
}
183205
}
184206

207+
/**
208+
* Writes the "infinity" byte sequence that sorts after all other byte
209+
* sequences written in ascending order.
210+
*/
211+
writeInfinityAscending(): void {
212+
this.writeEscapedByteAscending(ESCAPE2);
213+
this.writeEscapedByteAscending(INFINITY);
214+
}
215+
216+
/**
217+
* Writes the "infinity" byte sequence that sorts before all other byte
218+
* sequences written in descending order.
219+
*/
220+
writeInfinityDescending(): void {
221+
this.writeEscapedByteDescending(ESCAPE2);
222+
this.writeEscapedByteDescending(INFINITY);
223+
}
224+
185225
/**
186226
* Encodes `val` into an encoding so that the order matches the IEEE 754
187227
* floating-point comparison results with the following exceptions:
@@ -277,4 +317,10 @@ export class OrderedCodeWriter {
277317
newBuffer.set(this.buffer); // copy old data
278318
this.buffer = newBuffer;
279319
}
320+
321+
seed(encodedBytes: Uint8Array): void {
322+
this.ensureAvailable(encodedBytes.length);
323+
this.buffer.set(encodedBytes, this.position);
324+
this.position += encodedBytes.length;
325+
}
280326
}

packages/firestore/src/util/byte_string.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ export class ByteString {
4343
return new ByteString(binaryString);
4444
}
4545

46+
[Symbol.iterator](): Iterator<number> {
47+
let i = 0;
48+
return {
49+
next: () => {
50+
if (i < this.binaryString.length) {
51+
return { value: this.binaryString.charCodeAt(i++), done: false };
52+
} else {
53+
return { value: undefined, done: true };
54+
}
55+
}
56+
};
57+
}
58+
4659
toBase64(): string {
4760
return encodeBase64(this.binaryString);
4861
}

packages/firestore/test/unit/index/ordered_code_writer.test.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
numberOfLeadingZerosInByte,
2121
OrderedCodeWriter
2222
} from '../../../src/index/ordered_code_writer';
23+
import { ByteString } from '../../../src/util/byte_string';
2324

2425
class ValueTestCase<T> {
2526
constructor(
@@ -108,6 +109,23 @@ const STRING_TEST_CASES: Array<ValueTestCase<string>> = [
108109
)
109110
];
110111

112+
const BYTES_TEST_CASES: Array<ValueTestCase<Uint8Array>> = [
113+
new ValueTestCase(fromHex(''), '0001', 'fffe'),
114+
new ValueTestCase(fromHex('00'), '00ff0001', 'ff00fffe'),
115+
new ValueTestCase(fromHex('0000'), '00ff00ff0001', 'ff00ff00fffe'),
116+
new ValueTestCase(fromHex('0001'), '00ff010001', 'ff00fefffe'),
117+
new ValueTestCase(fromHex('0041'), '00ff410001', 'ff00befffe'),
118+
new ValueTestCase(fromHex('00ff'), '00ffff000001', 'ff0000fffffe'),
119+
new ValueTestCase(fromHex('01'), '010001', 'fefffe'),
120+
new ValueTestCase(fromHex('0100'), '0100ff0001', 'feff00fffe'),
121+
new ValueTestCase(fromHex('6f776c'), '6f776c0001', '908893fffe'),
122+
new ValueTestCase(fromHex('ff'), 'ff000001', '00fffffe'),
123+
new ValueTestCase(fromHex('ff00'), 'ff0000ff0001', '00ffff00fffe'),
124+
new ValueTestCase(fromHex('ff01'), 'ff00010001', '00fffefffe'),
125+
new ValueTestCase(fromHex('ffff'), 'ff00ff000001', '00ff00fffffe'),
126+
new ValueTestCase(fromHex('ffffff'), 'ff00ff00ff000001', '00ff00ff00fffffe')
127+
];
128+
111129
describe('Ordered Code Writer', () => {
112130
it('computes number of leading zeros', () => {
113131
for (let i = 0; i < 0xff; ++i) {
@@ -139,6 +157,34 @@ describe('Ordered Code Writer', () => {
139157
verifyOrdering(STRING_TEST_CASES);
140158
});
141159

160+
it('converts bytes to bits', () => {
161+
verifyEncoding(BYTES_TEST_CASES);
162+
});
163+
164+
it('orders bytes correctly', () => {
165+
verifyOrdering(BYTES_TEST_CASES);
166+
});
167+
168+
it('encodes infinity', () => {
169+
const writer = new OrderedCodeWriter();
170+
writer.writeInfinityAscending();
171+
expect(writer.encodedBytes()).to.deep.equal(fromHex("ffff"));
172+
173+
writer.reset();
174+
writer.writeInfinityDescending();
175+
expect(writer.encodedBytes()).to.deep.equal(fromHex("0000"));
176+
});
177+
178+
it('seeds bytes', () => {
179+
const writer = new OrderedCodeWriter();
180+
writer.seed(new Uint8Array([0x01]));
181+
writer.writeInfinityAscending();
182+
writer.seed(new Uint8Array([0x02]));
183+
expect(writer.encodedBytes()).to.deep.equal(
184+
fromHex("01ffff02")
185+
);
186+
});
187+
142188
function verifyEncoding(testCases: Array<ValueTestCase<unknown>>): void {
143189
for (let i = 0; i < testCases.length; ++i) {
144190
const bytes = getBytes(testCases[i].val);
@@ -202,8 +248,9 @@ function getBytes(val: unknown): { asc: Uint8Array; desc: Uint8Array } {
202248
} else if (typeof val === 'string') {
203249
ascWriter.writeUtf8Ascending(val);
204250
descWriter.writeUtf8Descending(val);
205-
} else {
206-
throw new Error('Encoding not yet supported for ' + val);
251+
} else if (val instanceof Uint8Array) {
252+
ascWriter.writeBytesAscending(ByteString.fromUint8Array(val));
253+
descWriter.writeBytesDescending(ByteString.fromUint8Array(val));
207254
}
208255
return { asc: ascWriter.encodedBytes(), desc: descWriter.encodedBytes() };
209256
}

0 commit comments

Comments
 (0)