UNPKG

molstar

Version:

A comprehensive macromolecular library.

286 lines (285 loc) 8.62 kB
/* * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from https://github.com/rcsb/mmtf-javascript * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ import { utf8ByteCount, utf8Write } from '../utf8'; export function encodeMsgPack(value) { const buffer = new ArrayBuffer(encodedSize(value)); const view = new DataView(buffer); const bytes = new Uint8Array(buffer); encodeInternal(value, view, bytes, 0); return bytes; } function encodedSize(value) { const type = typeof value; // Raw Bytes if (type === 'string') { const length = utf8ByteCount(value); if (length < 0x20) { return 1 + length; } if (length < 0x100) { return 2 + length; } if (length < 0x10000) { return 3 + length; } if (length < 0x100000000) { return 5 + length; } } if (value instanceof Uint8Array) { const length = value.byteLength; if (length < 0x100) { return 2 + length; } if (length < 0x10000) { return 3 + length; } if (length < 0x100000000) { return 5 + length; } } if (type === 'number') { // Floating Point // double if (Math.floor(value) !== value) return 9; // Integers if (value >= 0) { // positive fixnum if (value < 0x80) return 1; // uint 8 if (value < 0x100) return 2; // uint 16 if (value < 0x10000) return 3; // uint 32 if (value < 0x100000000) return 5; throw new Error('Number too big 0x' + value.toString(16)); } // negative fixnum if (value >= -0x20) return 1; // int 8 if (value >= -0x80) return 2; // int 16 if (value >= -0x8000) return 3; // int 32 if (value >= -0x80000000) return 5; throw new Error('Number too small -0x' + value.toString(16).substr(1)); } // Boolean, null if (type === 'boolean' || value === null || value === void 0) return 1; // Container Types if (type === 'object') { let length, size = 0; if (Array.isArray(value)) { length = value.length; for (let i = 0; i < length; i++) { size += encodedSize(value[i]); } } else { const keys = Object.keys(value); length = keys.length; for (let i = 0; i < length; i++) { const key = keys[i]; size += encodedSize(key) + encodedSize(value[key]); } } if (length < 0x10) { return 1 + size; } if (length < 0x10000) { return 3 + size; } if (length < 0x100000000) { return 5 + size; } throw new Error('Array or object too long 0x' + length.toString(16)); } throw new Error('Unknown type ' + type); } function encodeInternal(value, view, bytes, offset) { const type = typeof value; // Strings Bytes if (type === 'string') { const length = utf8ByteCount(value); // fix str if (length < 0x20) { view.setUint8(offset, length | 0xa0); utf8Write(bytes, offset + 1, value); return 1 + length; } // str 8 if (length < 0x100) { view.setUint8(offset, 0xd9); view.setUint8(offset + 1, length); utf8Write(bytes, offset + 2, value); return 2 + length; } // str 16 if (length < 0x10000) { view.setUint8(offset, 0xda); view.setUint16(offset + 1, length); utf8Write(bytes, offset + 3, value); return 3 + length; } // str 32 if (length < 0x100000000) { view.setUint8(offset, 0xdb); view.setUint32(offset + 1, length); utf8Write(bytes, offset + 5, value); return 5 + length; } } if (value instanceof Uint8Array) { const length = value.byteLength; const bytes = new Uint8Array(view.buffer); // bin 8 if (length < 0x100) { view.setUint8(offset, 0xc4); view.setUint8(offset + 1, length); bytes.set(value, offset + 2); return 2 + length; } // bin 16 if (length < 0x10000) { view.setUint8(offset, 0xc5); view.setUint16(offset + 1, length); bytes.set(value, offset + 3); return 3 + length; } // bin 32 if (length < 0x100000000) { view.setUint8(offset, 0xc6); view.setUint32(offset + 1, length); bytes.set(value, offset + 5); return 5 + length; } } if (type === 'number') { if (!isFinite(value)) { throw new Error('Number not finite: ' + value); } // Floating point if (Math.floor(value) !== value) { view.setUint8(offset, 0xcb); view.setFloat64(offset + 1, value); return 9; } // Integers if (value >= 0) { // positive fixnum if (value < 0x80) { view.setUint8(offset, value); return 1; } // uint 8 if (value < 0x100) { view.setUint8(offset, 0xcc); view.setUint8(offset + 1, value); return 2; } // uint 16 if (value < 0x10000) { view.setUint8(offset, 0xcd); view.setUint16(offset + 1, value); return 3; } // uint 32 if (value < 0x100000000) { view.setUint8(offset, 0xce); view.setUint32(offset + 1, value); return 5; } throw new Error('Number too big 0x' + value.toString(16)); } // negative fixnum if (value >= -0x20) { view.setInt8(offset, value); return 1; } // int 8 if (value >= -0x80) { view.setUint8(offset, 0xd0); view.setInt8(offset + 1, value); return 2; } // int 16 if (value >= -0x8000) { view.setUint8(offset, 0xd1); view.setInt16(offset + 1, value); return 3; } // int 32 if (value >= -0x80000000) { view.setUint8(offset, 0xd2); view.setInt32(offset + 1, value); return 5; } throw new Error('Number too small -0x' + (-value).toString(16).substr(1)); } // null if (value === null || value === undefined) { view.setUint8(offset, 0xc0); return 1; } // Boolean if (type === 'boolean') { view.setUint8(offset, value ? 0xc3 : 0xc2); return 1; } // Container Types if (type === 'object') { let length, size = 0; const isArray = Array.isArray(value); let keys; if (isArray) { length = value.length; } else { keys = Object.keys(value); length = keys.length; } if (length < 0x10) { view.setUint8(offset, length | (isArray ? 0x90 : 0x80)); size = 1; } else if (length < 0x10000) { view.setUint8(offset, isArray ? 0xdc : 0xde); view.setUint16(offset + 1, length); size = 3; } else if (length < 0x100000000) { view.setUint8(offset, isArray ? 0xdd : 0xdf); view.setUint32(offset + 1, length); size = 5; } if (isArray) { for (let i = 0; i < length; i++) { size += encodeInternal(value[i], view, bytes, offset + size); } } else { for (let i = 0, _i = keys.length; i < _i; i++) { const key = keys[i]; size += encodeInternal(key, view, bytes, offset + size); size += encodeInternal(value[key], view, bytes, offset + size); } } return size; } throw new Error('Unknown type ' + type); }