Skip to content

Commit

Permalink
Finishd OrderedCode
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Dec 16, 2021
1 parent eb13a86 commit a14ffc8
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 2 deletions.
46 changes: 46 additions & 0 deletions packages/firestore/src/index/ordered_code_writer.ts
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/
import { debugAssert } from '../util/assert';
import { ByteString } from '../util/byte_string';

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

const ESCAPE2 = 0xff;
const INFINITY = 0xff; // Combined with ESCAPE2
const FF_BYTE = 0x00; // Combined with ESCAPE2

const LONG_SIZE = 64;
Expand Down Expand Up @@ -110,6 +112,26 @@ export class OrderedCodeWriter {
buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
position = 0;

writeBytesAscending(value: ByteString): void {
const it = value[Symbol.iterator]();
let byte = it.next();
while (!byte.done) {
this.writeByteAscending(byte.value);
byte = it.next();
}
this.writeSeparatorAscending();
}

writeBytesDescending(value: ByteString): void {
const it = value[Symbol.iterator]();
let byte = it.next();
while (!byte.done) {
this.writeByteDescending(byte.value);
byte = it.next();
}
this.writeSeparatorDescending();
}

/** Writes utf8 bytes into this byte sequence, ascending. */
writeUtf8Ascending(sequence: string): void {
for (const c of sequence) {
Expand Down Expand Up @@ -182,6 +204,24 @@ export class OrderedCodeWriter {
}
}

/**
* Writes the "infinity" byte sequence that sorts after all other byte
* sequences written in ascending order.
*/
writeInfinityAscending(): void {
this.writeEscapedByteAscending(ESCAPE2);
this.writeEscapedByteAscending(INFINITY);
}

/**
* Writes the "infinity" byte sequence that sorts before all other byte
* sequences written in descending order.
*/
writeInfinityDescending(): void {
this.writeEscapedByteDescending(ESCAPE2);
this.writeEscapedByteDescending(INFINITY);
}

/**
* Encodes `val` into an encoding so that the order matches the IEEE 754
* floating-point comparison results with the following exceptions:
Expand Down Expand Up @@ -277,4 +317,10 @@ export class OrderedCodeWriter {
newBuffer.set(this.buffer); // copy old data
this.buffer = newBuffer;
}

seed(encodedBytes: Uint8Array): void {
this.ensureAvailable(encodedBytes.length);
this.buffer.set(encodedBytes, this.position);
this.position += encodedBytes.length;
}
}
13 changes: 13 additions & 0 deletions packages/firestore/src/util/byte_string.ts
Expand Up @@ -43,6 +43,19 @@ export class ByteString {
return new ByteString(binaryString);
}

[Symbol.iterator](): Iterator<number> {
let i = 0;
return {
next: () => {
if (i < this.binaryString.length) {
return { value: this.binaryString.charCodeAt(i++), done: false };
} else {
return { value: undefined, done: true };
}
}
};
}

toBase64(): string {
return encodeBase64(this.binaryString);
}
Expand Down
51 changes: 49 additions & 2 deletions packages/firestore/test/unit/index/ordered_code_writer.test.ts
Expand Up @@ -20,6 +20,7 @@ import {
numberOfLeadingZerosInByte,
OrderedCodeWriter
} from '../../../src/index/ordered_code_writer';
import { ByteString } from '../../../src/util/byte_string';

class ValueTestCase<T> {
constructor(
Expand Down Expand Up @@ -108,6 +109,23 @@ const STRING_TEST_CASES: Array<ValueTestCase<string>> = [
)
];

const BYTES_TEST_CASES: Array<ValueTestCase<Uint8Array>> = [
new ValueTestCase(fromHex(''), '0001', 'fffe'),
new ValueTestCase(fromHex('00'), '00ff0001', 'ff00fffe'),
new ValueTestCase(fromHex('0000'), '00ff00ff0001', 'ff00ff00fffe'),
new ValueTestCase(fromHex('0001'), '00ff010001', 'ff00fefffe'),
new ValueTestCase(fromHex('0041'), '00ff410001', 'ff00befffe'),
new ValueTestCase(fromHex('00ff'), '00ffff000001', 'ff0000fffffe'),
new ValueTestCase(fromHex('01'), '010001', 'fefffe'),
new ValueTestCase(fromHex('0100'), '0100ff0001', 'feff00fffe'),
new ValueTestCase(fromHex('6f776c'), '6f776c0001', '908893fffe'),
new ValueTestCase(fromHex('ff'), 'ff000001', '00fffffe'),
new ValueTestCase(fromHex('ff00'), 'ff0000ff0001', '00ffff00fffe'),
new ValueTestCase(fromHex('ff01'), 'ff00010001', '00fffefffe'),
new ValueTestCase(fromHex('ffff'), 'ff00ff000001', '00ff00fffffe'),
new ValueTestCase(fromHex('ffffff'), 'ff00ff00ff000001', '00ff00ff00fffffe')
];

describe('Ordered Code Writer', () => {
it('computes number of leading zeros', () => {
for (let i = 0; i < 0xff; ++i) {
Expand Down Expand Up @@ -139,6 +157,34 @@ describe('Ordered Code Writer', () => {
verifyOrdering(STRING_TEST_CASES);
});

it('converts bytes to bits', () => {
verifyEncoding(BYTES_TEST_CASES);
});

it('orders bytes correctly', () => {
verifyOrdering(BYTES_TEST_CASES);
});

it('encodes infinity', () => {
const writer = new OrderedCodeWriter();
writer.writeInfinityAscending();
expect(writer.encodedBytes()).to.deep.equal(fromHex("ffff"));

writer.reset();
writer.writeInfinityDescending();
expect(writer.encodedBytes()).to.deep.equal(fromHex("0000"));
});

it('seeds bytes', () => {
const writer = new OrderedCodeWriter();
writer.seed(new Uint8Array([0x01]));
writer.writeInfinityAscending();
writer.seed(new Uint8Array([0x02]));
expect(writer.encodedBytes()).to.deep.equal(
fromHex("01ffff02")
);
});

function verifyEncoding(testCases: Array<ValueTestCase<unknown>>): void {
for (let i = 0; i < testCases.length; ++i) {
const bytes = getBytes(testCases[i].val);
Expand Down Expand Up @@ -202,8 +248,9 @@ function getBytes(val: unknown): { asc: Uint8Array; desc: Uint8Array } {
} else if (typeof val === 'string') {
ascWriter.writeUtf8Ascending(val);
descWriter.writeUtf8Descending(val);
} else {
throw new Error('Encoding not yet supported for ' + val);
} else if (val instanceof Uint8Array) {
ascWriter.writeBytesAscending(ByteString.fromUint8Array(val));
descWriter.writeBytesDescending(ByteString.fromUint8Array(val));
}
return { asc: ascWriter.encodedBytes(), desc: descWriter.encodedBytes() };
}

0 comments on commit a14ffc8

Please sign in to comment.