1515 * limitations under the License.
1616 */
1717
18+ import { isSafariOrWebkit } from '@firebase/util' ;
19+
20+ import { DbIndexEntry } from '../local/indexeddb_schema' ;
21+ import { DbIndexEntryKey , KeySafeBytes } from '../local/indexeddb_sentinels' ;
1822import { DocumentKey } from '../model/document_key' ;
1923
2024/** Represents an index entry saved by the SDK in persisted storage. */
2125export class IndexEntry {
2226 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
2731 ) { }
2832
2933 /**
3034 * Returns an IndexEntry entry that sorts immediately after the current
3135 * directional value.
3236 */
3337 successor ( ) : IndexEntry {
34- const currentLength = this . directionalValue . length ;
38+ const currentLength = this . _directionalValue . length ;
3539 const newLength =
36- currentLength === 0 || this . directionalValue [ currentLength - 1 ] === 255
40+ currentLength === 0 || this . _directionalValue [ currentLength - 1 ] === 255
3741 ? currentLength + 1
3842 : currentLength ;
3943
4044 const successor = new Uint8Array ( newLength ) ;
41- successor . set ( this . directionalValue , 0 ) ;
45+ successor . set ( this . _directionalValue , 0 ) ;
4246 if ( newLength !== currentLength ) {
43- successor . set ( [ 0 ] , this . directionalValue . length ) ;
47+ successor . set ( [ 0 ] , this . _directionalValue . length ) ;
4448 } else {
4549 ++ successor [ successor . length - 1 ] ;
4650 }
4751
4852 return new IndexEntry (
49- this . indexId ,
50- this . documentKey ,
51- this . arrayValue ,
53+ this . _indexId ,
54+ this . _documentKey ,
55+ this . _arrayValue ,
5256 successor
5357 ) ;
5458 }
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+ }
5592}
5693
5794export function indexEntryComparator (
5895 left : IndexEntry ,
5996 right : IndexEntry
6097) : number {
61- let cmp = left . indexId - right . indexId ;
98+ let cmp = left . _indexId - right . _indexId ;
6299 if ( cmp !== 0 ) {
63100 return cmp ;
64101 }
65102
66- cmp = compareByteArrays ( left . arrayValue , right . arrayValue ) ;
103+ cmp = compareByteArrays ( left . _arrayValue , right . _arrayValue ) ;
67104 if ( cmp !== 0 ) {
68105 return cmp ;
69106 }
70107
71- cmp = compareByteArrays ( left . directionalValue , right . directionalValue ) ;
108+ cmp = compareByteArrays ( left . _directionalValue , right . _directionalValue ) ;
72109 if ( cmp !== 0 ) {
73110 return cmp ;
74111 }
75112
76- return DocumentKey . comparator ( left . documentKey , right . documentKey ) ;
113+ return DocumentKey . comparator ( left . _documentKey , right . _documentKey ) ;
77114}
78115
79116export function compareByteArrays ( left : Uint8Array , right : Uint8Array ) : number {
@@ -85,3 +122,57 @@ export function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
85122 }
86123 return left . length - right . length ;
87124}
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