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