@@ -988,6 +988,12 @@ class VlqHexDecoder {
988988}
989989class RoaringBitmap {
990990 constructor ( str ) {
991+ // https://github.com/RoaringBitmap/RoaringFormatSpec
992+ //
993+ // Roaring bitmaps are used for flags that can be kept in their
994+ // compressed form, even when loaded into memory. This decoder
995+ // turns the containers into objects, but uses byte array
996+ // slices of the original format for the data payload.
991997 const strdecoded = atob ( str ) ;
992998 const u8array = new Uint8Array ( strdecoded . length ) ;
993999 for ( let j = 0 ; j < strdecoded . length ; ++ j ) {
@@ -1053,9 +1059,24 @@ class RoaringBitmap {
10531059 contains ( keyvalue ) {
10541060 const key = keyvalue >> 16 ;
10551061 const value = keyvalue & 0xFFFF ;
1056- for ( let i = 0 ; i < this . keys . length ; ++ i ) {
1057- if ( this . keys [ i ] === key ) {
1058- return this . containers [ i ] . contains ( value ) ;
1062+ // Binary search algorithm copied from
1063+ // https://en.wikipedia.org/wiki/Binary_search#Procedure
1064+ //
1065+ // Format is required by specification to be sorted.
1066+ // Because keys are 16 bits and unique, length can't be
1067+ // bigger than 2**16, and because we have 32 bits of safe int,
1068+ // left + right can't overflow.
1069+ let left = 0 ;
1070+ let right = this . keys . length - 1 ;
1071+ while ( left <= right ) {
1072+ const mid = Math . floor ( ( left + right ) / 2 ) ;
1073+ const x = this . keys [ mid ] ;
1074+ if ( x < key ) {
1075+ left = mid + 1 ;
1076+ } else if ( x > key ) {
1077+ right = mid - 1 ;
1078+ } else {
1079+ return this . containers [ mid ] . contains ( value ) ;
10591080 }
10601081 }
10611082 return false ;
@@ -1068,11 +1089,23 @@ class RoaringBitmapRun {
10681089 this . array = array ;
10691090 }
10701091 contains ( value ) {
1071- const l = this . runcount * 4 ;
1072- for ( let i = 0 ; i < l ; i += 4 ) {
1092+ // Binary search algorithm copied from
1093+ // https://en.wikipedia.org/wiki/Binary_search#Procedure
1094+ //
1095+ // Since runcount is stored as 16 bits, left + right
1096+ // can't overflow.
1097+ let left = 0 ;
1098+ let right = this . runcount - 1 ;
1099+ while ( left <= right ) {
1100+ const mid = Math . floor ( ( left + right ) / 2 ) ;
1101+ const i = mid * 4 ;
10731102 const start = this . array [ i ] | ( this . array [ i + 1 ] << 8 ) ;
10741103 const lenm1 = this . array [ i + 2 ] | ( this . array [ i + 3 ] << 8 ) ;
1075- if ( value >= start && value <= ( start + lenm1 ) ) {
1104+ if ( ( start + lenm1 ) < value ) {
1105+ left = mid + 1 ;
1106+ } else if ( start > value ) {
1107+ right = mid - 1 ;
1108+ } else {
10761109 return true ;
10771110 }
10781111 }
@@ -1085,10 +1118,22 @@ class RoaringBitmapArray {
10851118 this . array = array ;
10861119 }
10871120 contains ( value ) {
1088- const l = this . cardinality * 2 ;
1089- for ( let i = 0 ; i < l ; i += 2 ) {
1090- const start = this . array [ i ] | ( this . array [ i + 1 ] << 8 ) ;
1091- if ( value === start ) {
1121+ // Binary search algorithm copied from
1122+ // https://en.wikipedia.org/wiki/Binary_search#Procedure
1123+ //
1124+ // Since cardinality can't be higher than 4096, left + right
1125+ // cannot overflow.
1126+ let left = 0 ;
1127+ let right = this . cardinality - 1 ;
1128+ while ( left <= right ) {
1129+ const mid = Math . floor ( ( left + right ) / 2 ) ;
1130+ const i = mid * 2 ;
1131+ const x = this . array [ i ] | ( this . array [ i + 1 ] << 8 ) ;
1132+ if ( x < value ) {
1133+ left = mid + 1 ;
1134+ } else if ( x > value ) {
1135+ right = mid - 1 ;
1136+ } else {
10921137 return true ;
10931138 }
10941139 }
0 commit comments