15
15
* limitations under the License.
16
16
*/
17
17
18
+ import { isSafariOrWebkit } from '@firebase/util' ;
19
+
20
+ import { DbIndexEntry } from '../local/indexeddb_schema' ;
21
+ import { DbIndexEntryKey , KeySafeBytes } from '../local/indexeddb_sentinels' ;
18
22
import { DocumentKey } from '../model/document_key' ;
19
23
20
24
/** Represents an index entry saved by the SDK in persisted storage. */
21
25
export class IndexEntry {
22
26
constructor (
23
- readonly indexId : number ,
24
- readonly documentKey : DocumentKey ,
25
- readonly arrayValue : Uint8Array ,
26
- readonly directionalValue : Uint8Array
27
+ readonly _indexId : number ,
28
+ readonly _documentKey : DocumentKey ,
29
+ readonly _arrayValue : Uint8Array ,
30
+ readonly _directionalValue : Uint8Array
27
31
) { }
28
32
29
33
/**
30
34
* Returns an IndexEntry entry that sorts immediately after the current
31
35
* directional value.
32
36
*/
33
37
successor ( ) : IndexEntry {
34
- const currentLength = this . directionalValue . length ;
38
+ const currentLength = this . _directionalValue . length ;
35
39
const newLength =
36
- currentLength === 0 || this . directionalValue [ currentLength - 1 ] === 255
40
+ currentLength === 0 || this . _directionalValue [ currentLength - 1 ] === 255
37
41
? currentLength + 1
38
42
: currentLength ;
39
43
40
44
const successor = new Uint8Array ( newLength ) ;
41
- successor . set ( this . directionalValue , 0 ) ;
45
+ successor . set ( this . _directionalValue , 0 ) ;
42
46
if ( newLength !== currentLength ) {
43
- successor . set ( [ 0 ] , this . directionalValue . length ) ;
47
+ successor . set ( [ 0 ] , this . _directionalValue . length ) ;
44
48
} else {
45
49
++ successor [ successor . length - 1 ] ;
46
50
}
47
51
48
52
return new IndexEntry (
49
- this . indexId ,
50
- this . documentKey ,
51
- this . arrayValue ,
53
+ this . _indexId ,
54
+ this . _documentKey ,
55
+ this . _arrayValue ,
52
56
successor
53
57
) ;
54
58
}
59
+
60
+ // Create a representation of the Index Entry as a DbIndexEntry
61
+ dbIndexEntry (
62
+ uid : string ,
63
+ orderedDocumentKey : Uint8Array ,
64
+ documentKey : DocumentKey
65
+ ) : DbIndexEntry {
66
+ return {
67
+ indexId : this . _indexId ,
68
+ uid,
69
+ arrayValue : encodeKeySafeBytes ( this . _arrayValue ) ,
70
+ directionalValue : encodeKeySafeBytes ( this . _directionalValue ) ,
71
+ orderedDocumentKey : encodeKeySafeBytes ( orderedDocumentKey ) ,
72
+ documentKey : documentKey . path . toArray ( )
73
+ } ;
74
+ }
75
+
76
+ // Create a representation of the Index Entry as a DbIndexEntryKey
77
+ dbIndexEntryKey (
78
+ uid : string ,
79
+ orderedDocumentKey : Uint8Array ,
80
+ documentKey : DocumentKey
81
+ ) : DbIndexEntryKey {
82
+ const entry = this . dbIndexEntry ( uid , orderedDocumentKey , documentKey ) ;
83
+ return [
84
+ entry . indexId ,
85
+ entry . uid ,
86
+ entry . arrayValue ,
87
+ entry . directionalValue ,
88
+ entry . orderedDocumentKey ,
89
+ entry . documentKey
90
+ ] ;
91
+ }
55
92
}
56
93
57
94
export function indexEntryComparator (
58
95
left : IndexEntry ,
59
96
right : IndexEntry
60
97
) : number {
61
- let cmp = left . indexId - right . indexId ;
98
+ let cmp = left . _indexId - right . _indexId ;
62
99
if ( cmp !== 0 ) {
63
100
return cmp ;
64
101
}
65
102
66
- cmp = compareByteArrays ( left . arrayValue , right . arrayValue ) ;
103
+ cmp = compareByteArrays ( left . _arrayValue , right . _arrayValue ) ;
67
104
if ( cmp !== 0 ) {
68
105
return cmp ;
69
106
}
70
107
71
- cmp = compareByteArrays ( left . directionalValue , right . directionalValue ) ;
108
+ cmp = compareByteArrays ( left . _directionalValue , right . _directionalValue ) ;
72
109
if ( cmp !== 0 ) {
73
110
return cmp ;
74
111
}
75
112
76
- return DocumentKey . comparator ( left . documentKey , right . documentKey ) ;
113
+ return DocumentKey . comparator ( left . _documentKey , right . _documentKey ) ;
77
114
}
78
115
79
116
export function compareByteArrays ( left : Uint8Array , right : Uint8Array ) : number {
@@ -85,3 +122,57 @@ export function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
85
122
}
86
123
return left . length - right . length ;
87
124
}
125
+
126
+ /**
127
+ * Workaround for WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=292721
128
+ * Create a key safe representation of Uint8Array values.
129
+ * If the browser is detected as Safari or WebKit, then
130
+ * the input array will be converted to "sortable byte string".
131
+ * Otherwise, the input array will be returned in its original type.
132
+ */
133
+ export function encodeKeySafeBytes ( array : Uint8Array ) : KeySafeBytes {
134
+ if ( isSafariOrWebkit ( ) ) {
135
+ return encodeUint8ArrayToSortableString ( array ) ;
136
+ }
137
+ return array ;
138
+ }
139
+
140
+ /**
141
+ * Reverts the key safe representation of Uint8Array (created by
142
+ * encodeKeySafeBytes) to a normal Uint8Array.
143
+ */
144
+ export function decodeKeySafeBytes ( input : KeySafeBytes ) : Uint8Array {
145
+ if ( typeof input !== 'string' ) {
146
+ return input ;
147
+ }
148
+ return decodeSortableStringToUint8Array ( input ) ;
149
+ }
150
+
151
+ /**
152
+ * Encodes a Uint8Array into a "sortable byte string".
153
+ * A "sortable byte string" sorts in the same order as the Uint8Array.
154
+ * This works because JS string comparison sorts strings based on code points.
155
+ */
156
+ function encodeUint8ArrayToSortableString ( array : Uint8Array ) : string {
157
+ let byteString = '' ;
158
+ for ( let i = 0 ; i < array . length ; i ++ ) {
159
+ byteString += String . fromCharCode ( array [ i ] ) ;
160
+ }
161
+
162
+ return byteString ;
163
+ }
164
+
165
+ /**
166
+ * Decodes a "sortable byte string" back into a Uint8Array.
167
+ * A "sortable byte string" is assumed to be created where each character's
168
+ * Unicode code point directly corresponds to a single byte value (0-255).
169
+ */
170
+ function decodeSortableStringToUint8Array ( byteString : string ) : Uint8Array {
171
+ const uint8array = new Uint8Array ( byteString . length ) ;
172
+
173
+ for ( let i = 0 ; i < byteString . length ; i ++ ) {
174
+ uint8array [ i ] = byteString . charCodeAt ( i ) ;
175
+ }
176
+
177
+ return uint8array ;
178
+ }
0 commit comments