jsonbinpack
Version:
A space-efficient schema-driven binary JSON serialization format based on JSON Schema
109 lines (92 loc) • 3.12 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 {
UINT8_MIN,
UINT8_MAX,
UINT3_MIN,
UINT3_MAX,
UINT5_MIN,
UINT5_MAX
} from '../../utils/limits'
// IMPORTANT! This enumeration is capped to have a maximum of 8 elements as
// we use the lowest-significant 3 bits to store the type, and the remaining
// 5 bits to store extra metadata about the type.
export enum Type {
SharedString = 0b00000000,
String = 0b00000001,
LongString = 0b00000010,
Object = 0b00000011,
Array = 0b00000100,
PositiveIntegerByte = 0b00000101,
NegativeIntegerByte = 0b00000110,
Other = 0b00000111
}
export enum Subtype {
False = 0b00000000,
True = 0b00000001,
Null = 0b00000010,
PositiveInteger = 0b00000011,
NegativeInteger = 0b00000100,
Number = 0b00000101,
// Note that the binary values actually match the exponents
LongStringBaseExponent7 = 0b00000111,
LongStringBaseExponent8 = 0b00001000,
LongStringBaseExponent9 = 0b00001001,
LongStringBaseExponent10 = 0b00001010
}
export const LONG_STRING_BASE_EXPONENT_MINIMUM: number = 7
export const LONG_STRING_BASE_EXPONENT_MAXIMUM: number = 10
export const isType = (type: Type, value: number): boolean => {
assert(type >= UINT3_MIN && type <= UINT3_MAX)
assert(value >= UINT8_MIN && value <= UINT8_MAX)
return (value & 0b00000111) === type
}
export const getTypeTag = (type: Type, metadata: number): number => {
assert(type >= UINT3_MIN && type <= UINT3_MAX)
assert(metadata >= UINT5_MIN && metadata <= UINT5_MAX)
return (metadata << 3) | type
}
export const getMetadata = (value: number): number => {
assert(value >= UINT8_MIN && value <= UINT8_MAX)
return value >>> 3
}
export const isTrue = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.True
}
export const isFalse = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.False
}
export const isNull = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.Null
}
export const isPositiveInteger = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.PositiveInteger
}
export const isNegativeInteger = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.NegativeInteger
}
export const isNumber = (value: number): boolean => {
return isType(Type.Other, value) &&
getMetadata(value) === Subtype.Number
}