feat(serialize, parse): Number, String, Array, ConstString, ConstArray
This commit is contained in:
147
src/index.js
Normal file
147
src/index.js
Normal file
@ -0,0 +1,147 @@
|
||||
import { limits } from "./limits"
|
||||
import { memcpy } from "./mem"
|
||||
import { ConstArray, ConstString } from "./type"
|
||||
|
||||
export { limits, memcpy, ConstString, ConstArray}
|
||||
|
||||
export function serialize(dv, src, ...types) {
|
||||
const [type, ...inner_types] = types
|
||||
|
||||
if (type == Number && typeof src == 'number') {
|
||||
if (dv.byteLength < 8) {
|
||||
throw new Error('too small buffer')
|
||||
}
|
||||
dv.setFloat64(0, src)
|
||||
return
|
||||
}
|
||||
if ((type == String || type._func == ConstString) && typeof src == 'string') {
|
||||
const encoder = new TextEncoder('utf-8')
|
||||
let encoded
|
||||
if (type == String) {
|
||||
encoded = new DataView(encoder.encode(src).buffer)
|
||||
if (dv.byteLength < 4 + encoded.byteLength) {
|
||||
throw new Error('too small buffer')
|
||||
}
|
||||
} else {
|
||||
encoded = new DataView(encoder.encode(src).buffer, 0, type._size)
|
||||
if (dv.byteLength < encoded.byteLength) {
|
||||
throw new Error('too small buffer')
|
||||
}
|
||||
}
|
||||
if (encoded.byteLength > limits.u32.MAX_VALUE) {
|
||||
throw new Error('string is too long')
|
||||
}
|
||||
|
||||
dv.setUint32(0, encoded.byteLength)
|
||||
|
||||
const frame = new DataView(dv.buffer, dv.byteOffset + 4)
|
||||
memcpy(frame, encoded)
|
||||
return
|
||||
}
|
||||
if ((type == Array || type._func == ConstArray) && Array.isArray(src)) {
|
||||
|
||||
const item_size = sizeofHead(src[0])
|
||||
let size
|
||||
|
||||
if (type == Array) {
|
||||
size = src.length
|
||||
if (dv.byteLength < 4 + size * item_size) {
|
||||
throw new Error('too small buffer')
|
||||
}
|
||||
} else {
|
||||
size = type._size
|
||||
if (dv.byteLength < size * item_size) {
|
||||
throw new Error('too small buffer')
|
||||
}
|
||||
}
|
||||
if (size > limits.u32.MAX_VALUE) {
|
||||
throw new Error('array is too long')
|
||||
}
|
||||
|
||||
dv.setUint32(0, size)
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
const item_frame = new DataView(dv.buffer, dv.byteOffset + item_size * i)
|
||||
serialize(item_frame, src[i], ...inner_types)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
export function parse(dv, ...types) {
|
||||
const [type, ...inner_types] = types
|
||||
|
||||
if (type == Number) {
|
||||
return dv.getFloat64(0)
|
||||
}
|
||||
if (type == String || type._func == ConstString) {
|
||||
let size
|
||||
let frame
|
||||
if (type == String) {
|
||||
size = dv.getUint32(0)
|
||||
frame = new DataView(dv.buffer, 4 + dv.byteOffset, size)
|
||||
} else {
|
||||
size = type._size
|
||||
frame = new DataView(dv.buffer, dv.byteOffset, size)
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
return decoder.decode(frame)
|
||||
}
|
||||
if (type == Array || type._func == ConstArray) {
|
||||
let size
|
||||
let offset = 0
|
||||
if (type == Array) {
|
||||
size = dv.getUint32(0)
|
||||
offset = 4
|
||||
} else {
|
||||
size = type._size
|
||||
}
|
||||
const item_size = sizeofHead(inner_types[0])
|
||||
const array = Array(size)
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
const item_frame = new DataView(dv.buffer, dv.byteOffset + offset + item_size * i)
|
||||
array[i] = parse(item_frame, ...inner_types)
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
export function isHeadless(...args) {
|
||||
const [first_arg] = args
|
||||
return first_arg == Array ||
|
||||
first_arg == String ||
|
||||
Array.isArray(first_arg) ||
|
||||
typeof first_arg == 'string'
|
||||
}
|
||||
|
||||
export function sizeofHead(...args) {
|
||||
if (isHeadless(...args)) {
|
||||
return 4
|
||||
} else {
|
||||
return sizeof(...args)
|
||||
}
|
||||
}
|
||||
|
||||
export function sizeof(...args) {
|
||||
const [first_arg, ...remain_args] = args
|
||||
|
||||
if (first_arg == Number || typeof first_arg == 'number') {
|
||||
return 8
|
||||
}
|
||||
if (first_arg._func == ConstArray) {
|
||||
return sizeofHead(...remain_args) * first_arg._size
|
||||
}
|
||||
if (first_arg._func == ConstString) {
|
||||
return first_arg._size
|
||||
}
|
||||
if (typeof first_arg == 'string') {
|
||||
const encoder = new TextEncoder('utf-8')
|
||||
return 4 + encoder.encode(first_arg).byteLength
|
||||
}
|
||||
if (Array.isArray(first_arg)) {
|
||||
return 4 + sizeofHead(first_arg[0]) * first_arg.length
|
||||
}
|
||||
throw new Error('unknown size of ' + args)
|
||||
}
|
||||
69
src/limits.js
Normal file
69
src/limits.js
Normal file
@ -0,0 +1,69 @@
|
||||
const i8 = {
|
||||
MIN_VALUE: -128,
|
||||
MAX_VALUE: 127,
|
||||
}
|
||||
const i16 = {
|
||||
MIN_VALUE: -32768,
|
||||
MAX_VALUE: 32767,
|
||||
}
|
||||
const i32 = {
|
||||
MIN_VALUE: -2147483648,
|
||||
MAX_VALUE: 2147483647,
|
||||
}
|
||||
const i64 = {
|
||||
MIN_VALUE: -9223372036854775808n,
|
||||
MAX_VALUE: 9223372036854775807n,
|
||||
}
|
||||
const u8 = {
|
||||
MIN_VALUE: 0,
|
||||
MAX_VALUE: 255,
|
||||
}
|
||||
const u16 = {
|
||||
MIN_VALUE: 0,
|
||||
MAX_VALUE: 65535,
|
||||
}
|
||||
const u32 = {
|
||||
MIN_VALUE: 0,
|
||||
MAX_VALUE: 4294967295,
|
||||
}
|
||||
const u64 = {
|
||||
MIN_VALUE: 0n,
|
||||
MAX_VALUE: 18446744073709551615n,
|
||||
}
|
||||
const bool = {
|
||||
MIN_VALUE: 0,
|
||||
MAX_VALUE: 1,
|
||||
}
|
||||
const f32 = {
|
||||
MIN_VALUE: -3.40282347e+38,
|
||||
MAX_VALUE: 3.40282347e+38,
|
||||
MIN_NORMAL_VALUE: 1.17549435e-38,
|
||||
MIN_SAFE_INTEGER: -16777215,
|
||||
MAX_SAFE_INTEGER: 16777215,
|
||||
EPSILON: 1.19209290e-07,
|
||||
}
|
||||
const f64 = {
|
||||
MIN_VALUE: -1.7976931348623157e+308,
|
||||
MAX_VALUE: 1.7976931348623157e+308,
|
||||
MIN_NORMAL_VALUE: 2.2250738585072014e-308,
|
||||
MIN_SAFE_INTEGER: -9007199254740991,
|
||||
MAX_SAFE_INTEGER: 9007199254740991,
|
||||
EPSILON: 2.2204460492503131e-16,
|
||||
}
|
||||
export const limits = {
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
bool,
|
||||
f32,
|
||||
f64,
|
||||
}
|
||||
for (const limit of Object.values(limits)) {
|
||||
Object.freeze(limit)
|
||||
}
|
||||
Object.freeze(limits)
|
||||
12
src/mem.js
Normal file
12
src/mem.js
Normal file
@ -0,0 +1,12 @@
|
||||
export function memcpy(dest, src) {
|
||||
const min_size = dest.byteLength > src.byteLength ?
|
||||
src.byteLength : dest.byteLength
|
||||
|
||||
for (let i = 0; i < Math.floor(min_size / 8); i++) {
|
||||
dest.setFloat64(i * 8, src.getFloat64(i * 8))
|
||||
}
|
||||
|
||||
for (let i = min_size - min_size % 8; i < min_size; i++) {
|
||||
dest.setInt8(i, src.getInt8(i))
|
||||
}
|
||||
}
|
||||
18
src/type.js
Normal file
18
src/type.js
Normal file
@ -0,0 +1,18 @@
|
||||
export class Type {
|
||||
constructor (func, obj) {
|
||||
this._func = func
|
||||
Object.assign(this, obj)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this._func.name
|
||||
}
|
||||
}
|
||||
|
||||
export function ConstString(size) {
|
||||
return new Type(ConstString, { _size: size })
|
||||
}
|
||||
|
||||
export function ConstArray(size) {
|
||||
return new Type(ConstArray, { _size: size })
|
||||
}
|
||||
Reference in New Issue
Block a user