@@ -320,8 +320,49 @@ export function bit_array_from_string(string) {
320
320
return toBitArray ( [ stringBits ( string ) ] ) ;
321
321
}
322
322
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
+
323
360
export 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
+ }
325
366
}
326
367
327
368
export function console_log ( term ) {
@@ -337,6 +378,10 @@ export function crash(message) {
337
378
}
338
379
339
380
export function bit_array_to_string ( bit_array ) {
381
+ if ( bit_array_bit_size ( bit_array ) % 8 !== 0 ) {
382
+ return new Error ( Nil ) ;
383
+ }
384
+
340
385
try {
341
386
const decoder = new TextDecoder ( "utf-8" , { fatal : true } ) ;
342
387
return new Ok ( decoder . decode ( bit_array . buffer ) ) ;
@@ -419,13 +464,19 @@ export function random_uniform() {
419
464
export function bit_array_slice ( bits , position , length ) {
420
465
const start = Math . min ( position , position + length ) ;
421
466
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
+
423
472
const byteOffset = bits . buffer . byteOffset + start ;
473
+
424
474
const buffer = new Uint8Array (
425
475
bits . buffer . buffer ,
426
476
byteOffset ,
427
477
Math . abs ( length ) ,
428
478
) ;
479
+
429
480
return new Ok ( new BitArray ( buffer ) ) ;
430
481
}
431
482
@@ -972,41 +1023,107 @@ export function base16_decode(string) {
972
1023
}
973
1024
974
1025
export 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 ;
976
1042
}
977
1043
978
1044
export 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 ) {
983
1051
const f = first . buffer [ i ] ;
984
1052
const s = second . buffer [ i ] ;
1053
+
985
1054
if ( f > s ) {
986
1055
return new Gt ( ) ;
987
- }
988
- if ( f < s ) {
1056
+ } else if ( f < s ) {
989
1057
return new Lt ( ) ;
990
1058
}
1059
+
1060
+ i ++ ;
1061
+ firstSize -= 8 ;
1062
+ secondSize -= 8 ;
991
1063
}
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 ) {
995
1066
return new Eq ( ) ;
996
1067
}
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 ( ) ;
998
1098
}
999
1099
1000
1100
export 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 ) ) {
1002
1104
return false ;
1003
1105
}
1004
1106
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 ++ ) {
1006
1112
if ( bits . buffer [ i ] !== prefix . buffer [ i ] ) {
1007
1113
return false ;
1008
1114
}
1009
1115
}
1010
1116
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
+
1011
1128
return true ;
1012
1129
}
0 commit comments