@@ -4,6 +4,7 @@ import { BSONError } from './error';
4
4
import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants' ;
5
5
import { ByteUtils } from './utils/byte_utils' ;
6
6
import { BSONValue } from './bson_value' ;
7
+ import { NumberUtils } from './utils/number_utils' ;
7
8
8
9
/** @public */
9
10
export type BinarySequence = Uint8Array | number [ ] ;
@@ -22,6 +23,15 @@ export interface BinaryExtended {
22
23
} ;
23
24
}
24
25
26
+ /** Creates a copy of the Uint8Array bytes. */
27
+ const copy =
28
+ // eslint-disable-next-line @typescript-eslint/unbound-method
29
+ Uint8Array . prototype . slice . call . bind ( Uint8Array . prototype . slice ) as unknown as (
30
+ bytes : Uint8Array ,
31
+ start : number ,
32
+ end : number
33
+ ) => Uint8Array ;
34
+
25
35
/**
26
36
* A class representation of the BSON Binary type.
27
37
* @public
@@ -58,9 +68,18 @@ export class Binary extends BSONValue {
58
68
static readonly SUBTYPE_COLUMN = 7 ;
59
69
/** Sensitive BSON type */
60
70
static readonly SUBTYPE_SENSITIVE = 8 ;
71
+ /** Vector BSON type */
72
+ static readonly SUBTYPE_VECTOR = 9 ;
61
73
/** User BSON type */
62
74
static readonly SUBTYPE_USER_DEFINED = 128 ;
63
75
76
+ /** d_type of a Binary Vector (subtype: 9) */
77
+ static readonly VECTOR_TYPE = Object . freeze ( {
78
+ Int8 : 0x03 ,
79
+ Float32 : 0x27 ,
80
+ PackedBit : 0x10
81
+ } as const ) ;
82
+
64
83
/**
65
84
* The bytes of the Binary value.
66
85
*
@@ -238,6 +257,11 @@ export class Binary extends BSONValue {
238
257
/** @internal */
239
258
toExtendedJSON ( options ?: EJSONOptions ) : BinaryExtendedLegacy | BinaryExtended {
240
259
options = options || { } ;
260
+
261
+ if ( this . sub_type === Binary . SUBTYPE_VECTOR ) {
262
+ Binary . validateVector ( this ) ;
263
+ }
264
+
241
265
const base64String = ByteUtils . toBase64 ( this . buffer ) ;
242
266
243
267
const subType = Number ( this . sub_type ) . toString ( 16 ) ;
@@ -310,6 +334,202 @@ export class Binary extends BSONValue {
310
334
const subTypeArg = inspect ( this . sub_type , options ) ;
311
335
return `Binary.createFromBase64(${ base64Arg } , ${ subTypeArg } )` ;
312
336
}
337
+
338
+ /**
339
+ * If this Binary represents a Int8 Vector,
340
+ * returns a copy of the bytes in a new Int8Array.
341
+ */
342
+ public toInt8Array ( ) : Int8Array {
343
+ if ( this . sub_type !== Binary . SUBTYPE_VECTOR ) {
344
+ throw new BSONError ( 'Binary sub_type is not Vector' ) ;
345
+ }
346
+
347
+ if ( this . buffer [ 0 ] !== Binary . VECTOR_TYPE . Int8 ) {
348
+ throw new BSONError ( 'Binary d_type field is not Int8' ) ;
349
+ }
350
+
351
+ return new Int8Array ( copy ( this . buffer , 2 , this . position ) . buffer ) ;
352
+ }
353
+
354
+ /**
355
+ * If this Binary represents a Float32 Vector,
356
+ * returns a copy of the bytes in a new Float32Array.
357
+ */
358
+ public toFloat32Array ( ) : Float32Array {
359
+ if ( this . sub_type !== Binary . SUBTYPE_VECTOR ) {
360
+ throw new BSONError ( 'Binary sub_type is not Vector' ) ;
361
+ }
362
+
363
+ if ( this . buffer [ 0 ] !== Binary . VECTOR_TYPE . Float32 ) {
364
+ throw new BSONError ( 'Binary d_type field is not Float32' ) ;
365
+ }
366
+
367
+ const floatBytes = copy ( this . buffer , 2 , this . position ) ;
368
+ if ( NumberUtils . isBigEndian ) {
369
+ for ( let i = 0 ; i < floatBytes . byteLength ; i += 4 ) {
370
+ const byte0 = floatBytes [ i ] ;
371
+ const byte1 = floatBytes [ i + 1 ] ;
372
+ const byte2 = floatBytes [ i + 2 ] ;
373
+ const byte3 = floatBytes [ i + 3 ] ;
374
+ floatBytes [ i ] = byte3 ;
375
+ floatBytes [ i + 1 ] = byte2 ;
376
+ floatBytes [ i + 2 ] = byte1 ;
377
+ floatBytes [ i + 3 ] = byte0 ;
378
+ }
379
+ }
380
+ return new Float32Array ( floatBytes . buffer ) ;
381
+ }
382
+
383
+ /**
384
+ * If this Binary represents packed bit Vector,
385
+ * returns a copy of the bytes that are packed bits.
386
+ *
387
+ * Use `toBits` to get the unpacked bits.
388
+ */
389
+ public toPackedBits ( ) : Uint8Array {
390
+ if ( this . sub_type !== Binary . SUBTYPE_VECTOR ) {
391
+ throw new BSONError ( 'Binary sub_type is not Vector' ) ;
392
+ }
393
+
394
+ if ( this . buffer [ 0 ] !== Binary . VECTOR_TYPE . PackedBit ) {
395
+ throw new BSONError ( 'Binary d_type field is not packed bit' ) ;
396
+ }
397
+
398
+ return copy ( this . buffer , 2 , this . position ) ;
399
+ }
400
+
401
+ /**
402
+ * If this Binary represents a Packed bit Vector,
403
+ * returns a copy of the bit unpacked into a new Int8Array.
404
+ */
405
+ public toBits ( ) : Int8Array {
406
+ if ( this . sub_type !== Binary . SUBTYPE_VECTOR ) {
407
+ throw new BSONError ( 'Binary sub_type is not Vector' ) ;
408
+ }
409
+
410
+ if ( this . buffer [ 0 ] !== Binary . VECTOR_TYPE . PackedBit ) {
411
+ throw new BSONError ( 'Binary d_type field is not packed bit' ) ;
412
+ }
413
+
414
+ const byteCount = this . length ( ) - 2 ;
415
+ const bitCount = byteCount * 8 - this . buffer [ 1 ] ;
416
+ const bits = new Int8Array ( bitCount ) ;
417
+ outer: for ( let byteOffset = 0 ; byteOffset < byteCount ; byteOffset ++ ) {
418
+ const byte = this . buffer [ byteOffset + 2 ] ;
419
+ for ( let bitBase = 0 ; bitBase < 8 ; bitBase ++ ) {
420
+ const bitOffset = Math . ceil ( byteOffset / 8 ) * 8 + bitBase ;
421
+ if ( bitOffset >= bits . length ) break outer;
422
+ const mask = 1 << ( 7 - bitBase ) ;
423
+ bits [ bitOffset ] = byte & mask ? 1 : 0 ;
424
+ }
425
+ }
426
+ return bits ;
427
+ }
428
+
429
+ /**
430
+ * Constructs a Binary representing an Int8 Vector.
431
+ * @param array - The array to store as a view on the Binary class
432
+ */
433
+ public static fromInt8Array ( array : Int8Array ) : Binary {
434
+ const buffer = ByteUtils . allocate ( array . byteLength + 2 ) ;
435
+ buffer [ 0 ] = Binary . VECTOR_TYPE . Int8 ;
436
+ buffer [ 1 ] = 0 ;
437
+ const intBytes = new Uint8Array ( array . buffer , array . byteOffset , array . byteLength ) ;
438
+ buffer . set ( intBytes , 2 ) ;
439
+ return new this ( buffer , this . SUBTYPE_VECTOR ) ;
440
+ }
441
+
442
+ /** Constructs a Binary representing an Float32 Vector. */
443
+ public static fromFloat32Array ( array : Float32Array ) : Binary {
444
+ const binaryBytes = ByteUtils . allocate ( array . byteLength + 2 ) ;
445
+ binaryBytes [ 0 ] = Binary . VECTOR_TYPE . Float32 ;
446
+ binaryBytes [ 1 ] = 0 ;
447
+
448
+ const floatBytes = new Uint8Array ( array . buffer , array . byteOffset , array . byteLength ) ;
449
+ binaryBytes . set ( floatBytes , 2 ) ;
450
+
451
+ if ( NumberUtils . isBigEndian ) {
452
+ for ( let i = 2 ; i < binaryBytes . byteLength ; i += 4 ) {
453
+ const byte0 = binaryBytes [ i ] ;
454
+ const byte1 = binaryBytes [ i + 1 ] ;
455
+ const byte2 = binaryBytes [ i + 2 ] ;
456
+ const byte3 = binaryBytes [ i + 3 ] ;
457
+ binaryBytes [ i ] = byte3 ;
458
+ binaryBytes [ i + 1 ] = byte2 ;
459
+ binaryBytes [ i + 2 ] = byte1 ;
460
+ binaryBytes [ i + 3 ] = byte0 ;
461
+ }
462
+ }
463
+
464
+ return new this ( binaryBytes , this . SUBTYPE_VECTOR ) ;
465
+ }
466
+
467
+ /**
468
+ * Constructs a Binary representing a packed bit Vector.
469
+ *
470
+ * Use `fromBits` to pack an array of 1s and 0s.
471
+ */
472
+ public static fromPackedBits ( array : Uint8Array , padding = 0 ) : Binary {
473
+ const buffer = ByteUtils . allocate ( array . byteLength + 2 ) ;
474
+ buffer [ 0 ] = Binary . VECTOR_TYPE . PackedBit ;
475
+ buffer [ 1 ] = padding ;
476
+ buffer . set ( array , 2 ) ;
477
+ return new this ( buffer , this . SUBTYPE_VECTOR ) ;
478
+ }
479
+
480
+ /**
481
+ * Constructs a Binary representing an Packed Bit Vector.
482
+ * @param array - The array of 1s and 0s to pack into the Binary instance
483
+ */
484
+ public static fromBits ( bits : ArrayLike < number > ) : Binary {
485
+ const byteLength = Math . ceil ( bits . length / 8 ) ;
486
+ const bytes = new Uint8Array ( byteLength + 2 ) ;
487
+ bytes [ 0 ] = Binary . VECTOR_TYPE . PackedBit ;
488
+
489
+ const remainder = bits . length % 8 ;
490
+ bytes [ 1 ] = remainder === 0 ? 0 : 8 - remainder ;
491
+
492
+ for ( let bitOffset = 0 ; bitOffset < bits . length ; bitOffset ++ ) {
493
+ const byteOffset = Math . floor ( bitOffset / 8 ) ;
494
+
495
+ const bit = bits [ bitOffset ] ? 1 : 0 ;
496
+
497
+ if ( bit === 0 ) continue ;
498
+
499
+ const shift = 7 - ( bitOffset % 8 ) ;
500
+ bytes [ byteOffset + 2 ] |= bit << shift ;
501
+ }
502
+
503
+ return new this ( bytes , Binary . SUBTYPE_VECTOR ) ;
504
+ }
505
+
506
+ /** @internal */
507
+ static validateVector ( vector : Binary ) : void {
508
+ if ( vector . sub_type !== this . SUBTYPE_VECTOR ) return ;
509
+
510
+ const size = vector . position ;
511
+ const d_type = vector . buffer [ 0 ] ;
512
+ const padding = vector . buffer [ 1 ] ;
513
+
514
+ if (
515
+ ( d_type === this . VECTOR_TYPE . Float32 || d_type === this . VECTOR_TYPE . Int8 ) &&
516
+ padding !== 0
517
+ ) {
518
+ throw new BSONError ( 'Invalid Vector: padding must be zero for int8 and float32 vectors' ) ;
519
+ }
520
+
521
+ if ( d_type === this . VECTOR_TYPE . PackedBit && padding !== 0 && size === 2 ) {
522
+ throw new BSONError (
523
+ 'Invalid Vector: padding must be zero for packed bit vectors that are empty'
524
+ ) ;
525
+ }
526
+
527
+ if ( d_type === this . VECTOR_TYPE . PackedBit && padding > 7 ) {
528
+ throw new BSONError (
529
+ `Invalid Vector: padding must be a value between 0 and 7. found: ${ padding } `
530
+ ) ;
531
+ }
532
+ }
313
533
}
314
534
315
535
/** @public */
0 commit comments