UNPKG

jexidb

Version:

JexiDB is a pure JS NPM library for managing data on disk using JSONL efficiently, without the need for a server.

120 lines (114 loc) 3.4 kB
import { EventEmitter } from 'events' import zlib from 'zlib' import v8 from 'v8' export default class Serializer extends EventEmitter { constructor(opts = {}) { super() this.opts = Object.assign({}, opts) this.brotliOptions = { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 4 } } } async serialize(data, opts={}) { let line let header = 0x00 // 1 byte de header const useV8 = (this.opts.v8 && opts.json !== true) || opts.v8 === true const compress = (this.opts.compress && opts.compress !== false) || opts.compress === true const addLinebreak = opts.linebreak !== false || (!useV8 && !compress && opts.linebreak !== false) if (useV8) { header |= 0x02 // set V8 line = v8.serialize(data) } else { const json = JSON.stringify(data) line = Buffer.from(json, 'utf-8') } if (compress) { let err const compressionType = useV8 ? 'deflate' : 'brotli' const buffer = await this.compress(line, compressionType).catch(e => err = e) if(!err && buffer.length && buffer.length < line.length) { header |= 0x01 line = buffer } } const totalLength = 1 + line.length + (addLinebreak ? 1 : 0) const result = Buffer.alloc(totalLength) result[0] = header line.copy(result, 1, 0, line.length) if(addLinebreak) { result[result.length - 1] = 0x0A } return result } async deserialize(data) { if(data.length === 0) { return null } let line, isCompressed, isV8 const header = data.readUInt8(0) const valid = header === 0x00 || header === 0x01 || header === 0x02 || header === 0x03 if(valid) { isCompressed = (header & 0x01) === 0x01 isV8 = (header & 0x02) === 0x02 line = data.subarray(1) // remove byte header } else { isCompressed = isV8 = false try { return JSON.parse(data.toString('utf-8').trim()) } catch (e) { throw new Error('Failed to deserialize JSON data') } } if (isCompressed) { const compressionType = isV8 ? 'deflate' : 'brotli' line = await this.decompress(line, compressionType).catch(e => err = e) } if (isV8) { try { return v8.deserialize(line) } catch (e) { throw new Error('Failed to deserialize V8 data') } } else { try { return JSON.parse(line.toString('utf-8').trim()) } catch (e) { throw new Error('Failed to deserialize JSON data') } } } compress(data, type) { return new Promise((resolve, reject) => { const callback = (err, buffer) => { if (err) { reject(err) } else { resolve(buffer) } } if(type === 'brotli') { zlib.brotliCompress(data, this.brotliOptions, callback) } else { zlib.deflate(data, callback) } }) } decompress(data, type) { return new Promise((resolve, reject) => { const callback = (err, buffer) => { if (err) { reject(err) } else { resolve(buffer) } } if(type === 'brotli') { zlib.brotliDecompress(data, callback) } else { zlib.inflate(data, callback) } }) } }