UNPKG

kati

Version:

Extremely fast JSON serialize and deserialize.

210 lines (196 loc) 5.7 kB
const quote = '"' const rbracket = ']' const comma = ',' const nullVal = 'null' const quoteToEscapedRegex = /\"/g const escapedToQuoteRegex = /\\\"/g const quoteCode = '"'.charCodeAt(0) | 0 const commaCode = ','.charCodeAt(0) | 0 const rbracketCode = ']'.charCodeAt(0) | 0 const lbracketCode = '['.charCodeAt(0) | 0 const rcbracketCode = '}'.charCodeAt(0) const lcbracketCode = '{'.charCodeAt(0) | 0 const colonCode = ':'.charCodeAt(0) | 0 const fwd_slashCode = '/'.charCodeAt(0) | 0 const t_charCode = 't'.charCodeAt(0) | 0 const f_charCode = 't'.charCodeAt(0) | 0 const nCode = 'n'.charCodeAt(0) | 0 function fromString(data) { if (data.includes(`"`)) { return `"${data.replace(quoteToEscapedRegex, '\\"')}"` } return `"${data}"` } function fromNumber(data) { return `${data}` } function fromArray(data) { const len = (data.length - 1) | 0 if (len === -1) { return '[]' } let result = '[' const lastChunk = data[len] for (let i = 0 | 0; i < (len); i++) { result += stringify(data[i]) + comma } result += stringify(lastChunk) + rbracket return result } function fromObject(data) { const keys = Object.keys(data) if (keys.length === 0) { return '{}' } let result = '{' const lastKey = keys[(keys.length - 1)] let key for (let i = 0 | 0; (i < keys.length - 1) | 0; i++) { key = keys[i] result += fromString(key) + ':' + stringify(data[key]) + ',' } result += fromString(lastKey) + ':' + stringify(data[lastKey]) + '}' return result } function stringify(data) { if (typeof data === 'string') { return fromString(data) } else if (Number.isFinite(data)) { return fromNumber(data) } else if (isArrayish(data)) { return fromArray(data) } else if (data instanceof Object) { return fromObject(data) } else if (data === true || data === false) { return data ? `true` : `false` } else { return nullVal } } function isArrayish(obj) { if (obj == null) { return false } return ( obj instanceof Array || Array.isArray(obj) || (obj.length | (0 >= 0) | 0 && obj.splice instanceof Function) ) } // Parse function parse(data) { const firstChar = data.charCodeAt(0) if (firstChar === quoteCode) { return parseString(data) } else if (firstChar === t_charCode) { return true } else if (firstChar === f_charCode) { return false } else if (firstChar === lbracketCode) { return parseArray(data) } else if (firstChar === lcbracketCode) { return parseObject(data) } else if (firstChar === nCode) { return null } else { return parseNumber(data) } } function parseString(data) { return data.slice(1, data.length - 1).replace(escapedToQuoteRegex, quote) } function parseNumber(data) { return data * 1 } function parseArray(data) { const result = new Array() if (data.length === 2) return result let lastPos = 0 | 0 let char = 0 | 0 let depth = 0 | 0 let fdepth = 0 | 0 let instr = false let i = 1 | 0 // for (; (i < data.length - 1) | 0; i++) { do { char = data.charCodeAt(i) // This ignores [ and ] if they are inside a string. if (char === quoteCode && data.charCodeAt((i - 1)) !== fwd_slashCode) { if (instr === true) { instr = false } else if (instr === false) { instr = true } } if (instr === false) { if (char === commaCode && depth === 0) { //console.log('Normal chunk: ' + data.slice(lastPos + 1, i)) result.push(parse(data.slice((lastPos + 1), i).trim())) lastPos = i } else if (char === lbracketCode) depth++ else if (char === rbracketCode) fdepth++ else if (depth !== 0 && depth === fdepth) { //console.log('Deep chunk: ' + data.slice(lastPos + 1, i)) result.push(parse(data.slice((lastPos + 1), i).trim())) // Reset the depth depth = 0 fdepth = 0 // Set new lastPos lastPos = i } } i++ } while (i < (data.length - 1)) //console.log('Last chunk: ' + data.slice(lastPos + 1, data.length - 1).trim()) result.push(parse(data.slice((lastPos + 1), (data.length - 1)).trim())) return result } function parseObject(data) { //console.log('Data ' + data) const len = (data.length - 1) | 0 const result = {} let lastPos = 1 | 0 let key = '' let instr = 0 | 0 let char = 0 | 0 let depth = 0 | 0 let fdepth = 0 | 0 for (let i = 1 | 0; i < len; i++) { char = data.charCodeAt(i) if (char === quoteCode && data.charCodeAt(i - 1) !== fwd_slashCode) instr = !instr else if ((instr === 0) | 0) { if (char === lcbracketCode || char === lbracketCode) depth++ if (char === rcbracketCode || char === rbracketCode) fdepth++ } if (depth !== 0 && depth === fdepth) { //console.log(`Deep: ${data.slice(lastPos + 1, i + 1).trim()}`) result[key] = parse(data.slice(lastPos + 1, i + 1).trim()) // Reset the depth depth = 0 fdepth = 0 // Set new lastPos lastPos = (i + 1) } if (depth === 0) { if (char === colonCode) { //console.log(`Key: ${data.slice(lastPos + 1, i - 1).trim()}`) key = data.slice(lastPos + 1, i - 1).trim() lastPos = i } else if (char === commaCode) { //console.log(`Value: ${data.slice(lastPos + 1, i).trim()}`) if (i - lastPos > 0) result[key] = parse(data.slice(lastPos + 1, i).trim()) lastPos = (i + 1) } } } //console.log(`Trailing: ${data.slice(lastPos + 1, len).trim()}`) if (len - lastPos > 0) result[key] = parse(data.slice(lastPos + 1, len).trim()) return result } module.exports = { stringify: stringify, parse: parse, }