@@ -320,8 +320,49 @@ export function bit_array_from_string(string) {
320320 return toBitArray ( [ stringBits ( string ) ] ) ;
321321}
322322
323+ export function bit_array_bit_size ( bit_array ) {
324+ if ( bit_array . bitSize === undefined ) {
325+ return bit_array . length * 8 ;
326+ }
327+
328+ return bit_array . bitSize ;
329+ }
330+
331+ export function bit_array_pad_to_bytes ( bit_array ) {
332+ // If the bit array is byte aligned it can be returned unchanged
333+ const trailingBitsCount = bit_array_bit_size ( bit_array ) % 8 ;
334+ if ( trailingBitsCount === 0 ) {
335+ return bit_array ;
336+ }
337+
338+ const finalByte = bit_array . buffer [ bit_array . length - 1 ] ;
339+
340+ let correctFinalByte = finalByte ;
341+ correctFinalByte >>= 8 - trailingBitsCount ;
342+ correctFinalByte <<= 8 - trailingBitsCount ;
343+
344+ // If the unused bits in the final byte are already set to zero then the
345+ // existing buffer can be re-used, avoiding a copy
346+ if ( finalByte === correctFinalByte ) {
347+ return new BitArray ( bit_array . buffer ) ;
348+ }
349+
350+ // Copy the bit array into a new buffer and set the correct final byte
351+ const newBuffer = bit_array . buffer . slice ( ) ;
352+ newBuffer [ newBuffer . length - 1 ] = correctFinalByte ;
353+
354+ return new BitArray ( newBuffer ) ;
355+ }
356+
357+ const BIT_ARRAY_UNALIGNED_SUPPORTED =
358+ new BitArray ( new Uint8Array ( ) ) . bitSize !== undefined ;
359+
323360export function bit_array_concat ( bit_arrays ) {
324- return toBitArray ( bit_arrays . toArray ( ) . map ( ( b ) => b . buffer ) ) ;
361+ if ( BIT_ARRAY_UNALIGNED_SUPPORTED ) {
362+ return toBitArray ( bit_arrays . toArray ( ) ) ;
363+ } else {
364+ return toBitArray ( bit_arrays . toArray ( ) . map ( ( b ) => b . buffer ) ) ;
365+ }
325366}
326367
327368export function console_log ( term ) {
@@ -337,6 +378,10 @@ export function crash(message) {
337378}
338379
339380export function bit_array_to_string ( bit_array ) {
381+ if ( bit_array_bit_size ( bit_array ) % 8 !== 0 ) {
382+ return new Error ( Nil ) ;
383+ }
384+
340385 try {
341386 const decoder = new TextDecoder ( "utf-8" , { fatal : true } ) ;
342387 return new Ok ( decoder . decode ( bit_array . buffer ) ) ;
@@ -419,13 +464,19 @@ export function random_uniform() {
419464export function bit_array_slice ( bits , position , length ) {
420465 const start = Math . min ( position , position + length ) ;
421466 const end = Math . max ( position , position + length ) ;
422- if ( start < 0 || end > bits . length ) return new Error ( Nil ) ;
467+
468+ if ( start < 0 || end * 8 > bit_array_bit_size ( bits ) ) {
469+ return new Error ( Nil ) ;
470+ }
471+
423472 const byteOffset = bits . buffer . byteOffset + start ;
473+
424474 const buffer = new Uint8Array (
425475 bits . buffer . buffer ,
426476 byteOffset ,
427477 Math . abs ( length ) ,
428478 ) ;
479+
429480 return new Ok ( new BitArray ( buffer ) ) ;
430481}
431482
@@ -972,41 +1023,107 @@ export function base16_decode(string) {
9721023}
9731024
9741025export function bit_array_inspect ( bits , acc ) {
975- return `${ acc } ${ [ ...bits . buffer ] . join ( ", " ) } ` ;
1026+ const bitSize = bit_array_bit_size ( bits ) ;
1027+
1028+ if ( bitSize % 8 === 0 ) {
1029+ return `${ acc } ${ [ ...bits . buffer ] . join ( ", " ) } ` ;
1030+ }
1031+
1032+ for ( let i = 0 ; i < bits . length - 1 ; i ++ ) {
1033+ acc += bits . buffer [ i ] . toString ( ) ;
1034+ acc += ", " ;
1035+ }
1036+
1037+ const trailingBitsCount = bitSize % 8 ;
1038+ acc += bits . buffer [ bits . length - 1 ] >> ( 8 - trailingBitsCount ) ;
1039+ acc += `:size(${ trailingBitsCount } )` ;
1040+
1041+ return acc ;
9761042}
9771043
9781044export function bit_array_compare ( first , second ) {
979- for ( let i = 0 ; i < first . length ; i ++ ) {
980- if ( i >= second . length ) {
981- return new Gt ( ) ; // first has more items
982- }
1045+ let i = 0 ;
1046+
1047+ let firstSize = bit_array_bit_size ( first ) ;
1048+ let secondSize = bit_array_bit_size ( second ) ;
1049+
1050+ while ( firstSize >= 8 && secondSize >= 8 ) {
9831051 const f = first . buffer [ i ] ;
9841052 const s = second . buffer [ i ] ;
1053+
9851054 if ( f > s ) {
9861055 return new Gt ( ) ;
987- }
988- if ( f < s ) {
1056+ } else if ( f < s ) {
9891057 return new Lt ( ) ;
9901058 }
1059+
1060+ i ++ ;
1061+ firstSize -= 8 ;
1062+ secondSize -= 8 ;
9911063 }
992- // This means that either first did not have any items
993- // or all items in first were equal to second.
994- if ( first . length === second . length ) {
1064+
1065+ if ( firstSize === 0 && secondSize === 0 ) {
9951066 return new Eq ( ) ;
9961067 }
997- return new Lt ( ) ; // second has more items
1068+
1069+ // First has more items, example: "AB" > "A":
1070+ if ( secondSize === 0 ) {
1071+ return new Gt ( ) ;
1072+ }
1073+
1074+ // Second has more items, example: "A" < "AB":
1075+ if ( firstSize === 0 ) {
1076+ return new Lt ( ) ;
1077+ }
1078+
1079+ // This happens when there are unaligned bit arrays
1080+
1081+ const f = first . buffer [ i ] >> ( 8 - firstSize ) ;
1082+ const s = second . buffer [ i ] >> ( 8 - secondSize ) ;
1083+
1084+ if ( f > s ) {
1085+ return new Gt ( ) ;
1086+ }
1087+ if ( f < s ) {
1088+ return new Lt ( ) ;
1089+ }
1090+ if ( firstSize > secondSize ) {
1091+ return new Gt ( ) ;
1092+ }
1093+ if ( firstSize < secondSize ) {
1094+ return new Lt ( ) ;
1095+ }
1096+
1097+ return new Eq ( ) ;
9981098}
9991099
10001100export function bit_array_starts_with ( bits , prefix ) {
1001- if ( prefix . length > bits . length ) {
1101+ const prefixSize = bit_array_bit_size ( prefix ) ;
1102+
1103+ if ( prefixSize > bit_array_bit_size ( bits ) ) {
10021104 return false ;
10031105 }
10041106
1005- for ( let i = 0 ; i < prefix . length ; i ++ ) {
1107+ const isPrefixAligned = prefixSize % 8 === 0 ;
1108+
1109+ // Check any whole bytes
1110+ const byteCount = isPrefixAligned ? prefix . length : prefix . length - 1 ;
1111+ for ( let i = 0 ; i < byteCount ; i ++ ) {
10061112 if ( bits . buffer [ i ] !== prefix . buffer [ i ] ) {
10071113 return false ;
10081114 }
10091115 }
10101116
1117+ // Check any trailing bits at the end of the prefix
1118+ if ( ! isPrefixAligned ) {
1119+ const unusedBitsCount = 8 - ( prefixSize % 8 ) ;
1120+ if (
1121+ bits . buffer [ prefix . length - 1 ] >> unusedBitsCount !==
1122+ prefix . buffer [ prefix . length - 1 ] >> unusedBitsCount
1123+ ) {
1124+ return false ;
1125+ }
1126+ }
1127+
10111128 return true ;
10121129}
0 commit comments