Skip to content

Commit

Permalink
feat: add extended json parsing for $uuid
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Reggi committed Oct 1, 2020
1 parent a48676b commit b1b2a0e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 8 deletions.
22 changes: 14 additions & 8 deletions src/binary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Buffer } from 'buffer';
import { ensureBuffer } from './ensure_buffer';
import type { EJSONOptions } from './extended_json';
import { parseUUID, UUIDExtended } from './uuid';

type BinarySequence = Uint8Array | Buffer | number[];

Expand Down Expand Up @@ -223,20 +224,25 @@ export class Binary {

/** @internal */
static fromExtendedJSON(
doc: BinaryExtendedLegacy | BinaryExtended,
doc: BinaryExtendedLegacy | BinaryExtended | UUIDExtended,
options?: EJSONOptions
): Binary {
options = options || {};
let data: Buffer | undefined;
let type;
if (options.legacy && typeof doc.$binary === 'string' && '$type' in doc) {
type = doc.$type ? parseInt(doc.$type, 16) : 0;
data = Buffer.from(doc.$binary, 'base64');
} else {
if (typeof doc.$binary !== 'string') {
type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
data = Buffer.from(doc.$binary.base64, 'base64');
if ('$binary' in doc) {
if (options.legacy && typeof doc.$binary === 'string' && '$type' in doc) {
type = doc.$type ? parseInt(doc.$type, 16) : 0;
data = Buffer.from(doc.$binary, 'base64');
} else {
if (typeof doc.$binary !== 'string') {
type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
data = Buffer.from(doc.$binary.base64, 'base64');
}
}
} else if ('$uuid' in doc) {
type = 4;
data = Buffer.from(parseUUID(doc.$uuid));
}
if (!data) {
throw new TypeError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);
Expand Down
1 change: 1 addition & 0 deletions src/extended_json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface EJSONOptions {
const keysToCodecs = {
$oid: ObjectId,
$binary: Binary,
$uuid: Binary,
$symbol: BSONSymbol,
$numberInt: Int32,
$numberDecimal: Decimal128,
Expand Down
56 changes: 56 additions & 0 deletions src/uuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* UUID regular expression pattern copied from `uuid` npm module.
* @see https://github.com/uuidjs/uuid/blob/master/src/regex.js
*/
const UUID_RX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;

export interface UUIDExtended {
$uuid: string;
}

/**
* Parser function copied from `uuid` npm module.
* @see https://github.com/uuidjs/uuid/blob/master/src/parse.js
* @internal
*/
export function parseUUID(uuid: string): Uint8Array {
if (typeof uuid !== 'string') {
throw new TypeError('Invalid type for UUID, expected string but got ' + typeof uuid);
}

if (!UUID_RX.test(uuid)) {
throw new TypeError('Invalid format for UUID: ' + uuid);
}

let v;
const arr = new Uint8Array(16);

// Parse ########-....-....-....-............
arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24;
arr[1] = (v >>> 16) & 0xff;
arr[2] = (v >>> 8) & 0xff;
arr[3] = v & 0xff;

// Parse ........-####-....-....-............
arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8;
arr[5] = v & 0xff;

// Parse ........-....-####-....-............
arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8;
arr[7] = v & 0xff;

// Parse ........-....-....-####-............
arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8;
arr[9] = v & 0xff;

// Parse ........-....-....-....-############
// (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)
arr[10] = ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff;
arr[11] = (v / 0x100000000) & 0xff;
arr[12] = (v >>> 24) & 0xff;
arr[13] = (v >>> 16) & 0xff;
arr[14] = (v >>> 8) & 0xff;
arr[15] = v & 0xff;

return arr;
}
16 changes: 16 additions & 0 deletions test/node/specs/bson-corpus/binary.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
"canonical_bson": "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400",
"canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"04\"}}}"
},
{
"description": "subtype 0x04 UUID",
"canonical_bson": "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400",
"canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"04\"}}}",
"degenerate_extjson": "{\"x\" : { \"$uuid\" : \"73ffd264-44b3-4c69-90e8-e7d1dfc035d4\"}}"
},
{
"description": "subtype 0x05",
"canonical_bson": "1D000000057800100000000573FFD26444B34C6990E8E7D1DFC035D400",
Expand Down Expand Up @@ -81,5 +87,15 @@
"description": "subtype 0x02 length negative one",
"bson": "130000000578000600000002FFFFFFFFFFFF00"
}
],
"parseErrors": [
{
"description": "$uuid wrong type",
"string": "{\"x\" : { \"$uuid\" : { \"data\" : \"73ffd264-44b3-4c69-90e8-e7d1dfc035d4\"}}}"
},
{
"description": "$uuid invalid value",
"string": "{\"x\" : { \"$uuid\" : \"73ffd264-44b3-90e8-e7d1dfc035d4\"}}"
}
]
}

0 comments on commit b1b2a0e

Please sign in to comment.