jsonbinpack
Version:
A space-efficient schema-driven binary JSON serialization format based on JSON Schema
87 lines (73 loc) • 2.48 kB
text/typescript
/*
* Copyright 2021 Juan Cruz Viotti
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
strict as assert
} from 'assert'
import fromExponential from 'from-exponential'
import ResizableBuffer from '../resizable-buffer'
import {
zigzagEncode
} from '../integer/zigzag'
import {
varintEncode
} from '../integer/varint'
import {
JSONNumber
} from '../../json'
import {
NoOptions
} from './options'
import {
EncodingContext
} from '../context'
const stringPrefixCount = (value: string, prefix: string): number => {
let count: number = 0
for (const character of value) {
if (character === prefix) {
count += 1
} else {
break
}
}
return count
}
// TODO: More efficient way to get the integral
// const toIntegral = (value, multiplier = 1) => {
// const result = value * multiplier
// if (Number.isInteger(result)) return result
// return toIntegral(value, multiplier * 10)
// }
export const DOUBLE_VARINT_TUPLE = (
buffer: ResizableBuffer, offset: number, value: JSONNumber,
_options: NoOptions, _context: EncodingContext
): number => {
const valueString: string = fromExponential(value)
const pointIndex: number = valueString.startsWith('-')
? valueString.indexOf('.') - 1
: valueString.indexOf('.')
const point: number = pointIndex > 0 ? pointIndex : 0
assert(point >= 0)
const integralString: string = valueString.replace('.', '')
const zeroPrefix: number = stringPrefixCount(integralString.startsWith('-')
? integralString.slice(1) : integralString, '0')
assert(zeroPrefix >= 0)
const integralBytes: number =
varintEncode(buffer, offset, zigzagEncode(BigInt(integralString)))
const pointValue: bigint = zeroPrefix === 0 || zeroPrefix === integralString.length
? zigzagEncode(BigInt(point))
: zigzagEncode(BigInt(-zeroPrefix - (valueString.startsWith('-') ? 1 : 0)))
return integralBytes + varintEncode(buffer, offset + integralBytes, pointValue)
}