@@ -4,6 +4,7 @@ import { BSONError } from './error';
44import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants' ;
55import { ByteUtils } from './utils/byte_utils' ;
66import { BSONValue } from './bson_value' ;
7+ import { NumberUtils } from './utils/number_utils' ;
78
89/** @public */
910export type BinarySequence = Uint8Array | number [ ] ;
@@ -58,9 +59,18 @@ export class Binary extends BSONValue {
5859 static readonly SUBTYPE_COLUMN = 7 ;
5960 /** Sensitive BSON type */
6061 static readonly SUBTYPE_SENSITIVE = 8 ;
62+ /** Vector BSON type */
63+ static readonly SUBTYPE_VECTOR = 9 ;
6164 /** User BSON type */
6265 static readonly SUBTYPE_USER_DEFINED = 128 ;
6366
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+
6474 /**
6575 * The bytes of the Binary value.
6676 *
@@ -238,6 +248,11 @@ export class Binary extends BSONValue {
238248 /** @internal */
239249 toExtendedJSON ( options ?: EJSONOptions ) : BinaryExtendedLegacy | BinaryExtended {
240250 options = options || { } ;
251+
252+ if ( this . sub_type === Binary . SUBTYPE_VECTOR ) {
253+ Binary . validateVector ( this ) ;
254+ }
255+
241256 const base64String = ByteUtils . toBase64 ( this . buffer ) ;
242257
243258 const subType = Number ( this . sub_type ) . toString ( 16 ) ;
@@ -310,6 +325,213 @@ export class Binary extends BSONValue {
310325 const subTypeArg = inspect ( this . sub_type , options ) ;
311326 return `Binary.createFromBase64(${ base64Arg } , ${ subTypeArg } )` ;
312327 }
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+ }
313535}
314536
315537/** @public */
0 commit comments