let serialize = value => {
let serializeNull = value => {
return `N;`
}
let serializeBool = value => {
return `b:${value?1:0};`
}
let serializeInt = value => {
return `i:${parseInt(value)};`
}
let serializeDouble = value => {
return `d:${value};`
}
let serializeString = value => {
return `s:${value.length}:"${value}";`
}
let serializeArray = value => {
let serialized = `a:${value.length}:{`
value.forEach((val, i) => {
serialized += serialize(i)
if(val === value) {
throw new Error("sorry doesn't support array referencing themselves")
} else {
serialized += serialize(val)
}
})
return serialized + "}"
}
let serializeKeyedArray = value => {
let entries = Object.entries(value)
let serialized = `a:${entries.length}:{`
entries.forEach(entry => {
serialized += serialize(entry[0])
if(entry[1] === value) {
throw new Error("sorry doesn't support array referencing themselves")
} else {
serialized += serialize(entry[1])
}
})
return serialized + "}"
}
let serializeObject = value => {
let entries = Object.entries(value)
let serialized = `O:${value.constructor.name.length}:"${value.constructor.name}":${entries.length}:{`
entries.forEach(([key, val]) => {
serialized += serialize(key)
// if protected key should become \0*\0${key}
// if private key should become \0${value.constructor.name}\0${key}
if(val === value) {
serialized += "r:1;"
} else {
serialized += serialize(val)
}
})
return serialized + "}"
}
switch(typeof value) {
case "number": // int or double
if (""+value == parseInt(""+value)) { return serializeInt(value) }
else { return serializeDouble(value) }
break
case "boolean": // boolean
return serializeBool(value)
break
case "string": // string
return serializeString(value)
break
case "undefined": // undefined
return serializeNull(value)
break
case "object": // Array, primitive object, object from class or null
if (Array.isArray(value)) { return serializeArray(value) }
else if (value === null) { return serializeNull(value) }
else if (value.constructor.name !== "Object") { return serializeObject(value) }
else { return serializeKeyedArray(value) }
break
default: throw new Error("sorry I don't know how to serialize this")
}
}
class TestClass {
constructor() {
this.self = this
this.a = 42
this.b = "How are you ?"
}
}
let tests = [
{expected: `i:1;`, val: 1},
{expected: `i:-1;`, val: -1},
{expected: `d:1;`, val: 1.0},
{expected: `d:1.5;`, val: 1.5},
{expected: `d:-4.2;`, val: -4.2},
{expected: `b:1;`, val: true},
{expected: `b:0;`, val: false},
{expected: `N;`, val: null},
{expected: `N;`, val: undefined},
{expected: `s:4:"test";`, val: "test"},
{expected: `s:5:"hello";`, val: "hello"},
{expected: `a:3:{i:0;i:1;i:1;s:2:"hi";i:2;i:5;}`, val: [1, "hi", 5]},
{expected: `a:3:{s:1:"a";i:1;s:1:"b";s:2:"hi";s:1:"c";i:5;}`, val: {a:1, b:"hi", c:5}},
{expected: `O:9:"TestClass":3:{s:4:"self";r:1;s:1:"a";i:42;s:1:"b";s:13:"How are you ?";}`, val: new TestClass()}
]
tests.forEach(val => {
let serialized = serialize(val.val)
let log = `expected : '${val.expected}', got : '${serialized}', value : ${(()=>{
if(typeof val.val === "object") {
return val.val ? Object.entries(val.val).join(", ") : val.val // null
} else {
return val.val ? val.val.toString() : val.val // undefined
}
})()}`
if(val.expected === serialized) {
console.log(log)
} else {
console.error(log)
}
})