bserializer
Version:
A TypeScript-based serialization/deserialization library built for exploration and learning.
106 lines (105 loc) • 3.55 kB
JavaScript
import { TYPE_CODES } from "./typecodes.js";
import { Buffer } from "buffer";
import { BufferWriter } from "./bufferWriter.js";
const { STRING, NUMBER, BOOLEAN, NULL, ARRAY, OBJECT, FIXED_POINT, UNDEFINED, BIG_FLOAT_STRING, } = TYPE_CODES;
function getFloatScaleInfo(value) {
const str = value.toString();
const dotIndex = str.indexOf(".");
if (dotIndex === -1)
return null;
const eIndex = str.indexOf("e");
if (eIndex !== -1)
return null; // Skip exponential notation
const fractionalPart = str.substring(dotIndex + 1);
const decimalPlaces = fractionalPart.length;
if (decimalPlaces === 0)
return null;
const scale = 10 ** decimalPlaces;
return { scale, decimalPlaces };
}
function _serializePrimitive(data, buffer, type) {
if (data === null) {
buffer.writeUInt8(NULL);
return;
}
if (data === undefined) {
buffer.writeUInt8(UNDEFINED);
return;
}
switch (type) {
case "string": {
buffer.writeUInt8(STRING);
const strBuf = Buffer.from(data, "utf8");
buffer.writeUInt32LE(strBuf.length);
buffer.writeBuffer(strBuf);
return;
}
case "number": {
const scaleInfo = getFloatScaleInfo(data);
if (scaleInfo) {
const { scale } = scaleInfo;
if (scale > 0xffffffff) {
// Too big to store in UInt32, fall back to stringified float
const str = data.toString();
const strLen = Buffer.byteLength(str, "utf-8");
buffer.writeUInt8(BIG_FLOAT_STRING);
buffer.writeUInt32LE(strLen);
buffer.writeString(str);
return;
}
const scaled = BigInt(Math.round(data * scale));
buffer.writeUInt8(FIXED_POINT);
buffer.writeUInt32LE(scale);
buffer.writeBigInt64LE(scaled);
return;
}
else {
buffer.writeUInt8(NUMBER);
buffer.writeDoubleLE(data);
return;
}
}
case "boolean": {
buffer.writeUInt8(BOOLEAN);
buffer.writeUInt8(data ? 1 : 0);
return;
}
default: {
throw new Error("Unsupported data type: " + type);
}
}
}
function _serializeData(data, buffer) {
const type = typeof data;
if (type !== "object" || data === null || data === undefined) {
_serializePrimitive(data, buffer, type);
return;
}
if (Array.isArray(data)) {
const length = data.length;
buffer.writeUInt8(ARRAY);
buffer.writeUInt32LE(length);
for (let i = 0; i < length; i++) {
_serializeData(data[i], buffer);
}
return;
}
if (type === "object") {
buffer.writeUInt8(OBJECT);
const entries = Object.entries(data);
const entriesLength = entries.length;
buffer.writeUInt32LE(entriesLength);
for (let i = 0; i < entriesLength; i++) {
const keyBuf = Buffer.from(entries[i][0], "utf8");
buffer.writeUInt32LE(keyBuf.length);
buffer.writeBuffer(keyBuf);
_serializeData(entries[i][1], buffer);
}
return;
}
}
export function serializeData(data) {
const buffer = new BufferWriter();
_serializeData(data, buffer);
return buffer.toBuffer();
}