7
7
UtfCodepoint ,
8
8
stringBits ,
9
9
toBitArray ,
10
+ bitArraySlice ,
10
11
NonEmpty ,
11
12
CustomType ,
12
13
} from "./gleam.mjs" ;
@@ -316,8 +317,49 @@ export function bit_array_from_string(string) {
316
317
return toBitArray ( [ stringBits ( string ) ] ) ;
317
318
}
318
319
320
+ export function bit_array_bit_size ( bit_array ) {
321
+ return bit_array . bitSize ;
322
+ }
323
+
324
+ export function bit_array_byte_size ( bit_array ) {
325
+ return bit_array . byteSize ;
326
+ }
327
+
328
+ export function bit_array_pad_to_bytes ( bit_array ) {
329
+ const trailingBitsCount = bit_array . bitSize % 8 ;
330
+
331
+ // If the bit array is a whole number of bytes it can be returned unchanged
332
+ if ( trailingBitsCount === 0 ) {
333
+ return bit_array ;
334
+ }
335
+
336
+ const finalByte = bit_array . byteAt ( bit_array . byteSize - 1 ) ;
337
+
338
+ const unusedBitsCount = 8 - trailingBitsCount ;
339
+ const correctFinalByte = ( finalByte >> unusedBitsCount ) << unusedBitsCount ;
340
+
341
+ // If the unused bits in the final byte are already set to zero then the
342
+ // existing buffer can be re-used, avoiding a copy
343
+ if ( finalByte === correctFinalByte ) {
344
+ return new BitArray (
345
+ bit_array . rawBuffer ,
346
+ bit_array . byteSize * 8 ,
347
+ bit_array . bitOffset ,
348
+ ) ;
349
+ }
350
+
351
+ // Copy the bit array into a new aligned buffer and set the correct final byte
352
+ const buffer = new Uint8Array ( bit_array . byteSize ) ;
353
+ for ( let i = 0 ; i < buffer . length - 1 ; i ++ ) {
354
+ buffer [ i ] = bit_array . byteAt ( i ) ;
355
+ }
356
+ buffer [ buffer . length - 1 ] = correctFinalByte ;
357
+
358
+ return new BitArray ( buffer ) ;
359
+ }
360
+
319
361
export function bit_array_concat ( bit_arrays ) {
320
- return toBitArray ( bit_arrays . toArray ( ) . map ( ( b ) => b . buffer ) ) ;
362
+ return toBitArray ( bit_arrays . toArray ( ) ) ;
321
363
}
322
364
323
365
export function console_log ( term ) {
@@ -333,9 +375,25 @@ export function crash(message) {
333
375
}
334
376
335
377
export function bit_array_to_string ( bit_array ) {
378
+ // If the bit array isn't a whole number of bytes then return an error
379
+ if ( bit_array . bitSize % 8 !== 0 ) {
380
+ return new Error ( Nil ) ;
381
+ }
382
+
336
383
try {
337
384
const decoder = new TextDecoder ( "utf-8" , { fatal : true } ) ;
338
- return new Ok ( decoder . decode ( bit_array . buffer ) ) ;
385
+
386
+ if ( bit_array . bitOffset === 0 ) {
387
+ return new Ok ( decoder . decode ( bit_array . rawBuffer ) ) ;
388
+ } else {
389
+ // The input data isn't aligned, so copy it into a new aligned buffer so
390
+ // that TextDecoder can be used
391
+ const buffer = new Uint8Array ( bit_array . byteSize ) ;
392
+ for ( let i = 0 ; i < buffer . length ; i ++ ) {
393
+ buffer [ i ] = bit_array . byteAt ( i ) ;
394
+ }
395
+ return new Ok ( decoder . decode ( buffer ) ) ;
396
+ }
339
397
} catch {
340
398
return new Error ( Nil ) ;
341
399
}
@@ -413,16 +471,14 @@ export function random_uniform() {
413
471
}
414
472
415
473
export function bit_array_slice ( bits , position , length ) {
416
- const start = Math . min ( position , position + length ) ;
417
- const end = Math . max ( position , position + length ) ;
418
- if ( start < 0 || end > bits . length ) return new Error ( Nil ) ;
419
- const byteOffset = bits . buffer . byteOffset + start ;
420
- const buffer = new Uint8Array (
421
- bits . buffer . buffer ,
422
- byteOffset ,
423
- Math . abs ( length ) ,
424
- ) ;
425
- return new Ok ( new BitArray ( buffer ) ) ;
474
+ let start = Math . min ( position , position + length ) ;
475
+ let end = Math . max ( position , position + length ) ;
476
+
477
+ if ( start < 0 || end * 8 > bits . bitSize ) {
478
+ return new Error ( Nil ) ;
479
+ }
480
+
481
+ return new Ok ( bitArraySlice ( bits , start * 8 , end * 8 ) ) ;
426
482
}
427
483
428
484
export function codepoint ( int ) {
@@ -522,16 +578,20 @@ let b64TextDecoder;
522
578
export function encode64 ( bit_array , padding ) {
523
579
b64TextDecoder ??= new TextDecoder ( ) ;
524
580
525
- const bytes = bit_array . buffer ;
581
+ bit_array = bit_array_pad_to_bytes ( bit_array ) ;
526
582
527
- const m = bytes . length ;
583
+ const m = bit_array . byteSize ;
528
584
const k = m % 3 ;
529
585
const n = Math . floor ( m / 3 ) * 4 + ( k && k + 1 ) ;
530
586
const N = Math . ceil ( m / 3 ) * 4 ;
531
587
const encoded = new Uint8Array ( N ) ;
532
588
533
589
for ( let i = 0 , j = 0 ; j < m ; i += 4 , j += 3 ) {
534
- const y = ( bytes [ j ] << 16 ) + ( bytes [ j + 1 ] << 8 ) + ( bytes [ j + 2 ] | 0 ) ;
590
+ const y =
591
+ ( bit_array . byteAt ( j ) << 16 ) +
592
+ ( bit_array . byteAt ( j + 1 ) << 8 ) +
593
+ ( bit_array . byteAt ( j + 2 ) | 0 ) ;
594
+
535
595
encoded [ i ] = b64EncodeLookup [ y >> 18 ] ;
536
596
encoded [ i + 1 ] = b64EncodeLookup [ ( y >> 12 ) & 0x3f ] ;
537
597
encoded [ i + 2 ] = b64EncodeLookup [ ( y >> 6 ) & 0x3f ] ;
@@ -804,7 +864,7 @@ export function inspect(v) {
804
864
if ( Array . isArray ( v ) ) return `#(${ v . map ( inspect ) . join ( ", " ) } )` ;
805
865
if ( v instanceof List ) return inspectList ( v ) ;
806
866
if ( v instanceof UtfCodepoint ) return inspectUtfCodepoint ( v ) ;
807
- if ( v instanceof BitArray ) return inspectBitArray ( v ) ;
867
+ if ( v instanceof BitArray ) return `<< ${ bit_array_inspect ( v , "" ) } >>` ;
808
868
if ( v instanceof CustomType ) return inspectCustomType ( v ) ;
809
869
if ( v instanceof Dict ) return inspectDict ( v ) ;
810
870
if ( v instanceof Set ) return `//js(Set(${ [ ...v ] . map ( inspect ) . join ( ", " ) } ))` ;
@@ -895,19 +955,26 @@ export function inspectList(list) {
895
955
return `[${ list . toArray ( ) . map ( inspect ) . join ( ", " ) } ]` ;
896
956
}
897
957
898
- export function inspectBitArray ( bits ) {
899
- return `<<${ Array . from ( bits . buffer ) . join ( ", " ) } >>` ;
900
- }
901
-
902
958
export function inspectUtfCodepoint ( codepoint ) {
903
959
return `//utfcodepoint(${ String . fromCodePoint ( codepoint . value ) } )` ;
904
960
}
905
961
906
962
export function base16_encode ( bit_array ) {
963
+ const trailingBitsCount = bit_array . bitSize % 8 ;
964
+
907
965
let result = "" ;
908
- for ( const byte of bit_array . buffer ) {
966
+
967
+ for ( let i = 0 ; i < bit_array . byteSize ; i ++ ) {
968
+ let byte = bit_array . byteAt ( i ) ;
969
+
970
+ if ( i === bit_array . byteSize - 1 && trailingBitsCount !== 0 ) {
971
+ const unusedBitsCount = 8 - trailingBitsCount ;
972
+ byte = ( byte >> unusedBitsCount ) << unusedBitsCount ;
973
+ }
974
+
909
975
result += byte . toString ( 16 ) . padStart ( 2 , "0" ) . toUpperCase ( ) ;
910
976
}
977
+
911
978
return result ;
912
979
}
913
980
@@ -923,38 +990,53 @@ export function base16_decode(string) {
923
990
}
924
991
925
992
export function bit_array_inspect ( bits , acc ) {
926
- return `${ acc } ${ [ ...bits . buffer ] . join ( ", " ) } ` ;
927
- }
993
+ if ( bits . bitSize === 0 ) {
994
+ return acc ;
995
+ }
928
996
929
- export function bit_array_compare ( first , second ) {
930
- for ( let i = 0 ; i < first . length ; i ++ ) {
931
- if ( i >= second . length ) {
932
- return new Gt ( ) ; // first has more items
933
- }
934
- const f = first . buffer [ i ] ;
935
- const s = second . buffer [ i ] ;
936
- if ( f > s ) {
937
- return new Gt ( ) ;
938
- }
939
- if ( f < s ) {
940
- return new Lt ( ) ;
941
- }
997
+ for ( let i = 0 ; i < bits . byteSize - 1 ; i ++ ) {
998
+ acc += bits . byteAt ( i ) . toString ( ) ;
999
+ acc += ", " ;
942
1000
}
943
- // This means that either first did not have any items
944
- // or all items in first were equal to second.
945
- if ( first . length === second . length ) {
946
- return new Eq ( ) ;
1001
+
1002
+ if ( bits . byteSize * 8 === bits . bitSize ) {
1003
+ acc += bits . byteAt ( bits . byteSize - 1 ) . toString ( ) ;
1004
+ } else {
1005
+ const trailingBitsCount = bits . bitSize % 8 ;
1006
+ acc += bits . byteAt ( bits . byteSize - 1 ) >> ( 8 - trailingBitsCount ) ;
1007
+ acc += `:size(${ trailingBitsCount } )` ;
947
1008
}
948
- return new Lt ( ) ; // second has more items
1009
+
1010
+ return acc ;
1011
+ }
1012
+
1013
+ export function bit_array_to_int_and_size ( bits ) {
1014
+ const trailingBitsCount = bits . bitSize % 8 ;
1015
+ const unusedBitsCount = trailingBitsCount === 0 ? 0 : 8 - trailingBitsCount ;
1016
+
1017
+ return [ bits . byteAt ( 0 ) >> unusedBitsCount , bits . bitSize ] ;
949
1018
}
950
1019
951
1020
export function bit_array_starts_with ( bits , prefix ) {
952
- if ( prefix . length > bits . length ) {
1021
+ if ( prefix . bitSize > bits . bitSize ) {
953
1022
return false ;
954
1023
}
955
1024
956
- for ( let i = 0 ; i < prefix . length ; i ++ ) {
957
- if ( bits . buffer [ i ] !== prefix . buffer [ i ] ) {
1025
+ // Check any whole bytes
1026
+ const byteCount = Math . trunc ( prefix . bitSize / 8 ) ;
1027
+ for ( let i = 0 ; i < byteCount ; i ++ ) {
1028
+ if ( bits . byteAt ( i ) !== prefix . byteAt ( i ) ) {
1029
+ return false ;
1030
+ }
1031
+ }
1032
+
1033
+ // Check any trailing bits at the end of the prefix
1034
+ if ( prefix . bitSize % 8 !== 0 ) {
1035
+ const unusedBitsCount = 8 - ( prefix . bitSize % 8 ) ;
1036
+ if (
1037
+ bits . byteAt ( byteCount ) >> unusedBitsCount !==
1038
+ prefix . byteAt ( byteCount ) >> unusedBitsCount
1039
+ ) {
958
1040
return false ;
959
1041
}
960
1042
}
0 commit comments