Skip to content

Commit

Permalink
Add support for Blob
Browse files Browse the repository at this point in the history
- Fix string serialization
- Fix dictionary key serialization
  • Loading branch information
lxsmnsyc committed Apr 9, 2023
1 parent 2bd8327 commit ec017d7
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 61 deletions.
53 changes: 18 additions & 35 deletions packages/seroval/src/string.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,30 @@
export function serializeChar(str: string) {
const SAFE_SERIALIZE_CHECK = /<|\u2028|\u2029/;

export function serializeChar(str: string): string {
switch (str) {
case '"': return '\\"';
case '\\': return '\\\\';
case '<': return '\\x3C';
case '\n': return '\\n';
case '\r': return '\\r';
case '\u2028': return '\\u2028';
case '\u2029': return '\\u2029';
default: return undefined;
case '<':
return '\\x3C';
case '\u2028':
return '\\u2028';
case '\u2029':
return '\\u2029';
default:
return str;
}
}

// Written by https://github.com/DylanPiercey and is distributed under the MIT license.
// Creates a JavaScript double quoted string and escapes all characters
// not listed as DoubleStringCharacters on
// Also includes "<" to escape "</script>" and "\" to avoid invalid escapes in the output.
// http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
export function serializeString(str: string) {
let result = '';
let lastPos = 0;
let replacement: string | undefined;
for (let i = 0, len = str.length; i < len; i++) {
replacement = serializeChar(str[i]);
if (replacement) {
result += str.slice(lastPos, i) + replacement;
lastPos = i + 1;
}
}
if (lastPos === 0) {
result = str;
} else {
result += str.slice(lastPos);
const safe = JSON.stringify(str);
const unquoted = safe.substring(1, safe.length - 1);
if (SAFE_SERIALIZE_CHECK.test(unquoted)) {
return unquoted.replace(/<|\u2028|\u2029/g, serializeChar);
}
return result;
return unquoted;
}

export function deserializeString(str: string): string {
return str
.replace(/\\"/g, '"')
.replace(/\\\\/g, '\\')
.replace(/\\x3C/g, '<')
.replace(/\\n/g, '\n')
.replace(/\\r/g, '\r')
const restored = str.replace(/\\x3C/g, '<')
.replace(/\\u2028/g, '\u2028')
.replace(/\\u2029/g, '\u2029');
return JSON.parse('"' + restored + '"') as string;
}
10 changes: 7 additions & 3 deletions packages/seroval/src/tree/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import {
SerovalPromiseNode,
SerovalSetNode,
} from './types';
import { createURLNode, createURLSearchParamsNode } from './web-api';
import { createBlobNode, createURLNode, createURLSearchParamsNode } from './web-api';

type ObjectLikeNode =
| SerovalObjectNode
Expand Down Expand Up @@ -203,14 +203,16 @@ async function generateProperties(
let deferredSize = 0;
let nodesSize = 0;
let item: unknown;
let escaped: string;
for (const key of keys) {
item = properties[key];
escaped = serializeString(key);
if (isIterable(item)) {
deferredKeys[deferredSize] = key;
deferredKeys[deferredSize] = escaped;
deferredValues[deferredSize] = item;
deferredSize++;
} else {
keyNodes[nodesSize] = key;
keyNodes[nodesSize] = escaped;
valueNodes[nodesSize] = await parse(ctx, item);
nodesSize++;
}
Expand Down Expand Up @@ -454,6 +456,8 @@ async function parse<T>(
return createURLNode(ctx, id, current as unknown as URL);
case URLSearchParams:
return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
case Blob:
return createBlobNode(ctx, id, current as unknown as Blob);
default:
break;
}
Expand Down
21 changes: 18 additions & 3 deletions packages/seroval/src/tree/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SerovalArrayBufferNode,
SerovalArrayNode,
SerovalBigIntTypedArrayNode,
SerovalBlobNode,
SerovalDataViewNode,
SerovalErrorNode,
SerovalIterableNode,
Expand Down Expand Up @@ -74,7 +75,7 @@ function deserializeProperties(
return {};
}
for (let i = 0; i < node.s; i++) {
result[node.k[i]] = deserializeTree(ctx, node.v[i]);
result[deserializeString(node.k[i])] = deserializeTree(ctx, node.v[i]);
}
return result;
}
Expand Down Expand Up @@ -254,6 +255,18 @@ function deserializeDataView(
return result;
}

function deserializeBlob(
ctx: SerializationContext,
node: SerovalBlobNode,
) {
const source = deserializeTree(ctx, node.f) as ArrayBuffer;
const result = assignIndexedValue(ctx, node.i, new Blob(
[source],
{ type: node.c },
));
return result;
}

export default function deserializeTree(
ctx: SerializationContext,
node: SerovalNode,
Expand Down Expand Up @@ -312,11 +325,13 @@ export default function deserializeTree(
case SerovalNodeType.WKSymbol:
return SYMBOL_REF[node.s];
case SerovalNodeType.URL:
return assignIndexedValue(ctx, node.i, new URL(node.s));
return assignIndexedValue(ctx, node.i, new URL(deserializeString(node.s)));
case SerovalNodeType.URLSearchParams:
return assignIndexedValue(ctx, node.i, new URLSearchParams(node.s));
return assignIndexedValue(ctx, node.i, new URLSearchParams(deserializeString(node.s)));
case SerovalNodeType.Reference:
return assignIndexedValue(ctx, node.i, getReference(deserializeString(node.s)));
case SerovalNodeType.Blob:
return deserializeBlob(ctx, node);
default:
throw new Error('Unsupported type');
}
Expand Down
5 changes: 2 additions & 3 deletions packages/seroval/src/tree/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export function createArrayBufferNode(
};
}

function serializeArrayBuffer(
export function serializeArrayBuffer(
ctx: ParserContext,
current: ArrayBuffer,
) {
Expand Down Expand Up @@ -283,7 +283,6 @@ export function createBigIntTypedArrayNode(
current: BigIntTypedArrayValue,
): SerovalBigIntTypedArrayNode {
const constructor = current.constructor.name;
console.log(ctx.features.toString(2), BIGINT_FLAG.toString(2), (ctx.features & BIGINT_FLAG).toString(2));
assert(
(ctx.features & BIGINT_FLAG) === BIGINT_FLAG,
`Unsupported value type "${constructor}"`,
Expand Down Expand Up @@ -357,4 +356,4 @@ export function createDataViewNode(
f: serializeArrayBuffer(ctx, current.buffer),
b: current.byteOffset,
};
}
}
22 changes: 16 additions & 6 deletions packages/seroval/src/tree/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
getRefParam,
markRef,
} from '../context';
import { serializeString } from '../string';
import { GLOBAL_KEY } from './reference';
import { isValidIdentifier } from './shared';
import { SYMBOL_STRING } from './symbols';
Expand All @@ -28,6 +27,7 @@ import {
SerovalTypedArrayNode,
SerovalArrayBufferNode,
SerovalDataViewNode,
SerovalBlobNode,
} from './types';

function getAssignmentExpression(assignment: Assignment): string {
Expand Down Expand Up @@ -287,11 +287,11 @@ function serializeProperties(
if (isIdentifier && Number.isNaN(check)) {
createObjectAssign(ctx, sourceID, key, refParam);
} else {
createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + serializeString(key) + '"'), refParam);
createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
}
} else {
result += (hasPrev ? ',' : '')
+ (isIdentifier ? key : ('"' + serializeString(key) + '"'))
+ (isIdentifier ? key : ('"' + key + '"'))
+ ':' + serializeTree(ctx, val);
hasPrev = true;
}
Expand Down Expand Up @@ -341,7 +341,7 @@ function serializeAssignments(
if (isIdentifier && Number.isNaN(check)) {
createObjectAssign(ctx, sourceID, key, refParam);
} else {
createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + serializeString(key) + '"'), refParam);
createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
}
ctx.assignments = parentAssignment;
}
Expand Down Expand Up @@ -481,7 +481,7 @@ function serializeAggregateError(
) {
// Serialize the required arguments
ctx.stack.push(node.i);
const serialized = 'new AggregateError(' + serializeNodeList(ctx, node) + ',"' + serializeString(node.m) + '")';
const serialized = 'new AggregateError(' + serializeNodeList(ctx, node) + ',"' + node.m + '")';
ctx.stack.pop();
// `AggregateError` might've been extended
// either through class or custom properties
Expand All @@ -493,7 +493,7 @@ function serializeError(
ctx: SerializationContext,
node: SerovalErrorNode,
) {
const serialized = 'new ' + node.c + '("' + serializeString(node.m) + '")';
const serialized = 'new ' + node.c + '("' + node.m + '")';
return serializeDictionary(ctx, node.i, node.d, serialized);
}

Expand Down Expand Up @@ -579,6 +579,14 @@ function serializeDataView(
return assignIndexedValue(ctx, node.i, 'new DataView(' + args + ')');
}

function serializeBlob(
ctx: SerializationContext,
node: SerovalBlobNode,
) {
const args = '[' + serializeTree(ctx, node.f) + '],{type:"' + node.c + '"}';
return assignIndexedValue(ctx, node.i, 'new Blob(' + args + ')');
}

export default function serializeTree(
ctx: SerializationContext,
node: SerovalNode,
Expand Down Expand Up @@ -643,6 +651,8 @@ export default function serializeTree(
return assignIndexedValue(ctx, node.i, node.s ? 'new URLSearchParams("' + node.s + '")' : 'new URLSearchParams');
case SerovalNodeType.Reference:
return assignIndexedValue(ctx, node.i, GLOBAL_KEY + '.get("' + node.s + '")');
case SerovalNodeType.Blob:
return assignIndexedValue(ctx, node.i, serializeBlob(ctx, node));
default:
throw new Error('Unsupported type');
}
Expand Down
6 changes: 4 additions & 2 deletions packages/seroval/src/tree/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,16 @@ function generateProperties(
let deferredSize = 0;
let nodesSize = 0;
let item: unknown;
let escaped: string;
for (const key of keys) {
item = properties[key];
escaped = serializeString(key);
if (isIterable(item)) {
deferredKeys[deferredSize] = key;
deferredKeys[deferredSize] = escaped;
deferredValues[deferredSize] = item;
deferredSize++;
} else {
keyNodes[nodesSize] = key;
keyNodes[nodesSize] = escaped;
valueNodes[nodesSize] = parse(ctx, item);
nodesSize++;
}
Expand Down
Loading

0 comments on commit ec017d7

Please sign in to comment.