Skip to content

Commit

Permalink
fix(NODE-6059): clean up experimental APIs (#665)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken committed Apr 1, 2024
1 parent d7898f9 commit 3289184
Show file tree
Hide file tree
Showing 13 changed files with 63 additions and 627 deletions.
2 changes: 1 addition & 1 deletion src/bson.ts
Expand Up @@ -51,7 +51,7 @@ export {
Decimal128
};
export { BSONValue } from './bson_value';
export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
export { BSONError, BSONVersionError, BSONRuntimeError, BSONOffsetError } from './error';
export { BSONType } from './constants';
export { EJSON } from './extended_json';
export { onDemand, type OnDemand } from './parser/on_demand/index';
Expand Down
4 changes: 2 additions & 2 deletions src/error.ts
Expand Up @@ -98,8 +98,8 @@ export class BSONOffsetError extends BSONError {

public offset: number;

constructor(message: string, offset: number) {
super(`${message}. offset: ${offset}`);
constructor(message: string, offset: number, options?: { cause?: unknown }) {
super(`${message}. offset: ${offset}`, options);
this.offset = offset;
}
}
25 changes: 1 addition & 24 deletions src/parser/on_demand/index.ts
@@ -1,40 +1,20 @@
import { type BSONError, BSONOffsetError } from '../../error';
import { ByteUtils } from '../../utils/byte_utils';
import { NumberUtils } from '../../utils/number_utils';
import { type BSONElement, parseToElements, getSize } from './parse_to_elements';
import { type BSONReviver, type Container, parseToStructure } from './parse_to_structure';
import { type BSONElement, parseToElements } from './parse_to_elements';
/**
* @experimental
* @public
*
* A new set of BSON APIs that are currently experimental and not intended for production use.
*/
export type OnDemand = {
BSONOffsetError: {
new (message: string, offset: number): BSONOffsetError;
isBSONError(value: unknown): value is BSONError;
};
parseToElements: (this: void, bytes: Uint8Array, startOffset?: number) => Iterable<BSONElement>;
parseToStructure: <
TRoot extends Container = {
dest: Record<string, unknown>;
kind: 'object';
}
>(
bytes: Uint8Array,
startOffset?: number,
root?: TRoot,
reviver?: BSONReviver
) => TRoot extends undefined ? Record<string, unknown> : TRoot['dest'];
// Types
BSONElement: BSONElement;
Container: Container;
BSONReviver: BSONReviver;

// Utils
ByteUtils: ByteUtils;
NumberUtils: NumberUtils;
getSize: (source: Uint8Array, offset: number) => number;
};

/**
Expand All @@ -44,11 +24,8 @@ export type OnDemand = {
const onDemand: OnDemand = Object.create(null);

onDemand.parseToElements = parseToElements;
onDemand.parseToStructure = parseToStructure;
onDemand.BSONOffsetError = BSONOffsetError;
onDemand.ByteUtils = ByteUtils;
onDemand.NumberUtils = NumberUtils;
onDemand.getSize = getSize;

Object.freeze(onDemand);

Expand Down
21 changes: 6 additions & 15 deletions src/parser/on_demand/parse_to_elements.ts
@@ -1,4 +1,5 @@
import { BSONOffsetError } from '../../error';
import { NumberUtils } from '../../utils/number_utils';

/**
* @internal
Expand Down Expand Up @@ -44,22 +45,12 @@ export type BSONElement = [
length: number
];

/**
* @experimental
* @public
*
* Parses a int32 little-endian at offset, throws if it is negative
*/
export function getSize(source: Uint8Array, offset: number): number {
if (source[offset + 3] > 127) {
throw new BSONOffsetError('BSON size cannot be negative', offset);
function getSize(source: Uint8Array, offset: number) {
try {
return NumberUtils.getNonnegativeInt32LE(source, offset);
} catch (cause) {
throw new BSONOffsetError('BSON size cannot be negative', offset, { cause });
}
return (
source[offset] |
(source[offset + 1] << 8) |
(source[offset + 2] << 16) |
(source[offset + 3] << 24)
);
}

/**
Expand Down
145 changes: 0 additions & 145 deletions src/parser/on_demand/parse_to_structure.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/utils/byte_utils.ts
Expand Up @@ -10,7 +10,7 @@ import { webByteUtils } from './web_byte_utils';
*/
export type ByteUtils = {
/** Transforms the input to an instance of Buffer if running on node, otherwise Uint8Array */
toLocalBufferType(buffer: Uint8Array | ArrayBufferView | ArrayBuffer): Uint8Array;
toLocalBufferType: (buffer: Uint8Array | ArrayBufferView | ArrayBuffer) => Uint8Array;
/** Create empty space of size */
allocate: (size: number) => Uint8Array;
/** Create empty space of size, use pooled memory when available */
Expand All @@ -36,9 +36,9 @@ export type ByteUtils = {
/** Get the utf8 code unit count from a string if it were to be transformed to utf8 */
utf8ByteLength: (input: string) => number;
/** Encode UTF8 bytes generated from `source` string into `destination` at byteOffset. Returns the number of bytes encoded. */
encodeUTF8Into(destination: Uint8Array, source: string, byteOffset: number): number;
encodeUTF8Into: (destination: Uint8Array, source: string, byteOffset: number) => number;
/** Generate a Uint8Array filled with random bytes with byteLength */
randomBytes(byteLength: number): Uint8Array;
randomBytes: (byteLength: number) => Uint8Array;
};

declare const Buffer: { new (): unknown; prototype?: { _isBuffer?: boolean } } | undefined;
Expand Down
34 changes: 25 additions & 9 deletions src/utils/number_utils.ts
Expand Up @@ -13,15 +13,19 @@ const isBigEndian = FLOAT_BYTES[7] === 0;
* A collection of functions that get or set various numeric types and bit widths from a Uint8Array.
*/
export type NumberUtils = {
getInt32LE(source: Uint8Array, offset: number): number;
getUint32LE(source: Uint8Array, offset: number): number;
getUint32BE(source: Uint8Array, offset: number): number;
getBigInt64LE(source: Uint8Array, offset: number): bigint;
getFloat64LE(source: Uint8Array, offset: number): number;
setInt32BE(destination: Uint8Array, offset: number, value: number): 4;
setInt32LE(destination: Uint8Array, offset: number, value: number): 4;
setBigInt64LE(destination: Uint8Array, offset: number, value: bigint): 8;
setFloat64LE(destination: Uint8Array, offset: number, value: number): 8;
/**
* Parses a signed int32 at offset. Throws a `RangeError` if value is negative.
*/
getNonnegativeInt32LE: (source: Uint8Array, offset: number) => number;
getInt32LE: (source: Uint8Array, offset: number) => number;
getUint32LE: (source: Uint8Array, offset: number) => number;
getUint32BE: (source: Uint8Array, offset: number) => number;
getBigInt64LE: (source: Uint8Array, offset: number) => bigint;
getFloat64LE: (source: Uint8Array, offset: number) => number;
setInt32BE: (destination: Uint8Array, offset: number, value: number) => 4;
setInt32LE: (destination: Uint8Array, offset: number, value: number) => 4;
setBigInt64LE: (destination: Uint8Array, offset: number, value: bigint) => 8;
setFloat64LE: (destination: Uint8Array, offset: number, value: number) => 8;
};

/**
Expand All @@ -31,6 +35,18 @@ export type NumberUtils = {
* @public
*/
export const NumberUtils: NumberUtils = {
getNonnegativeInt32LE(source: Uint8Array, offset: number): number {
if (source[offset + 3] > 127) {
throw new RangeError(`Size cannot be negative at offset: ${offset}`);
}
return (
source[offset] |
(source[offset + 1] << 8) |
(source[offset + 2] << 16) |
(source[offset + 3] << 24)
);
},

/** Reads a little-endian 32-bit integer from source */
getInt32LE(source: Uint8Array, offset: number): number {
return (
Expand Down
10 changes: 5 additions & 5 deletions test/node/error.test.ts
Expand Up @@ -6,7 +6,7 @@ import {
BSONError,
BSONVersionError,
BSONRuntimeError,
onDemand
BSONOffsetError
} from '../register-bson';

const instanceOfChecksWork = !__isWeb__;
Expand Down Expand Up @@ -111,19 +111,19 @@ describe('BSONError', function () {

describe('class BSONOffsetError', () => {
it('is a BSONError instance', function () {
expect(BSONError.isBSONError(new onDemand.BSONOffsetError('Oopsie', 3))).to.be.true;
expect(BSONError.isBSONError(new BSONOffsetError('Oopsie', 3))).to.be.true;
});

it('has a name property equal to "BSONOffsetError"', function () {
expect(new onDemand.BSONOffsetError('Woops!', 3)).to.have.property('name', 'BSONOffsetError');
expect(new BSONOffsetError('Woops!', 3)).to.have.property('name', 'BSONOffsetError');
});

it('sets the offset property', function () {
expect(new onDemand.BSONOffsetError('Woops!', 3)).to.have.property('offset', 3);
expect(new BSONOffsetError('Woops!', 3)).to.have.property('offset', 3);
});

it('includes the offset in the message', function () {
expect(new onDemand.BSONOffsetError('Woops!', 3))
expect(new BSONOffsetError('Woops!', 3))
.to.have.property('message')
.that.matches(/offset: 3/i);
});
Expand Down
1 change: 1 addition & 0 deletions test/node/exports.test.ts
Expand Up @@ -30,6 +30,7 @@ const EXPECTED_EXPORTS = [
'Decimal128',
'BSONError',
'BSONRuntimeError',
'BSONOffsetError',
'setInternalBufferSize',
'serialize',
'serializeWithBufferAndIndex',
Expand Down
2 changes: 1 addition & 1 deletion test/node/parser/on_demand/parse_to_elements.test.ts
Expand Up @@ -5,7 +5,7 @@ import * as BSON from '../../../register-bson';
import { bufferFromHexArray, stringToUTF8HexBytes, int32LEToHex } from '../../tools/utils';

const parseToElements = BSON.onDemand.parseToElements;
const BSONOffsetError = BSON.onDemand.BSONOffsetError;
const BSONOffsetError = BSON.BSONOffsetError;

describe('parseToElements()', () => {
context('when given less than 5 bytes', () => {
Expand Down

0 comments on commit 3289184

Please sign in to comment.