Skip to content

Commit 39b6f0c

Browse files
committed
resolve comments
1 parent 513db9d commit 39b6f0c

File tree

7 files changed

+126
-83
lines changed

7 files changed

+126
-83
lines changed

packages/firestore/src/remote/bloom_filter.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ function get64BitUints(Bytes: Uint8Array): [Integer, Integer] {
4242
}
4343

4444
export class BloomFilter {
45-
readonly size: number;
46-
private readonly sizeInInteger: Integer;
45+
readonly bitCount: number;
46+
private readonly bitCountInInteger: Integer;
4747

4848
constructor(
49-
private readonly _bitmap: Uint8Array,
49+
readonly bitmap: Uint8Array,
5050
readonly padding: number,
5151
readonly hashCount: number
5252
) {
@@ -58,12 +58,12 @@ export class BloomFilter {
5858
throw new BloomFilterError(`Invalid hash count: ${hashCount}`);
5959
}
6060

61-
if (_bitmap.length > 0 && this.hashCount === 0) {
61+
if (bitmap.length > 0 && this.hashCount === 0) {
6262
// Only empty bloom filter can have 0 hash count.
6363
throw new BloomFilterError(`Invalid hash count: ${hashCount}`);
6464
}
6565

66-
if (_bitmap.length === 0) {
66+
if (bitmap.length === 0) {
6767
// Empty bloom filter should have 0 padding.
6868
if (padding !== 0) {
6969
throw new BloomFilterError(
@@ -72,13 +72,9 @@ export class BloomFilter {
7272
}
7373
}
7474

75-
this.size = _bitmap.length * 8 - padding;
76-
// Set the size in Integer to avoid repeated calculation in mightContain().
77-
this.sizeInInteger = Integer.fromNumber(this.size);
78-
}
79-
80-
getBitMap(): Uint8Array {
81-
return new Uint8Array(this._bitmap);
75+
this.bitCount = bitmap.length * 8 - padding;
76+
// Set the bit count in Integer to avoid repetition in mightContain().
77+
this.bitCountInInteger = Integer.fromNumber(this.bitCount);
8278
}
8379

8480
// Calculate the ith hash value based on the hashed 64bit integers,
@@ -90,25 +86,25 @@ export class BloomFilter {
9086
if (hashValue.compare(MAX_64_BIT_UNSIGNED_INTEGER) === 1) {
9187
hashValue = new Integer([hashValue.getBits(0), hashValue.getBits(1)], 0);
9288
}
93-
return hashValue.modulo(this.sizeInInteger).toNumber();
89+
return hashValue.modulo(this.bitCountInInteger).toNumber();
9490
}
9591

9692
// Return whether the bit on the given index in the bitmap is set to 1.
9793
private isBitSet(index: number): boolean {
9894
// To retrieve bit n, calculate: (bitmap[n / 8] & (0x01 << (n % 8))).
99-
const byte = this._bitmap[Math.floor(index / 8)];
95+
const byte = this.bitmap[Math.floor(index / 8)];
10096
const offset = index % 8;
10197
return (byte & (0x01 << offset)) !== 0;
10298
}
10399

104100
mightContain(value: string): boolean {
105-
// Empty bitmap and empty value should always return false on membership
106-
// check.
107-
if (this.size === 0 || value === '') {
101+
// Empty bitmap should always return false on membership check.
102+
if (this.bitCount === 0) {
108103
return false;
109104
}
110-
105+
console.log("value to Hash:",value)
111106
const md5HashedValue = getMd5HashValue(value);
107+
console.log("Hashed value: ", md5HashedValue)
112108
const [hash1, hash2] = get64BitUints(md5HashedValue);
113109
for (let i = 0; i < this.hashCount; i++) {
114110
const index = this.getBitIndex(hash1, hash2, i);
@@ -133,7 +129,11 @@ export class BloomFilter {
133129
}
134130

135131
private insert(value: string): void {
136-
if (this.size === 0 || value === '') {
132+
if (value === '') {
133+
throw new Error('Cannot insert empty string to a bloom filter.');
134+
}
135+
136+
if (this.bitCount === 0) {
137137
return;
138138
}
139139

@@ -148,7 +148,7 @@ export class BloomFilter {
148148
private setBit(index: number): void {
149149
const indexOfByte = Math.floor(index / 8);
150150
const offset = index % 8;
151-
this._bitmap[indexOfByte] |= 0x01 << offset;
151+
this.bitmap[indexOfByte] |= 0x01 << offset;
152152
}
153153
}
154154

packages/firestore/src/remote/watch_change.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,8 @@ export class WatchChangeAggregator {
411411
}
412412
} else {
413413
const currentSize = this.getCurrentDocumentCountForTarget(targetId);
414-
// Existence filter mismatch. Mark the documents as being in limbo, and raise a
415-
// snapshot with `isFromCache:true`.
414+
// Existence filter mismatch. Mark the documents as being in limbo, and
415+
// raise a snapshot with `isFromCache:true`.
416416
if (currentSize !== expectedCount) {
417417
// Apply bloom filter to identify and mark removed documents.
418418
const bloomFilterApplied = this.applyBloomFilter(
@@ -421,8 +421,8 @@ export class WatchChangeAggregator {
421421
currentSize
422422
);
423423
if (!bloomFilterApplied) {
424-
// If bloom filter application fails, we reset the mapping and trigger
425-
// re-run of the query.
424+
// If bloom filter application fails, we reset the mapping and
425+
// trigger re-run of the query.
426426
this.resetTarget(targetId);
427427
this.pendingTargetResets = this.pendingTargetResets.add(targetId);
428428
}
@@ -449,22 +449,30 @@ export class WatchChangeAggregator {
449449
hashCount = 0
450450
} = unchangedNames;
451451

452+
if (typeof bitmap === 'string') {
453+
const isValidBitmap = this.isValidBase64String(bitmap);
454+
if (!isValidBitmap) {
455+
logWarn('Invalid base64 string. Applying bloom filter failed.');
456+
return false;
457+
}
458+
}
459+
460+
const normalizedBitmap = normalizeByteString(bitmap).toUint8Array();
461+
452462
let bloomFilter;
453463
try {
454-
// normalizeByteString throws error if the bitmap includes invalid 64base characters
455-
const normalizedBitmap = normalizeByteString(bitmap).toUint8Array();
456464
// BloomFilter throws error if the inputs are invalid
457465
bloomFilter = new BloomFilter(normalizedBitmap, padding, hashCount);
458466
} catch (err) {
459467
if (err instanceof BloomFilterError) {
460-
logWarn('BloomFilter', err);
468+
logWarn('BloomFilter error: ', err);
461469
} else {
462470
logWarn('Applying bloom filter failed: ', err);
463471
}
464472
return false;
465473
}
466474

467-
if (bloomFilter.size === 0) {
475+
if (bloomFilter.bitCount === 0) {
468476
return false;
469477
}
470478

@@ -476,9 +484,16 @@ export class WatchChangeAggregator {
476484
return currentCount - removedDocumentCount === expectedCount;
477485
}
478486

487+
private isValidBase64String(value: string): boolean {
488+
const regExp = new RegExp(
489+
'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
490+
);
491+
return regExp.test(value);
492+
}
493+
479494
/**
480-
* Filter out removed documents based on bloom filter membership result and return number
481-
* of documents removed.
495+
* Filter out removed documents based on bloom filter membership result and
496+
* return number of documents removed.
482497
*/
483498
private filterRemovedDocuments(
484499
bloomFilter: BloomFilter,

packages/firestore/test/unit/remote/bloom_filter.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import * as TEST_DATA from './bloom_filter_golden_test_data';
2424
describe('BloomFilter', () => {
2525
it('can instantiate an empty bloom filter', () => {
2626
const bloomFilter = new BloomFilter(new Uint8Array(0), 0, 0);
27-
expect(bloomFilter.size).to.equal(0);
27+
expect(bloomFilter.bitCount).to.equal(0);
2828
});
2929

3030
it('should throw error if empty bloom filter inputs are invalid', () => {
@@ -46,14 +46,14 @@ describe('BloomFilter', () => {
4646
const bloomFilter6 = new BloomFilter(new Uint8Array(1), 6, 1);
4747
const bloomFilter7 = new BloomFilter(new Uint8Array(1), 7, 1);
4848

49-
expect(bloomFilter0.size).to.equal(8);
50-
expect(bloomFilter1.size).to.equal(7);
51-
expect(bloomFilter2.size).to.equal(6);
52-
expect(bloomFilter3.size).to.equal(5);
53-
expect(bloomFilter4.size).to.equal(4);
54-
expect(bloomFilter5.size).to.equal(3);
55-
expect(bloomFilter6.size).to.equal(2);
56-
expect(bloomFilter7.size).to.equal(1);
49+
expect(bloomFilter0.bitCount).to.equal(8);
50+
expect(bloomFilter1.bitCount).to.equal(7);
51+
expect(bloomFilter2.bitCount).to.equal(6);
52+
expect(bloomFilter3.bitCount).to.equal(5);
53+
expect(bloomFilter4.bitCount).to.equal(4);
54+
expect(bloomFilter5.bitCount).to.equal(3);
55+
expect(bloomFilter6.bitCount).to.equal(2);
56+
expect(bloomFilter7.bitCount).to.equal(1);
5757
});
5858

5959
it('should throw error if padding is invalid', () => {
@@ -90,7 +90,7 @@ describe('BloomFilter', () => {
9090
expect(bloomFilter.mightContain('def')).to.be.false;
9191
});
9292

93-
it('mightContain should always return false for empty string', () => {
93+
it.only('mightContain should always return false for empty string', () => {
9494
const emptyBloomFilter = new BloomFilter(new Uint8Array(0), 0, 0);
9595
const nonEmptyBloomFilter = new BloomFilter(
9696
new Uint8Array([255, 255, 255]),

0 commit comments

Comments
 (0)