You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
7.0 KiB

// write-type.js
var IS_ARRAY = require("isarray");
var Int64Buffer = require("int64-buffer");
var Uint64BE = Int64Buffer.Uint64BE;
var Int64BE = Int64Buffer.Int64BE;
var Bufferish = require("./bufferish");
var BufferProto = require("./bufferish-proto");
var WriteToken = require("./write-token");
var uint8 = require("./write-uint8").uint8;
var ExtBuffer = require("./ext-buffer").ExtBuffer;
var HAS_UINT8ARRAY = ("undefined" !== typeof Uint8Array);
var HAS_MAP = ("undefined" !== typeof Map);
var extmap = [];
extmap[1] = 0xd4;
extmap[2] = 0xd5;
extmap[4] = 0xd6;
extmap[8] = 0xd7;
extmap[16] = 0xd8;
exports.getWriteType = getWriteType;
function getWriteType(options) {
var token = WriteToken.getWriteToken(options);
var useraw = options && options.useraw;
var binarraybuffer = HAS_UINT8ARRAY && options && options.binarraybuffer;
var isBuffer = binarraybuffer ? Bufferish.isArrayBuffer : Bufferish.isBuffer;
var bin = binarraybuffer ? bin_arraybuffer : bin_buffer;
var usemap = HAS_MAP && options && options.usemap;
var map = usemap ? map_to_map : obj_to_map;
var writeType = {
"boolean": bool,
"function": nil,
"number": number,
"object": (useraw ? object_raw : object),
"string": _string(useraw ? raw_head_size : str_head_size),
"symbol": nil,
"undefined": nil
};
return writeType;
// false -- 0xc2
// true -- 0xc3
function bool(encoder, value) {
var type = value ? 0xc3 : 0xc2;
token[type](encoder, value);
}
function number(encoder, value) {
var ivalue = value | 0;
var type;
if (value !== ivalue) {
// float 64 -- 0xcb
type = 0xcb;
token[type](encoder, value);
return;
} else if (-0x20 <= ivalue && ivalue <= 0x7F) {
// positive fixint -- 0x00 - 0x7f
// negative fixint -- 0xe0 - 0xff
type = ivalue & 0xFF;
} else if (0 <= ivalue) {
// uint 8 -- 0xcc
// uint 16 -- 0xcd
// uint 32 -- 0xce
type = (ivalue <= 0xFF) ? 0xcc : (ivalue <= 0xFFFF) ? 0xcd : 0xce;
} else {
// int 8 -- 0xd0
// int 16 -- 0xd1
// int 32 -- 0xd2
type = (-0x80 <= ivalue) ? 0xd0 : (-0x8000 <= ivalue) ? 0xd1 : 0xd2;
}
token[type](encoder, ivalue);
}
// uint 64 -- 0xcf
function uint64(encoder, value) {
var type = 0xcf;
token[type](encoder, value.toArray());
}
// int 64 -- 0xd3
function int64(encoder, value) {
var type = 0xd3;
token[type](encoder, value.toArray());
}
// str 8 -- 0xd9
// str 16 -- 0xda
// str 32 -- 0xdb
// fixstr -- 0xa0 - 0xbf
function str_head_size(length) {
return (length < 32) ? 1 : (length <= 0xFF) ? 2 : (length <= 0xFFFF) ? 3 : 5;
}
// raw 16 -- 0xda
// raw 32 -- 0xdb
// fixraw -- 0xa0 - 0xbf
function raw_head_size(length) {
return (length < 32) ? 1 : (length <= 0xFFFF) ? 3 : 5;
}
function _string(head_size) {
return string;
function string(encoder, value) {
// prepare buffer
var length = value.length;
var maxsize = 5 + length * 3;
encoder.offset = encoder.reserve(maxsize);
var buffer = encoder.buffer;
// expected header size
var expected = head_size(length);
// expected start point
var start = encoder.offset + expected;
// write string
length = BufferProto.write.call(buffer, value, start);
// actual header size
var actual = head_size(length);
// move content when needed
if (expected !== actual) {
var targetStart = start + actual - expected;
var end = start + length;
BufferProto.copy.call(buffer, buffer, targetStart, start, end);
}
// write header
var type = (actual === 1) ? (0xa0 + length) : (actual <= 3) ? (0xd7 + actual) : 0xdb;
token[type](encoder, length);
// move cursor
encoder.offset += length;
}
}
function object(encoder, value) {
// null
if (value === null) return nil(encoder, value);
// Buffer
if (isBuffer(value)) return bin(encoder, value);
// Array
if (IS_ARRAY(value)) return array(encoder, value);
// int64-buffer objects
if (Uint64BE.isUint64BE(value)) return uint64(encoder, value);
if (Int64BE.isInt64BE(value)) return int64(encoder, value);
// ext formats
var packer = encoder.codec.getExtPacker(value);
if (packer) value = packer(value);
if (value instanceof ExtBuffer) return ext(encoder, value);
// plain old Objects or Map
map(encoder, value);
}
function object_raw(encoder, value) {
// Buffer
if (isBuffer(value)) return raw(encoder, value);
// others
object(encoder, value);
}
// nil -- 0xc0
function nil(encoder, value) {
var type = 0xc0;
token[type](encoder, value);
}
// fixarray -- 0x90 - 0x9f
// array 16 -- 0xdc
// array 32 -- 0xdd
function array(encoder, value) {
var length = value.length;
var type = (length < 16) ? (0x90 + length) : (length <= 0xFFFF) ? 0xdc : 0xdd;
token[type](encoder, length);
var encode = encoder.codec.encode;
for (var i = 0; i < length; i++) {
encode(encoder, value[i]);
}
}
// bin 8 -- 0xc4
// bin 16 -- 0xc5
// bin 32 -- 0xc6
function bin_buffer(encoder, value) {
var length = value.length;
var type = (length < 0xFF) ? 0xc4 : (length <= 0xFFFF) ? 0xc5 : 0xc6;
token[type](encoder, length);
encoder.send(value);
}
function bin_arraybuffer(encoder, value) {
bin_buffer(encoder, new Uint8Array(value));
}
// fixext 1 -- 0xd4
// fixext 2 -- 0xd5
// fixext 4 -- 0xd6
// fixext 8 -- 0xd7
// fixext 16 -- 0xd8
// ext 8 -- 0xc7
// ext 16 -- 0xc8
// ext 32 -- 0xc9
function ext(encoder, value) {
var buffer = value.buffer;
var length = buffer.length;
var type = extmap[length] || ((length < 0xFF) ? 0xc7 : (length <= 0xFFFF) ? 0xc8 : 0xc9);
token[type](encoder, length);
uint8[value.type](encoder);
encoder.send(buffer);
}
// fixmap -- 0x80 - 0x8f
// map 16 -- 0xde
// map 32 -- 0xdf
function obj_to_map(encoder, value) {
var keys = Object.keys(value);
var length = keys.length;
var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
token[type](encoder, length);
var encode = encoder.codec.encode;
keys.forEach(function(key) {
encode(encoder, key);
encode(encoder, value[key]);
});
}
// fixmap -- 0x80 - 0x8f
// map 16 -- 0xde
// map 32 -- 0xdf
function map_to_map(encoder, value) {
if (!(value instanceof Map)) return obj_to_map(encoder, value);
var length = value.size;
var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
token[type](encoder, length);
var encode = encoder.codec.encode;
value.forEach(function(val, key, m) {
encode(encoder, key);
encode(encoder, val);
});
}
// raw 16 -- 0xda
// raw 32 -- 0xdb
// fixraw -- 0xa0 - 0xbf
function raw(encoder, value) {
var length = value.length;
var type = (length < 32) ? (0xa0 + length) : (length <= 0xFFFF) ? 0xda : 0xdb;
token[type](encoder, length);
encoder.send(value);
}
}