Skip to content

Commit 2919c0a

Browse files
author
Brian Chen
authored
Change ProtoByteString to use its own class rather than String | Uint8Array (#2639)
1 parent 4c2f8d1 commit 2919c0a

27 files changed

+271
-200
lines changed

packages/firestore/src/api/blob.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import {
2424
validateExactNumberOfArgs
2525
} from '../util/input_validation';
2626
import { primitiveComparator } from '../util/misc';
27+
import {
28+
binaryStringFromUint8Array,
29+
uint8ArrayFromBinaryString
30+
} from '../util/proto_byte_string';
2731

2832
/** Helper function to assert Uint8Array is available at runtime. */
2933
function assertUint8ArrayAvailable(): void {
@@ -85,10 +89,7 @@ export class Blob {
8589
if (!(array instanceof Uint8Array)) {
8690
throw invalidClassError('Blob.fromUint8Array', 'Uint8Array', 1, array);
8791
}
88-
let binaryString = '';
89-
for (let i = 0; i < array.length; ++i) {
90-
binaryString += String.fromCharCode(array[i]);
91-
}
92+
const binaryString = binaryStringFromUint8Array(array);
9293
return new Blob(binaryString);
9394
}
9495

@@ -101,10 +102,7 @@ export class Blob {
101102
toUint8Array(): Uint8Array {
102103
validateExactNumberOfArgs('Blob.toUint8Array', arguments, 0);
103104
assertUint8ArrayAvailable();
104-
const buffer = new Uint8Array(this._binaryString.length);
105-
for (let i = 0; i < this._binaryString.length; i++) {
106-
buffer[i] = this._binaryString.charCodeAt(i);
107-
}
105+
const buffer = uint8ArrayFromBinaryString(this._binaryString);
108106
return buffer;
109107
}
110108

packages/firestore/src/core/types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export type TargetId = number;
2929

3030
export type ListenSequenceNumber = number;
3131

32-
// TODO(b/35918695): In GRPC / node, tokens are Uint8Array. In WebChannel,
33-
// they're strings. We should probably (de-)serialize to a common internal type.
34-
export type ProtoByteString = Uint8Array | string;
35-
3632
/** The different states of a mutation batch. */
3733
export type MutationBatchState = 'pending' | 'acknowledged' | 'rejected';
3834

packages/firestore/src/local/indexeddb_mutation_queue.ts

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
import { Timestamp } from '../api/timestamp';
1919
import { User } from '../auth/user';
2020
import { Query } from '../core/query';
21-
import { BatchId, ProtoByteString } from '../core/types';
21+
import { BatchId } from '../core/types';
2222
import { DocumentKeySet } from '../model/collections';
2323
import { DocumentKey } from '../model/document_key';
2424
import { Mutation } from '../model/mutation';
2525
import { BATCHID_UNKNOWN, MutationBatch } from '../model/mutation_batch';
2626
import { ResourcePath } from '../model/path';
2727
import { assert, fail } from '../util/assert';
2828
import { primitiveComparator } from '../util/misc';
29+
import { ByteString } from '../util/proto_byte_string';
2930
import { SortedMap } from '../util/sorted_map';
3031
import { SortedSet } from '../util/sorted_set';
3132

@@ -47,7 +48,7 @@ import { LocalSerializer } from './local_serializer';
4748
import { MutationQueue } from './mutation_queue';
4849
import { PersistenceTransaction, ReferenceDelegate } from './persistence';
4950
import { PersistencePromise } from './persistence_promise';
50-
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
51+
import { SimpleDbStore, SimpleDbTransaction } from './simple_db';
5152

5253
/** A mutation queue for a specific user, backed by IndexedDB. */
5354
export class IndexedDbMutationQueue implements MutationQueue {
@@ -121,29 +122,33 @@ export class IndexedDbMutationQueue implements MutationQueue {
121122
acknowledgeBatch(
122123
transaction: PersistenceTransaction,
123124
batch: MutationBatch,
124-
streamToken: ProtoByteString
125+
streamToken: ByteString
125126
): PersistencePromise<void> {
126127
return this.getMutationQueueMetadata(transaction).next(metadata => {
127-
metadata.lastStreamToken = convertStreamToken(streamToken);
128+
// We can't store the resumeToken as a ByteString in IndexedDB, so we
129+
// convert it to a Base64 string for storage.
130+
metadata.lastStreamToken = streamToken.toBase64();
128131

129132
return mutationQueuesStore(transaction).put(metadata);
130133
});
131134
}
132135

133136
getLastStreamToken(
134137
transaction: PersistenceTransaction
135-
): PersistencePromise<ProtoByteString> {
136-
return this.getMutationQueueMetadata(transaction).next<ProtoByteString>(
137-
metadata => metadata.lastStreamToken
138+
): PersistencePromise<ByteString> {
139+
return this.getMutationQueueMetadata(transaction).next<ByteString>(
140+
metadata => ByteString.fromBase64String(metadata.lastStreamToken)
138141
);
139142
}
140143

141144
setLastStreamToken(
142145
transaction: PersistenceTransaction,
143-
streamToken: ProtoByteString
146+
streamToken: ByteString
144147
): PersistencePromise<void> {
145148
return this.getMutationQueueMetadata(transaction).next(metadata => {
146-
metadata.lastStreamToken = convertStreamToken(streamToken);
149+
// We can't store the resumeToken as a ByteString in IndexedDB, so we
150+
// convert it to a Base64 string for storage.
151+
metadata.lastStreamToken = streamToken.toBase64();
147152
return mutationQueuesStore(transaction).put(metadata);
148153
});
149154
}
@@ -671,19 +676,6 @@ export function removeMutationBatch(
671676
return PersistencePromise.waitFor(promises).next(() => removedDocuments);
672677
}
673678

674-
function convertStreamToken(token: ProtoByteString): string {
675-
if (token instanceof Uint8Array) {
676-
// TODO(b/78771403): Convert tokens to strings during deserialization
677-
assert(
678-
SimpleDb.isMockPersistence(),
679-
'Persisting non-string stream tokens is only supported with mock persistence.'
680-
);
681-
return token.toString();
682-
} else {
683-
return token;
684-
}
685-
}
686-
687679
/**
688680
* Helper to get a typed SimpleDbStore for the mutations object store.
689681
*/

packages/firestore/src/local/local_serializer.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { MutationBatch } from '../model/mutation_batch';
2828
import * as api from '../protos/firestore_proto_api';
2929
import { JsonProtoSerializer } from '../remote/serializer';
3030
import { assert, fail } from '../util/assert';
31+
import { ByteString } from '../util/proto_byte_string';
3132

3233
import { documentKeySet, DocumentKeySet } from '../model/collections';
3334
import { Target } from '../core/target';
@@ -42,7 +43,6 @@ import {
4243
DbTimestampKey,
4344
DbUnknownDocument
4445
} from './indexeddb_schema';
45-
import { SimpleDb } from './simple_db';
4646
import { TargetData, TargetPurpose } from './target_data';
4747

4848
/** Serializer for values stored in the LocalStore. */
@@ -207,8 +207,6 @@ export class LocalSerializer {
207207
dbTarget.lastLimboFreeSnapshotVersion !== undefined
208208
? this.fromDbTimestamp(dbTarget.lastLimboFreeSnapshotVersion)
209209
: SnapshotVersion.MIN;
210-
// TODO(b/140573486): Convert to platform representation
211-
const resumeToken = dbTarget.resumeToken;
212210

213211
let target: Target;
214212
if (isDocumentQuery(dbTarget.query)) {
@@ -223,7 +221,7 @@ export class LocalSerializer {
223221
dbTarget.lastListenSequenceNumber,
224222
version,
225223
lastLimboFreeSnapshotVersion,
226-
resumeToken
224+
ByteString.fromBase64String(dbTarget.resumeToken)
227225
);
228226
}
229227

@@ -247,18 +245,9 @@ export class LocalSerializer {
247245
queryProto = this.remoteSerializer.toQueryTarget(targetData.target);
248246
}
249247

250-
let resumeToken: string;
251-
252-
if (targetData.resumeToken instanceof Uint8Array) {
253-
// TODO(b/78771403): Convert tokens to strings during deserialization
254-
assert(
255-
SimpleDb.isMockPersistence(),
256-
'Persisting non-string stream tokens is only supported with mock persistence .'
257-
);
258-
resumeToken = targetData.resumeToken.toString();
259-
} else {
260-
resumeToken = targetData.resumeToken;
261-
}
248+
// We can't store the resumeToken as a ByteString in IndexedDb, so we
249+
// convert it to a base64 string for storage.
250+
const resumeToken = targetData.resumeToken.toBase64();
262251

263252
// lastListenSequenceNumber is always 0 until we do real GC.
264253
return new DbTarget(

packages/firestore/src/local/local_store.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { User } from '../auth/user';
2020
import { Query } from '../core/query';
2121
import { SnapshotVersion } from '../core/snapshot_version';
2222
import { Target } from '../core/target';
23-
import { BatchId, ProtoByteString, TargetId } from '../core/types';
23+
import { BatchId, TargetId } from '../core/types';
2424
import {
2525
DocumentKeySet,
2626
documentKeySet,
@@ -62,6 +62,7 @@ import { RemoteDocumentCache } from './remote_document_cache';
6262
import { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';
6363
import { ClientId } from './shared_client_state';
6464
import { TargetData, TargetPurpose } from './target_data';
65+
import { ByteString } from '../util/proto_byte_string';
6566

6667
const LOG_TAG = 'LocalStore';
6768

@@ -464,7 +465,7 @@ export class LocalStore {
464465
}
465466

466467
/** Returns the last recorded stream token for the current user. */
467-
getLastStreamToken(): Promise<ProtoByteString> {
468+
getLastStreamToken(): Promise<ByteString> {
468469
return this.persistence.runTransaction(
469470
'Get last stream token',
470471
'readonly-idempotent',
@@ -479,7 +480,7 @@ export class LocalStore {
479480
* mutation batch. This is usually only useful after a stream handshake or in
480481
* response to an error that requires clearing the stream token.
481482
*/
482-
setLastStreamToken(streamToken: ProtoByteString): Promise<void> {
483+
setLastStreamToken(streamToken: ByteString): Promise<void> {
483484
return this.persistence.runTransaction(
484485
'Set last stream token',
485486
'readwrite-primary-idempotent',
@@ -551,7 +552,7 @@ export class LocalStore {
551552

552553
const resumeToken = change.resumeToken;
553554
// Update the resume token if the change includes one.
554-
if (resumeToken.length > 0) {
555+
if (resumeToken.approximateByteSize() > 0) {
555556
const newTargetData = oldTargetData
556557
.withResumeToken(resumeToken, remoteVersion)
557558
.withSequenceNumber(txn.currentSequenceNumber);
@@ -696,12 +697,12 @@ export class LocalStore {
696697
change: TargetChange
697698
): boolean {
698699
assert(
699-
newTargetData.resumeToken.length > 0,
700+
newTargetData.resumeToken.approximateByteSize() > 0,
700701
'Attempted to persist target data with no resume token'
701702
);
702703

703704
// Always persist target data if we don't already have a resume token.
704-
if (oldTargetData.resumeToken.length === 0) {
705+
if (oldTargetData.resumeToken.approximateByteSize() === 0) {
705706
return true;
706707
}
707708

packages/firestore/src/local/memory_mutation_queue.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717

1818
import { Timestamp } from '../api/timestamp';
1919
import { Query } from '../core/query';
20-
import { BatchId, ProtoByteString } from '../core/types';
20+
import { BatchId } from '../core/types';
2121
import { DocumentKeySet } from '../model/collections';
2222
import { DocumentKey } from '../model/document_key';
2323
import { Mutation } from '../model/mutation';
2424
import { MutationBatch, BATCHID_UNKNOWN } from '../model/mutation_batch';
25-
import { emptyByteString } from '../platform/platform';
2625
import { assert } from '../util/assert';
2726
import { primitiveComparator } from '../util/misc';
27+
import { ByteString } from '../util/proto_byte_string';
2828
import { SortedMap } from '../util/sorted_map';
2929
import { SortedSet } from '../util/sorted_set';
3030

@@ -48,7 +48,7 @@ export class MemoryMutationQueue implements MutationQueue {
4848
* responses the client has processed. Stream tokens are opaque checkpoint
4949
* markers whose only real value is their inclusion in the next request.
5050
*/
51-
private lastStreamToken: ProtoByteString = emptyByteString();
51+
private lastStreamToken: ByteString = ByteString.EMPTY_BYTE_STRING;
5252

5353
/** An ordered mapping between documents and the mutations batch IDs. */
5454
private batchesByDocumentKey = new SortedSet(DocReference.compareByKey);
@@ -65,7 +65,7 @@ export class MemoryMutationQueue implements MutationQueue {
6565
acknowledgeBatch(
6666
transaction: PersistenceTransaction,
6767
batch: MutationBatch,
68-
streamToken: ProtoByteString
68+
streamToken: ByteString
6969
): PersistencePromise<void> {
7070
const batchId = batch.batchId;
7171
const batchIndex = this.indexOfExistingBatchId(batchId, 'acknowledged');
@@ -90,13 +90,13 @@ export class MemoryMutationQueue implements MutationQueue {
9090

9191
getLastStreamToken(
9292
transaction: PersistenceTransaction
93-
): PersistencePromise<ProtoByteString> {
93+
): PersistencePromise<ByteString> {
9494
return PersistencePromise.resolve(this.lastStreamToken);
9595
}
9696

9797
setLastStreamToken(
9898
transaction: PersistenceTransaction,
99-
streamToken: ProtoByteString
99+
streamToken: ByteString
100100
): PersistencePromise<void> {
101101
this.lastStreamToken = streamToken;
102102
return PersistencePromise.resolve();

packages/firestore/src/local/mutation_queue.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717

1818
import { Timestamp } from '../api/timestamp';
1919
import { Query } from '../core/query';
20-
import { BatchId, ProtoByteString } from '../core/types';
20+
import { BatchId } from '../core/types';
2121
import { DocumentKeySet } from '../model/collections';
2222
import { DocumentKey } from '../model/document_key';
2323
import { Mutation } from '../model/mutation';
2424
import { MutationBatch } from '../model/mutation_batch';
25+
import { ByteString } from '../util/proto_byte_string';
2526
import { SortedMap } from '../util/sorted_map';
2627

2728
import { PersistenceTransaction } from './persistence';
@@ -38,18 +39,18 @@ export interface MutationQueue {
3839
acknowledgeBatch(
3940
transaction: PersistenceTransaction,
4041
batch: MutationBatch,
41-
streamToken: ProtoByteString
42+
streamToken: ByteString
4243
): PersistencePromise<void>;
4344

4445
/** Returns the current stream token for this mutation queue. */
4546
getLastStreamToken(
4647
transaction: PersistenceTransaction
47-
): PersistencePromise<ProtoByteString>;
48+
): PersistencePromise<ByteString>;
4849

4950
/** Sets the stream token for this mutation queue. */
5051
setLastStreamToken(
5152
transaction: PersistenceTransaction,
52-
streamToken: ProtoByteString
53+
streamToken: ByteString
5354
): PersistencePromise<void>;
5455

5556
/**

packages/firestore/src/local/target_data.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
import { SnapshotVersion } from '../core/snapshot_version';
1919
import { Target } from '../core/target';
20-
import { ListenSequenceNumber, ProtoByteString, TargetId } from '../core/types';
21-
import { emptyByteString } from '../platform/platform';
20+
import { ListenSequenceNumber, TargetId } from '../core/types';
21+
import { ByteString } from '../util/proto_byte_string';
2222

2323
/** An enumeration of the different purposes we have for targets. */
2424
export enum TargetPurpose {
@@ -66,7 +66,7 @@ export class TargetData {
6666
* matches the target. The resume token essentially identifies a point in
6767
* time from which the server should resume sending results.
6868
*/
69-
readonly resumeToken: ProtoByteString = emptyByteString()
69+
readonly resumeToken: ByteString = ByteString.EMPTY_BYTE_STRING
7070
) {}
7171

7272
/** Creates a new target data instance with an updated sequence number. */
@@ -87,7 +87,7 @@ export class TargetData {
8787
* snapshot version.
8888
*/
8989
withResumeToken(
90-
resumeToken: ProtoByteString,
90+
resumeToken: ByteString,
9191
snapshotVersion: SnapshotVersion
9292
): TargetData {
9393
return new TargetData(
@@ -128,7 +128,7 @@ export class TargetData {
128128
this.lastLimboFreeSnapshotVersion.isEqual(
129129
other.lastLimboFreeSnapshotVersion
130130
) &&
131-
this.resumeToken === other.resumeToken &&
131+
this.resumeToken.isEqual(other.resumeToken) &&
132132
this.target.isEqual(other.target)
133133
);
134134
}

0 commit comments

Comments
 (0)