Skip to content

Instantly share code, notes, and snippets.

@SwadicalRag
Last active June 12, 2023 08:34
Show Gist options
  • Save SwadicalRag/c7d862a6bcebd2872d7f98bff4bd87b7 to your computer and use it in GitHub Desktop.
Save SwadicalRag/c7d862a6bcebd2872d7f98bff4bd87b7 to your computer and use it in GitHub Desktop.
Obj2Obj for message passing between electron/node
/**
* @typedef {(EncodedValue|EncodedDate|EncodedArray|EncodedMap|EncodedSet|EncodedObject|EncodedReference)} EncodedObj
*/
/**
* @typedef {{type: "string"|"number"|"boolean"|"undefined", val: string|number|boolean|undefined}} EncodedValue
*/
/**
* @typedef {{type: "date", val: string, ref: number}} EncodedDate
*/
/**
* @typedef {{type: "array", val: EncodedObj[], ref: number}} EncodedArray
*/
/**
* @typedef {{type: "map", val: {key: EncodedObj, val: EncodedObj}[], ref: number}} EncodedMap
*/
/**
* @typedef {{type: "set", val: EncodedObj[], ref: number}} EncodedSet
*/
/**
* @typedef {{type: "object", val: {key: string, val: EncodedObj}[], ref: number}} EncodedObject
*/
/**
* @typedef {{type: "ref", val: number}} EncodedReference
*/
class Obj2Obj {
/**
* Encodes any object into an EncodedObj.
* @param {*} obj - The object to encode.
* @param {Map<*, number>=} refs - The reference map.
* @param {Set<*>=} blocklist - Entities to omit during encoding.
* @return {EncodedObj} - The encoded object.
*/
static Encode(obj, refs, blocklist) {
const typeID = typeof obj;
refs = refs || new Map();
// different encoding strategies depending on the object type
if(typeID === "object") {
if(blocklist && blocklist.has(obj)) {
return {type: "undefined",val: undefined};
}
else if(refs.has(obj)) {
return {
type: "ref",
val: refs.get(obj),
}
}
else if(Array.isArray(obj)) {
let out = [];
let ref = refs.size;
refs.set(obj,ref);
for(let val of obj) {
out.push(this.Encode(val,refs,blocklist));
}
return {
type: "array",
val: out,
ref,
}
}
else if(obj instanceof Date) {
let ref = refs.size;
refs.set(obj,ref);
return {
type: "date",
val: obj.toISOString(),
ref,
}
}
else if(obj instanceof Map) {
let out = [];
let ref = refs.size;
refs.set(obj,ref);
for(let [key,val] of obj) {
out.push({
key: this.Encode(key,refs,blocklist),
val: this.Encode(val,refs,blocklist),
});
}
return {
type: "map",
val: out,
ref,
}
}
else if(obj instanceof Set) {
let out = [];
let ref = refs.size;
refs.set(obj,ref);
for(let val of obj) {
out.push(this.Encode(val,refs,blocklist));
}
return {
type: "set",
val: out,
ref,
}
}
else {
let out = [];
let ref = refs.size;
refs.set(obj,ref);
for(let key in obj) {
out.push({
key: key,
val: this.Encode(obj[key],refs,blocklist),
});
}
return {
type: "object",
val: out,
ref,
}
}
}
else if(typeID === "symbol" || typeID === "function" || typeID === "bigint") {
return {type: "undefined", val: undefined};
}
else {
// encoding for string, number, boolean and undefined types
return {
type: typeID,
val: obj,
};
}
}
/**
* Decodes an EncodedObj into its original form.
* @param {EncodedObj} obj - The EncodedObj to decode.
* @param {Map<number, *>=} refs - The reference map.
* @return {*} - The decoded object.
*/
static Decode(obj, refs) {
refs = refs || new Map();
// different decoding strategies depending on the EncodedObj type
switch(obj.type) {
case "array": {
let out = [];
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.push(this.Decode(entry,refs));
}
return out;
}
case "date": {
let out = new Date(obj.val);
refs.set(obj.ref,out);
return out;
}
case "map": {
let out = new Map();
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.set(this.Decode(entry.key,refs),this.Decode(entry.val,refs));
}
return out;
}
case "object": {
let out = {};
refs.set(obj.ref,out);
for(let entry of obj.val) {
out[entry.key] = this.Decode(entry.val,refs);
}
return out;
}
case "ref": {
return refs.get(obj.val);
}
case "set": {
let out = new Set();
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.add(this.Decode(entry,refs));
}
return out;
}
default: {
// decoding for value types
return obj.val;
}
}
}
}
interface EncodedValue {
type: "string" | "number" | "boolean" | "undefined";
val: string | number | boolean | undefined;
}
interface EncodedDate {
type: "date";
val: string;
ref: number;
}
interface EncodedArray {
type: "array";
val: EncodedObj[];
ref: number;
}
interface EncodedMap {
type: "map";
val: {key: EncodedObj, val: EncodedObj}[];
ref: number;
}
interface EncodedSet {
type: "set";
val: EncodedObj[];
ref: number;
}
interface EncodedObject {
type: "object";
val: {key: string, val: EncodedObj}[];
ref: number;
}
interface EncodedReference {
type: "ref";
val: number;
}
export type EncodedObj = EncodedValue | EncodedDate | EncodedArray | EncodedMap | EncodedSet | EncodedObject | EncodedReference;
export class Obj2Obj {
static Encode(obj: any, refs?: Map<any, number>, blocklist?: Set<any>): EncodedObj {
const typeID = typeof obj;
refs = refs ?? new Map();
if(typeID === "object") {
if(blocklist && blocklist.has(obj)) {
return {type: "undefined",val: undefined};
}
else if(refs.has(obj)) {
return {
type: "ref",
val: refs.get(obj)!,
}
}
else if(Array.isArray(obj)) {
let out: EncodedArray["val"] = [];
let ref = refs.size;
refs.set(obj,ref);
for(let val of obj) {
out.push(this.Encode(val,refs,blocklist));
}
return {
type: "array",
val: out,
ref,
}
}
else if(obj instanceof Date) {
let ref = refs.size;
refs.set(obj,ref);
return {
type: "date",
val: obj.toISOString(),
ref,
}
}
else if(obj instanceof Map) {
let out: EncodedMap["val"] = [];
let ref = refs.size;
refs.set(obj,ref);
for(let [key,val] of obj) {
out.push({
key: this.Encode(key,refs,blocklist),
val: this.Encode(val,refs,blocklist),
});
}
return {
type: "map",
val: out,
ref,
}
}
else if(obj instanceof Set) {
let out = [];
let ref = refs.size;
refs.set(obj,ref);
for(let val of obj) {
out.push(this.Encode(val,refs,blocklist));
}
return {
type: "set",
val: out,
ref,
}
}
else {
let out: EncodedObject["val"] = [];
let ref = refs.size;
refs.set(obj,ref);
for(let key in obj) {
out.push({
key: key,
val: this.Encode(obj[key],refs,blocklist),
});
}
return {
type: "object",
val: out,
ref,
}
}
}
else if(typeID === "symbol") {
return {type: "undefined", val: undefined};
}
else if(typeID === "function") {
return {type: "undefined", val: undefined};
}
else if(typeID === "bigint") {
return {type: "undefined", val: undefined};
}
else {
return {
type: typeID,
val: obj,
};
}
}
static Decode(obj: EncodedObj, refs?: Map<number, any>): any {
refs = refs ?? new Map();
switch(obj.type) {
case "array": {
let out: any[] = [];
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.push(this.Decode(entry,refs));
}
return out;
}
case "date": {
let out = new Date(obj.val);
refs.set(obj.ref,out);
return out;
}
case "map": {
let out = new Map();
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.set(this.Decode(entry.key,refs),this.Decode(entry.val,refs));
}
return out;
}
case "object": {
let out: any = {};
refs.set(obj.ref,out);
for(let entry of obj.val) {
out[entry.key] = this.Decode(entry.val,refs);
}
return out;
}
case "ref": {
return refs.get(obj.val)!;
}
case "set": {
let out = new Set();
refs.set(obj.ref,out);
for(let entry of obj.val) {
out.add(this.Decode(entry,refs));
}
return out;
}
default: {
return obj.val;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment