Skip to content

Commit

Permalink
non-recursive encoding wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Jun 24, 2024
1 parent 614b090 commit 0da4481
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 137 deletions.
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0",
"lint": "npx eslint -c ../../.eslintrc.js \"src/**/*.ts\" && node ./scripts/lint",
"format": "prettier --config ../../prettier.config.js --ignore-path ../.prettierignore --write \"**/*.{ts,md,json}\"",
"test": "yarn g:jest"
"test": "yarn g:jest --maxWorkers=1"
},
"main": "./dist-cjs/index.js",
"module": "./dist-es/index.js",
Expand Down
269 changes: 137 additions & 132 deletions packages/core/src/submodules/cbor/cbor-encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,149 +84,154 @@ function encodeHeader(major: CborMajorType, value: Uint64 | number): void {
}

/**
* @param input - JS data object.
* @param _input - JS data object.
*/
export function encode(input: any): void {
ensureSpace(64);
if (typeof input === "string") {
encodeString(input);
return;
} else if (typeof input === "number") {
if (Number.isInteger(input)) {
// this section is inlined duplicate for performance.
export function encode(_input: any): void {
const encodeQueue = [_input];

while (encodeQueue.length) {
const input = encodeQueue.shift();
ensureSpace(typeof input === "string" ? input.length * 4 : 64);

if (typeof input === "string") {
if (USE_BUFFER) {
encodeHeader(majorUtf8String, Buffer.byteLength(input));
if ((data as BufferWithUtf8Write).utf8Write) {
cursor += (data as BufferWithUtf8Write).write(input, cursor);
} else {
cursor += (data as Buffer).write(input, cursor);
}
} else {
const bytes = fromUtf8(input);
encodeHeader(majorUtf8String, bytes.byteLength);
data.set(bytes, cursor);
cursor += bytes.byteLength;
}
continue;
} else if (typeof input === "number") {
if (Number.isInteger(input)) {
// this section is inlined duplicate for performance.
const major = input >= 0 ? majorUint64 : majorNegativeInt64;
const value = input >= 0 ? input : -input - 1;
if (value < 24) {
data[cursor++] = (major << 5) | value;
} else if (value < 256 /* 2 ** 8 */) {
data[cursor++] = (major << 5) | 24;
data[cursor++] = value;
} else if (value < 65536 /* 2 ** 16 */) {
data[cursor++] = (major << 5) | extendedFloat16;
data[cursor++] = (value as number) >> 8;
data[cursor++] = value as number & 0b1111_1111;
} else if (value < 4294967296 /* 2 ** 32 */) {
data[cursor++] = (major << 5) | extendedFloat32;
dataView.setUint32(cursor, value);
cursor += 4;
} else {
data[cursor++] = (major << 5) | extendedFloat64;
dataView.setBigUint64(cursor, BigInt(value));
cursor += 8;
}
continue;
}
data[cursor++] = (majorSpecial << 5) | extendedFloat64;
dataView.setFloat64(cursor, input);
cursor += 8;
continue;
} else if (typeof input === "bigint") {
const major = input >= 0 ? majorUint64 : majorNegativeInt64;
const value = input >= 0 ? input : -input - 1;
if (value < 24) {
data[cursor++] = (major << 5) | value;
} else if (value < 256 /* 2 ** 8 */) {
const value = input >= 0 ? input : -input - BigInt(1);
const n = Number(value);
// this section is inlined duplicate for performance.
if (n < 24) {
data[cursor++] = (major << 5) | n;
} else if (n < 256 /* 2 ** 8 */) {
data[cursor++] = (major << 5) | 24;
data[cursor++] = value;
} else if (value < 65536 /* 2 ** 16 */) {
data[cursor++] = n;
} else if (n < 65536 /* 2 ** 16 */) {
data[cursor++] = (major << 5) | extendedFloat16;
data[cursor++] = (value as number) >> 8;
data[cursor++] = value as number & 0b1111_1111;
} else if (value < 4294967296 /* 2 ** 32 */) {
data[cursor++] = n >> 8;
data[cursor++] = n & 0b1111_1111;
} else if (n < 4294967296 /* 2 ** 32 */) {
data[cursor++] = (major << 5) | extendedFloat32;
dataView.setUint32(cursor, value);
dataView.setUint32(cursor, n);
cursor += 4;
} else {
data[cursor++] = (major << 5) | extendedFloat64;
dataView.setBigUint64(cursor, BigInt(value));
dataView.setBigUint64(cursor, value);
cursor += 8;
}
return;
}
data[cursor++] = (majorSpecial << 5) | extendedFloat64;
dataView.setFloat64(cursor, input);
cursor += 8;
return;
} else if (typeof input === "bigint") {
const major = input >= 0 ? majorUint64 : majorNegativeInt64;
const value = input >= 0 ? input : -input - BigInt(1);
const n = Number(value);
// this section is inlined duplicate for performance.
if (n < 24) {
data[cursor++] = (major << 5) | n;
} else if (n < 256 /* 2 ** 8 */) {
data[cursor++] = (major << 5) | 24;
data[cursor++] = n;
} else if (n < 65536 /* 2 ** 16 */) {
data[cursor++] = (major << 5) | extendedFloat16;
data[cursor++] = n >> 8;
data[cursor++] = n & 0b1111_1111;
} else if (n < 4294967296 /* 2 ** 32 */) {
data[cursor++] = (major << 5) | extendedFloat32;
dataView.setUint32(cursor, n);
cursor += 4;
} else {
data[cursor++] = (major << 5) | extendedFloat64;
dataView.setBigUint64(cursor, value);
cursor += 8;
}
return;
} else if (input === null) {
data[cursor++] = (majorSpecial << 5) | specialNull;
return;
} else if (typeof input === "boolean") {
data[cursor++] = (majorSpecial << 5) | (input ? specialTrue : specialFalse);
return;
} else if (typeof input === "undefined") {
// Note: Smithy spec requires that undefined not be serialized
// though the CBOR spec includes it.
// buffer[0] = compose(majorSpecial, specialUndefined);
// return 1;
throw new Error("@smithy/core/cbor: client may not serialize undefined value.");
} else if (Array.isArray(input)) {
encodeHeader(majorList, input.length);
for (let i = 0; i < input.length; ++i) {
encode(input[i]);
}
return;
} else if (typeof input.byteLength === "number") {
ensureSpace(input.length * 2);
encodeHeader(majorUnstructuredByteString, input.length);
data.set(input, cursor);
cursor += input.byteLength;
return;
} else if (typeof input === "object" && "tag" in input && "value" in input && Object.keys(input).length === 2) {
encodeHeader(majorTag, input.tag);
encode(input.value);
return;
} else if (typeof input === "object") {
const keys = Object.keys(input);
encodeHeader(majorMap, keys.length);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
encodeString(key);
encode(input[key]);
continue;
} else if (input === null) {
data[cursor++] = (majorSpecial << 5) | specialNull;
continue;
} else if (typeof input === "boolean") {
data[cursor++] = (majorSpecial << 5) | (input ? specialTrue : specialFalse);
continue;
} else if (typeof input === "undefined") {
// Note: Smithy spec requires that undefined not be serialized
// though the CBOR spec includes it.
throw new Error("@smithy/core/cbor: client may not serialize undefined value.");
} else if (Array.isArray(input)) {
encodeHeader(majorList, input.length);
for (let i = 0; i < input.length; ++i) {
// encode(input[i]);
encodeQueue.push(input[i]);
}
continue;
} else if (typeof input.byteLength === "number") {
ensureSpace(input.length * 2);
encodeHeader(majorUnstructuredByteString, input.length);
data.set(input, cursor);
cursor += input.byteLength;
continue;
} else if (typeof input === "object" && "tag" in input && "value" in input && Object.keys(input).length === 2) {
encodeHeader(majorTag, input.tag);
// encode(input.value);
encodeQueue.push(input.value);
continue;
} else if (typeof input === "object") {
const keys = Object.keys(input);
encodeHeader(majorMap, keys.length);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
// encode(key);
// encode(input[key]);
encodeQueue.push(key);
encodeQueue.push(input[key]);
}
continue;
}
return;
}

throw new Error(`data type ${input?.constructor?.name ?? typeof input} not compatible for encoding.`);
}

function encodeString(input: string) {
ensureSpace(input.length * 4);

if (USE_BUFFER) {
encodeHeader(majorUtf8String, Buffer.byteLength(input));
if ((data as BufferWithUtf8Write).utf8Write) {
cursor += (data as BufferWithUtf8Write).write(input, cursor);
} else {
cursor += (data as Buffer).write(input, cursor);
}
} else {
const bytes = fromUtf8(input);
encodeHeader(majorUtf8String, bytes.byteLength);
data.set(bytes, cursor);
cursor += bytes.byteLength;
throw new Error(`data type ${input?.constructor?.name ?? typeof input} not compatible for encoding.`);
}
// for (let i = 0; i < input.length; ++i) {
// const c = input.charCodeAt(i);
// const trailer = 0b1000_0000;
//
// if (c < 128 /* byte */) {
// data[cursor++] = c;
// } else if (c < 2048 /* 12 bits */) {
// // left 6 bits
// data[cursor++] = (c >> 6) | 0b1100_00000; // 11 leading.
// // right 6 bits
// data[cursor++] = (c & 0b00_111111) | trailer;
// } else if (c < 65536 /* 17 bits*/) {
// data[cursor++] = ((c >> 12) & 0b0011_1111) | 0b1110_0000; // 111 leading.
// data[cursor++] = ((c >> 6) & 0b0011_1111) | trailer;
// data[cursor++] = ((c >> 0) & 0b0011_1111) | trailer;
// } else {
// // surrogate pair
// i++;
// // 1st
// data[cursor++] = ((c >> 18) & 0b0011_1111) | 0b1111_0000; // 1111 leading.
// data[cursor++] = ((c >> 12) & 0b0011_1111) | trailer;
//
// // 2nd
// data[cursor++] = ((c >> 6) & 0b0011_1111) | trailer;
// data[cursor++] = ((c >> 0) & 0b0011_1111) | trailer;
// }
// }
}

// function encodeString(input: string) {
// for (let i = 0; i < input.length; ++i) {
// const c = input.charCodeAt(i);
// const trailer = 0b1000_0000;
//
// if (c < 128 /* byte */) {
// data[cursor++] = c;
// } else if (c < 2048 /* 12 bits */) {
// // left 6 bits
// data[cursor++] = (c >> 6) | 0b1100_00000; // 11 leading.
// // right 6 bits
// data[cursor++] = (c & 0b00_111111) | trailer;
// } else if (c < 65536 /* 17 bits*/) {
// data[cursor++] = ((c >> 12) & 0b0011_1111) | 0b1110_0000; // 111 leading.
// data[cursor++] = ((c >> 6) & 0b0011_1111) | trailer;
// data[cursor++] = ((c >> 0) & 0b0011_1111) | trailer;
// } else {
// // surrogate pair
// i++;
// // 1st
// data[cursor++] = ((c >> 18) & 0b0011_1111) | 0b1111_0000; // 1111 leading.
// data[cursor++] = ((c >> 12) & 0b0011_1111) | trailer;
//
// // 2nd
// data[cursor++] = ((c >> 6) & 0b0011_1111) | trailer;
// data[cursor++] = ((c >> 0) & 0b0011_1111) | trailer;
// }
// }
// }
8 changes: 4 additions & 4 deletions packages/core/src/submodules/cbor/cbor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ describe("cbor", () => {
},
cbor: allocByteArray([
164, 102, 110, 117, 109, 98, 101, 114, 27, 0, 0, 122, 204, 161, 196, 74, 227, 103, 109, 101, 115, 115, 97, 103,
101, 108, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 100, 108, 105, 115, 116, 131, 0, 244, 161,
97, 97, 97, 98, 99, 109, 97, 112, 163, 97, 97, 97, 97, 97, 98, 97, 98, 101, 105, 116, 101, 109, 115, 136, 0, 32,
245, 244, 246, 96, 100, 116, 101, 115, 116, 130, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101, 109, 32,
65, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101, 109, 32, 66,
101, 108, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 100, 108, 105, 115, 116, 131, 99, 109, 97,
112, 163, +0, +244, +161, 97, 97, 97, 97, 97, 98, 97, 98, 101, 105, 116, 101, 109, 115, 136, +97, +97, +97, +98,
0, 32, 245, 244, 246, 96, 100, 116, 101, 115, 116, 130, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101,
109, 32, 65, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101, 109, 32, 66,
]),
},
];
Expand Down

0 comments on commit 0da4481

Please sign in to comment.