Comparing version 0.0.4 to 0.1.2
1596
jpacks.js
@@ -8,264 +8,681 @@ (function (exportName) { | ||
* zswang (http://weibo.com/zswang) | ||
* @version 0.0.4 | ||
* @date 2015-10-30 | ||
* @version 0.1.2 | ||
* @date 2015-11-02 | ||
*/ | ||
var exports = {}; | ||
function createSchema() { | ||
/** | ||
* 对字符串进行 utf8 编码 | ||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView | ||
*/ | ||
var schemas = {}; | ||
/** | ||
* 数据结构 | ||
* @param options 配置项 | ||
* @param {Function} options.unpack 数据解析的方法 | ||
* [[[ | ||
* @param {ArrayBuffer|Buffer|Array} buffer 数据缓存 | ||
* @param {Object=} options 配置项 | ||
* @param {Array=} offsets 第一个原始为偏移,数值类型是为了能修改值 | ||
* @return {Any} 返回解析后的对象 | ||
* ]]] | ||
* function unpack(buffer, options, offsets) {} | ||
* @param {Function} options.pack 数据打包的方法 | ||
* [[[ | ||
* @param {Any} value 需要打包的对象 | ||
* @param {Object=} options 配置项 | ||
* @param {buffer=} {Array} 目标字节数组 | ||
* ]]] | ||
* function pack(value, options, buffer) {} | ||
* @param {number} options.size 占用大小,如果为负数则为动态类型,绝对值为最小尺寸 | ||
* @param {string} options.name 类型名称 | ||
* @param {string} options.namespace 命名空间 e.g. ( 'base': 基础类型 ) | ||
* @constructor 构造数据结构类型 | ||
*/ | ||
function Schema(options) { | ||
var self = this; | ||
Object.keys(options).forEach(function (key) { | ||
self[key] = options[key]; | ||
}); | ||
} | ||
/** | ||
* 定义数据结构 | ||
* | ||
* param {string} str 原始字符串 | ||
* @param {string} name 数据结构名 | ||
* @param {Schema|string|Function} schema 数据结构或者数据结构名 | ||
* @return {boolean} 返回是否定义成功 | ||
*/ | ||
function encodeUTF8(str) { | ||
return String(str).replace( | ||
/[\u0080-\u07ff]/g, | ||
function (c) { | ||
var cc = c.charCodeAt(0); | ||
return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); | ||
Schema.register = function (name, schema) { | ||
/*<safe>*/ | ||
if (typeof name === 'undefined') { | ||
throw new Error('Parameter "name" is undefined.'); | ||
} | ||
/*</safe>*/ | ||
schemas[name] = schema; | ||
if (!Schema[name]) { // 避免覆盖系统方法 | ||
Schema[name] = schema; | ||
} | ||
}; | ||
/** | ||
* 匹配数据结构的方法集合 | ||
* | ||
* @type {Array} | ||
*/ | ||
var schemaPatterns = [ | ||
function _pattern(schema) { | ||
var dicts = {}; | ||
var findSchema = schema; | ||
while (typeof findSchema === 'string') { | ||
if (dicts[findSchema]) { | ||
return; | ||
} | ||
findSchema = schemas[findSchema]; | ||
dicts[findSchema] = true; | ||
} | ||
).replace( | ||
/[\u0800-\uffff]/g, | ||
function (c) { | ||
var cc = c.charCodeAt(0); | ||
return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f); | ||
} | ||
); | ||
} | ||
return findSchema; | ||
} | ||
]; | ||
/** | ||
* 对 utf8 字符串进行解码 | ||
* 添加匹配数据结构的方法集合 | ||
* | ||
* @param {string} str 编码字符串 | ||
* @param {Function} pattern 匹配方法 | ||
* [[[ | ||
* @param {string|object} schema 用于测试的对象 | ||
* @return {Schema} 返回匹配的配数据结构,如果没有匹配的结果则返回 undefined | ||
* function _pattern(schema) {} | ||
* ]]] | ||
*/ | ||
function decodeUTF8(str) { | ||
return String(str).replace( | ||
/[\u00c0-\u00df][\u0080-\u00bf]/g, | ||
function (c) { | ||
var cc = (c.charCodeAt(0) & 0x1f) << 6 | (c.charCodeAt(1) & 0x3f); | ||
return String.fromCharCode(cc); | ||
Schema.pushPattern = function (pattern) { | ||
/*<safe>*/ | ||
if (typeof pattern !== 'function') { | ||
return; | ||
} | ||
/*</safe>*/ | ||
schemaPatterns.push(pattern); | ||
}; | ||
/** | ||
* 确保是数据结构 | ||
* | ||
* @param {string|Object|Schema} schema 数据结构名 | ||
* @return {Schema} 返回名字对应的数据结构 | ||
*/ | ||
Schema.from = function (schema) { | ||
/*<safe>*/ | ||
if (typeof schema === 'undefined') { // 无效数据 | ||
return; | ||
} | ||
/*</safe>*/ | ||
if (schema instanceof Schema) { | ||
return schema; | ||
} | ||
var filter = -1; | ||
for (var i = 0; i < schemaPatterns.length; i++) { // 解析表达式 | ||
if (filter === i) { // 已经被过滤 | ||
continue; | ||
} | ||
).replace( | ||
/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, | ||
function (c) { | ||
var cc = (c.charCodeAt(0) & 0x0f) << 12 | (c.charCodeAt(1) & 0x3f) << 6 | (c.charCodeAt(2) & 0x3f); | ||
return String.fromCharCode(cc); | ||
var match = schemaPatterns[i](schema); | ||
if (match) { | ||
if (match instanceof Schema) { | ||
return match; | ||
} | ||
schema = match; // 类型改变 | ||
filter = i; // 不要走同一个规则 | ||
i = 0; // 重新扫描 | ||
} | ||
); | ||
} | ||
} | ||
}; | ||
/** | ||
* 参考了 c-struct 的接口 | ||
* c-struct 问题是:不支持浏览器环境、不支持有符号数、不支持 int64、扩展性不高 | ||
* 默认低字节序 | ||
* | ||
* @see https://github.com/majimboo/c-struct | ||
* @type {boolean} | ||
*/ | ||
var defaultLittleEndian; | ||
/** | ||
* 数据结构集合 | ||
* @type {Object} | ||
* 设置默认低字节序 | ||
* | ||
* @param {boolean} value 默认值 | ||
*/ | ||
var schemas = {}; | ||
function setDefaultLittleEndian(value) { | ||
defaultLittleEndian = value; | ||
} | ||
Schema.setDefaultLittleEndian = setDefaultLittleEndian; | ||
/** | ||
* 声明数组类型 | ||
* 确保是 ArrayBuffer 类型 | ||
* | ||
* @param {string|Schema} schema 字节长度 | ||
* @param {number} count 元素个数 | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 1 | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.array(_.byte, 10); | ||
var ab = _.pack(_.array(_.byte, 10), [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] | ||
``` | ||
* @param {Array|Buffer} 数组和缓冲区 | ||
* @return {ArrayBuffer} 返回转换后的 ArrayBuffer | ||
*/ | ||
function array(schema, count) { | ||
return new Schema(function _unpack(buffer, options, offsets) { | ||
var result = []; | ||
for (var i = 0; i < count; i++) { | ||
result.push(unpack(schema, buffer, options, offsets)); | ||
} | ||
return result; | ||
}, function _pack(value, options, buffer) { | ||
for (var i = 0; i < count; i++) { | ||
pack(schema, value[i], options, buffer); | ||
} | ||
}); | ||
function arrayBufferFrom(buffer) { | ||
if (buffer instanceof ArrayBuffer) { | ||
return buffer; | ||
} | ||
var ab = new ArrayBuffer(buffer.length); | ||
var arr = new Uint8Array(ab, 0, buffer.length); | ||
arr.set(buffer); | ||
return ab; | ||
} | ||
exports.array = array; | ||
Schema.arrayBufferFrom = arrayBufferFrom; | ||
/** | ||
* 声明字节数组类型 | ||
* 解包 | ||
* | ||
* @param {number} count 元素个数 | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.bytes(10); | ||
var ab = _.pack(_schema, [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] | ||
``` | ||
* @param {string|Object|Schema} packSchema 数据结构信息 | ||
* @param {ArrayBuffer|Buffer} buffer 缓冲区 | ||
* @param {Array=} offsets 读取偏移,会被改写 | ||
* @return {Number|Object} 返回解包的值 | ||
*/ | ||
function bytes(count) { | ||
return array('uint8', count); | ||
function unpack(packSchema, buffer, options, offsets) { | ||
/*<debug> | ||
console.log('unpack(packSchema: %j, buffer: %j)', packSchema, buffer); | ||
//</debug>*/ | ||
var schema = Schema.from(packSchema); | ||
if (!schema) { | ||
throw new Error('Parameter schema "' + packSchema + '" is unregister.'); | ||
} | ||
buffer = arrayBufferFrom(buffer); // 确保是 ArrayBuffer 类型 | ||
options = options || {}; | ||
offsets = offsets || [0]; | ||
if (typeof options.littleEndian === 'undefined') { | ||
options.littleEndian = defaultLittleEndian; | ||
} | ||
return schema.unpack(buffer, options, offsets); // 解码 | ||
} | ||
exports.bytes = bytes; | ||
Schema.unpack = unpack; | ||
/** | ||
* 声明指定长度的数组类型 | ||
* 组包 | ||
* | ||
* @param {string|Schema} lengthSchema 长度类型 | ||
* @param {string|Schema} itemSchema 元素类型 | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 1 | ||
* @param {string|Object|Schema} schema 数据结构信息 | ||
* @param {Number|Object} data 数据 | ||
* @param {Object} options 配置信息 | ||
* @return {ArrayBuffer} | ||
*/ | ||
function pack(packSchema, data, options, buffer) { | ||
/*<debug> | ||
console.log('pack(schema: %j)', packSchema); | ||
//</debug>*/ | ||
var schema = Schema.from(packSchema); | ||
if (!schema) { | ||
throw new Error('Parameter schema "' + packSchema + '" is unregister.'); | ||
} | ||
buffer = buffer || []; | ||
options = options || {}; | ||
if (typeof options.littleEndian === 'undefined') { | ||
options.littleEndian = defaultLittleEndian; | ||
} | ||
schema.pack(data, options, buffer); | ||
return buffer; | ||
} | ||
Schema.pack = pack; | ||
/** | ||
* 凑足参数则调用函数 | ||
* | ||
* @param {Function} fn 任意函数 | ||
* @param {Array=} args 已经凑到的参数 | ||
'''<example>''' | ||
* @example together():base | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.lengthArray(_.byte, _.byte); | ||
var ab = _.pack(_schema, [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [4, 1, 2, 3, 4] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4] | ||
function f(a, b, c) { | ||
console.log(JSON.stringify([a, b, c])); | ||
} | ||
var t = _.together(f); | ||
t(1)()(2, 3); | ||
// -> [1,2,3] | ||
t(4)(5)()(6); | ||
// -> [4,5,6] | ||
t(7, 8, 9); | ||
// -> [7,8,9] | ||
t('a', 'b')('c'); | ||
// -> ["a","b","c"] | ||
t()('x')()()('y')()()('z'); | ||
// -> ["x","y","z"] | ||
``` | ||
* @example 调用示例 2 | ||
'''</example>''' | ||
'''<example>''' | ||
* @example 过程注入 | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.lengthArray(_.word, _.byte); | ||
var ab = _.pack(_schema, [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [0, 0, 0, 4, 1, 2, 3, 4] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4] | ||
function f(a, b, c) {} | ||
var t = _.together(f, function(t, args) { | ||
t.schema = 'f(' + args + ')'; | ||
}); | ||
console.log(t(1)(2).schema); | ||
// > f(1,2) | ||
``` | ||
'''</example>''' | ||
*/ | ||
function lengthArray(lengthSchema, itemSchema) { | ||
return new Schema(function _unpack(buffer, options, offsets) { | ||
var length = unpack(lengthSchema, buffer, options, offsets); | ||
if (isNaN(length)) { | ||
throw new Error('Length is not a numeric type.'); | ||
function together(fn, hook, args) { | ||
if (fn.length <= 0) { | ||
return fn; | ||
} | ||
var result = function() { | ||
var list = []; | ||
[].push.apply(list, args); | ||
[].push.apply(list, arguments); | ||
if (list.length >= fn.length) { | ||
return fn.apply(null, list); | ||
} else { | ||
var result = together(fn, hook, list); | ||
if (typeof hook === 'function') { | ||
hook(result, list); | ||
} | ||
return result; | ||
} | ||
}; | ||
return result; | ||
} | ||
Schema.together = together; | ||
var guid = 0; | ||
/** | ||
* 获取对象的结构表达式 | ||
* | ||
* @param {Any} obj 目标对象 | ||
*/ | ||
function stringify(obj) { | ||
if (arguments.length > 1) { | ||
var result = []; | ||
for (var i = 0; i < length; i++) { | ||
result.push(unpack(itemSchema, buffer, options, offsets)); | ||
for (var i = 0; i < arguments.length; i++) { | ||
result.push(stringify(arguments[i])); | ||
} | ||
return result; | ||
}, function _pack(value, options, buffer) { | ||
if (!value) { | ||
pack(lengthSchema, 0, options, buffer); | ||
} else { | ||
pack(lengthSchema, value.length, options, buffer); | ||
for (var i = 0; i < value.length; i++) { | ||
pack(itemSchema, value[i], options, buffer); | ||
return result.join(); | ||
} | ||
function scan(obj) { | ||
if (!obj) { | ||
return obj; | ||
} | ||
if (obj.namespace) { | ||
if (obj.namespace === 'number') { | ||
return obj.name; | ||
} | ||
if (obj.args) { | ||
return obj.namespace + '(' + stringify.apply(null, obj.args) + ')'; | ||
} | ||
return obj.namespace; | ||
} | ||
}); | ||
if (obj.name) { | ||
return obj.name; | ||
} | ||
if (typeof obj === 'function') { | ||
obj.name = '_pack_fn' + (guid++); | ||
Schema.define(obj.name, obj); | ||
return obj.name; | ||
} | ||
if (typeof obj === 'object') { | ||
var result = new obj.constructor(); | ||
Object.keys(obj).forEach(function (key) { | ||
result[key] = scan(obj[key]); | ||
}); | ||
return result; | ||
} | ||
return obj; | ||
} | ||
return JSON.stringify(scan(obj) || '').replace(/"/g, ''); | ||
} | ||
exports.lengthArray = lengthArray; | ||
Schema.stringify = stringify; | ||
Schema.prototype.toString = function () { | ||
return stringify(this); | ||
}; | ||
return Schema; | ||
} | ||
/** | ||
* 声明指定 uint8 长度的数组类型 | ||
* 创建数据结构作用域 | ||
* | ||
* @param {string|Schema} itemSchema 元素类型 | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 | ||
* @return 返回 Schema | ||
*/ | ||
function create() { | ||
var Schema = createSchema(); | ||
/** | ||
* 基础类型 | ||
* | ||
* @type {Object} | ||
* @key 基础类型名称 | ||
* @value | ||
* @field {string} type 对应 DataView 类型名 | ||
* @field {number} size 数据大小,单位 byte | ||
* @field {Array of string} alias 别名 | ||
* @example all number | ||
'''<example>''' | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.shortArray(_.byte); | ||
var ab = _.pack(_schema, [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [4, 1, 2, 3, 4] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4] | ||
var _map = {}; | ||
'int8,int16,int32,uint8,uint16,uint32,float32,float64,shortint,smallint,longint,byte,word,longword'.split(/,/).forEach(function (item) { | ||
_map[item] = item; | ||
}); | ||
var _schema = _.union(_map, 8); | ||
console.log(_.stringify(_schema)); | ||
// -> union({int8:int8,int16:int16,int32:int32,uint8:uint8,uint16:uint16,uint32:uint32,float32:float32,float64:float64,shortint:shortint,smallint:smallint,longint:longint,byte:byte,word:word,longword:longword},8) | ||
var buffer = _.pack(_schema, { | ||
float64: 2.94296650666094e+189 | ||
}); | ||
console.log(buffer.join(' ')); | ||
// -> 103 69 35 1 120 86 52 16 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"int8":103,"int16":26437,"int32":1732584193,"uint8":103,"uint16":26437,"uint32":1732584193,"float32":9.30951905225494e+23,"float64":2.94296650666094e+189,"shortint":103,"smallint":26437,"longint":1732584193,"byte":103,"word":26437,"longword":1732584193} | ||
``` | ||
'''</example>''' | ||
* @example map is object | ||
*/ | ||
function shortArray(itemSchema) { | ||
return lengthArray('uint8', itemSchema); | ||
} | ||
exports.shortArray = shortArray; | ||
var bases = { | ||
int8: { | ||
type: 'Int8', | ||
size: 1, | ||
alias: ['shortint'], | ||
array: Int8Array | ||
}, | ||
uint8: { | ||
type: 'Uint8', | ||
size: 1, | ||
alias: ['byte'], | ||
array: Uint8Array | ||
}, | ||
int16: { | ||
type: 'Int16', | ||
size: 2, | ||
alias: ['smallint'], | ||
array: Int16Array | ||
}, | ||
uint16: { | ||
type: 'Uint16', | ||
size: 2, | ||
alias: ['word'], | ||
array: Uint16Array | ||
}, | ||
int32: { | ||
type: 'Int32', | ||
size: 4, | ||
alias: ['longint'], | ||
array: Int32Array | ||
}, | ||
uint32: { | ||
type: 'Uint32', | ||
size: 4, | ||
alias: ['longword'], | ||
array: Uint32Array | ||
}, | ||
float32: { | ||
type: 'Float32', | ||
size: 4, | ||
alias: ['single'], | ||
array: Float32Array | ||
}, | ||
float64: { | ||
type: 'Float64', | ||
size: 8, | ||
alias: ['double'], | ||
array: Float64Array | ||
}, | ||
}; | ||
/** | ||
* 声明指定 uint16 长度的数组类型 | ||
* 定义基础类型 | ||
*/ | ||
Object.keys(bases).forEach(function (name) { | ||
var item = bases[name]; | ||
var schema = new Schema({ | ||
unpack: (function (method) { | ||
return function _unpack(buffer, options, offsets) { | ||
var offset = offsets[0]; | ||
offsets[0] += item.size; | ||
var dataView; | ||
if (buffer instanceof DataView) { | ||
dataView = buffer; | ||
} else { | ||
dataView = new DataView(buffer); | ||
} | ||
return dataView[method](offset, options.littleEndian); | ||
}; | ||
})('get' + item.type), | ||
pack: (function (method) { | ||
return function _pack(value, options, buffer) { | ||
var arrayBuffer = new ArrayBuffer(item.size); | ||
var dataView = new DataView(arrayBuffer); | ||
var uint8Array = new Uint8Array(arrayBuffer); | ||
dataView[method](0, value, options.littleEndian); | ||
[].push.apply(buffer, uint8Array); | ||
}; | ||
})('set' + item.type), | ||
size: item.size, | ||
name: name, | ||
namespace: 'number', | ||
array: item.array | ||
}); | ||
Schema.register(name, schema); | ||
(item.alias || []).forEach(function (alias) { | ||
Schema.register(alias, schema); | ||
}); | ||
}); | ||
/** | ||
* 声明指定长度或者下标的数组 | ||
* | ||
* @param {string|Schema} itemSchema 元素类型 | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 | ||
* @param {string|Schema|number=} count 下标类型或个数 | ||
* @return {Schema|Function} 返回数据结构 | ||
'''<example>''' | ||
* @example arrayCreator():static array | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.longArray(_.byte); | ||
var ab = _.pack(_schema, [1, 2, 3, 4]); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [0, 4, 1, 2, 3, 4] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> [1, 2, 3, 4] | ||
var _schema = jpacks.array('int16', 2); | ||
console.log(String(_schema)); | ||
// > array(int16,2) | ||
var value = [12337, 12851]; | ||
var buffer = jpacks.pack(_schema, value); | ||
console.log(buffer.join(' ')); | ||
// > 48 49 50 51 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// > [12337,12851] | ||
``` | ||
'''</example>''' | ||
'''<example>''' | ||
* @example arrayCreator():dynamic array | ||
```js | ||
var _ = jpacks; | ||
var _schema = jpacks.array('int16', 'int8'); | ||
console.log(String(_schema)); | ||
// > array(int16,int8) | ||
var value = [12337, 12851]; | ||
var buffer = jpacks.pack(_schema, value); | ||
console.log(buffer.join(' ')); | ||
// > 2 48 49 50 51 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// > [12337,12851] | ||
``` | ||
* @example arrayCreator():dynamic array 2 | ||
```js | ||
var _ = jpacks; | ||
var _schema = jpacks.array('int16')(6); | ||
console.log(String(_schema)); | ||
// > array(int16,6) | ||
var value = [12337, 12851]; | ||
var buffer = jpacks.pack(_schema, value); | ||
console.log(buffer.join(' ')); | ||
// > 48 49 50 51 0 0 0 0 0 0 0 0 | ||
console.log(JSON.stringify(jpacks.unpack(_schema, buffer))); | ||
// > [12337,12851,0,0,0,0] | ||
``` | ||
'''</example>''' | ||
*/ | ||
function longArray(itemSchema) { | ||
return lengthArray('uint16', itemSchema); | ||
} | ||
exports.longArray = longArray; | ||
/** | ||
* 声明指定长度的字符串 | ||
* | ||
* @param {string|Schema} lengthSchema 长度类型 | ||
* @return {Schema} 返回数据结构 | ||
*/ | ||
function lengthString(lengthSchema) { | ||
var schema = lengthArray(lengthSchema, 'uint8'); | ||
return new Schema(function _unpack(buffer, options, offsets) { | ||
var stringBuffer = unpack(schema, buffer, options, offsets); | ||
if (typeof Buffer !== 'undefined') { // NodeJS | ||
return new Buffer(stringBuffer).toString(); | ||
function arrayCreator(itemSchema, count) { | ||
if (typeof itemSchema === 'undefined') { | ||
throw new Error('Parameter "itemSchema" is undefined.'); | ||
} | ||
/*<debug> | ||
console.log('arrayCreator()', Schema.stringify(itemSchema, count)); | ||
//</debug>*/ | ||
var size; | ||
var countSchema; | ||
if (typeof count === 'number') { | ||
size = itemSchema.size * count; | ||
} else { | ||
countSchema = Schema.from(count); | ||
if (countSchema.namespace !== 'number') { | ||
throw new Error('Parameter "count" is not a numeric type.'); | ||
} | ||
return decodeUTF8(String.fromCharCode.apply(String, stringBuffer)); | ||
}, function _pack(value, options, buffer) { | ||
var stringBuffer; | ||
if (typeof Buffer !== 'undefined') { // NodeJS | ||
stringBuffer = new Buffer(value); | ||
} else { | ||
stringBuffer = encodeUTF8(value).split('').map(function (item) { | ||
return item.charCodeAt(); | ||
}); | ||
} | ||
pack(schema, stringBuffer, options, buffer); | ||
} | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var length = count; | ||
if (countSchema) { | ||
length = Schema.unpack(countSchema, buffer, options, offsets); | ||
} | ||
if (itemSchema.array && options.littleEndian) { | ||
size = countSchema.size * length; | ||
/* TypeArray littleEndian is true */ | ||
var offset = offsets[0]; | ||
offsets[0] += size; | ||
return [].slice.apply(new itemSchema.array(buffer, offset, length)); | ||
} | ||
var result = []; | ||
for (var i = 0; i < length; i++) { | ||
result.push(Schema.unpack(itemSchema, buffer, options, offsets)); | ||
} | ||
return result; | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var length = count; | ||
if (countSchema) { | ||
length = value ? value.length : 0; | ||
Schema.pack(countSchema, length, options, buffer); | ||
} | ||
if (itemSchema.array && options.littleEndian) { | ||
size = itemSchema.size * length; | ||
/* TypeArray littleEndian is true */ | ||
var arrayBuffer = new ArrayBuffer(size); | ||
var typeArray = new itemSchema.array(arrayBuffer); | ||
typeArray.set(value); | ||
var uint8Array = new Uint8Array(arrayBuffer); | ||
[].push.apply(buffer, uint8Array); | ||
} | ||
for (var i = 0; i < length; i++) { | ||
Schema.pack(itemSchema, value[i], options, buffer); | ||
} | ||
}, | ||
namespace: 'array', | ||
args: arguments, | ||
size: size | ||
}); | ||
} | ||
var array = Schema.together(arrayCreator, function (fn, args) { | ||
fn.namespace = 'array'; | ||
fn.args = args; | ||
}); | ||
Schema.register('array', array); | ||
function shortArray(itemSchema) { | ||
return array(itemSchema, 'uint8'); | ||
} | ||
Schema.register('shortArray', shortArray); | ||
function smallArray(itemSchema) { | ||
return array(itemSchema, 'uint16'); | ||
} | ||
Schema.register('smallArray', smallArray); | ||
function longArray(itemSchema) { | ||
return array(itemSchema, 'uint32'); | ||
} | ||
Schema.register('longArray', longArray); | ||
/** | ||
* 短字符串类型 | ||
* 字节数组 | ||
* | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 | ||
* @param {string|Schema|number=} count 下标类型或个数 | ||
* @return {Schema|Function} 返回数据结构 | ||
'''<example>''' | ||
* @example bytes() | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.shortString; | ||
var ab = _.pack(_schema, '你好 World!'); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [13, 228, 189, 160, 229, 165, 189, 32, 87, 111, 114, 108, 100, 33] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> 你好 World! | ||
var _schema = jpacks.bytes(6); | ||
console.log(String(_schema)); | ||
// > array(uint8,6) | ||
var value = [0, 1, 2, 3, 4, 5]; | ||
var buffer = jpacks.pack(_schema, value); | ||
console.log(buffer.join(' ')); | ||
// > 0 1 2 3 4 5 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// > [0,1,2,3,4,5] | ||
``` | ||
*/ | ||
exports.shortString = lengthString('uint8'); | ||
function bytes(count) { | ||
return Schema.array('uint8', count); | ||
} | ||
Schema.register('bytes', bytes); | ||
/** | ||
* 长字符串类型 | ||
* 定义一个对象结构 | ||
* | ||
* @return {Schema} 返回数据结构 | ||
* @example 调用示例 | ||
* @param {object} schema 数据结构 | ||
* @return {Schema} 返回构建的数据结构 | ||
'''<example>''' | ||
* @example objectCreator:array | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.longString; | ||
var ab = _.pack(_schema, '你好 World!'); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [0, 13, 228, 189, 160, 229, 165, 189, 32, 87, 111, 114, 108, 100, 33] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> 你好 World! | ||
var _schema = _.object([_.shortString, _.word]); | ||
console.log(_.stringify(_schema)); | ||
// -> object([string(uint8),uint16]) | ||
var buffer = _.pack(_schema, ['zswang', 1978]); | ||
console.log(buffer.join(' ')); | ||
// -> 6 122 115 119 97 110 103 7 186 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> ["zswang",1978] | ||
``` | ||
'''</example>''' | ||
'''<example>''' | ||
* @example objectCreator:object | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.object({ | ||
name: _.shortString, | ||
year: _.word | ||
}); | ||
console.log(_.stringify(_schema)); | ||
// -> object({namespace:string,args:{0:uint8}}) | ||
var buffer = _.pack(_schema, { | ||
name: 'zswang', | ||
year: 1978 | ||
}); | ||
console.log(buffer.join(' ')); | ||
// -> 6 122 115 119 97 110 103 7 186 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"name":"zswang","year":1978} | ||
``` | ||
'''</example>''' | ||
*/ | ||
exports.longString = lengthString('uint16'); | ||
function objectCreator(objectSchema) { | ||
if (typeof objectSchema !== 'object') { | ||
throw new Error('Parameter "schemas" must be a object type.'); | ||
} | ||
if (objectSchema instanceof Schema) { | ||
return objectSchema; | ||
} | ||
var keys = Object.keys(objectSchema); | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var result = new objectSchema.constructor(); | ||
var $scope = options.$scope; | ||
options.$scope = result; | ||
keys.forEach(function (key) { | ||
result[key] = Schema.unpack(objectSchema[key], buffer, options, offsets); | ||
}); | ||
options.$scope = $scope; | ||
return result; | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var $scope = options.$scope; | ||
options.$scope = value; | ||
keys.forEach(function (key) { | ||
Schema.pack(objectSchema[key], value[key], options, buffer); | ||
}); | ||
options.$scope = $scope; | ||
}, | ||
args: arguments, | ||
namespace: 'object' | ||
}); | ||
}; | ||
var object = Schema.together(objectCreator, function (fn, args) { | ||
fn.namespace = 'object'; | ||
fn.args = args; | ||
}); | ||
Schema.register('object', object); | ||
Schema.pushPattern(function _objectPattern(schema) { | ||
if (typeof schema === 'object') { | ||
if (schema instanceof Schema) { | ||
return; | ||
} | ||
if (schema instanceof Array) { | ||
return; | ||
} | ||
return object(schema); | ||
} | ||
}); | ||
/** | ||
@@ -277,23 +694,23 @@ * 创建联合类型 | ||
* @return {Schema} 返回联合类型 | ||
* @example 调用示例 | ||
'''<example>''' | ||
* @example unionCreator():base | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.union(20, { | ||
var _schema = _.union({ | ||
length: _.byte, | ||
content: _.shortString | ||
}); | ||
var ab = _.pack(_schema, { | ||
}, 20); | ||
console.log(_.stringify(_schema)); | ||
// -> union({length:uint8,content:string(uint8)},20) | ||
var buffer = _.pack(_schema, { | ||
content: '0123456789' | ||
}); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [10, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> Object {length: 10, content: "0123456789"} | ||
console.log(buffer.join(' ')); | ||
// -> 10 48 49 50 51 52 53 54 55 56 57 0 0 0 0 0 0 0 0 0 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"length":10,"content":"0123456789"} | ||
``` | ||
'''</example>''' | ||
*/ | ||
function union(size, schemas) { | ||
if (typeof size !== 'number') { | ||
throw new Error('Parameter "size" must be a numeric type.'); | ||
} | ||
function unionCreator(schemas, size) { | ||
if (typeof schemas !== 'object') { | ||
@@ -303,298 +720,569 @@ throw new Error('Parameter "schemas" must be a object type.'); | ||
if (schemas instanceof Schema) { | ||
throw new Error('Parameter "schemas" cannot be a Scheam object.'); | ||
throw new Error('Parameter "schemas" cannot be a Schema object.'); | ||
} | ||
var keys = Object.keys(schemas); | ||
return new Schema(function _unpack(buffer, options, offsets) { | ||
var beginOffset = offsets[0]; | ||
var result = {}; | ||
keys.forEach(function (key) { | ||
offsets[0] = beginOffset; | ||
result[key] = unpack(schemas[key], buffer, options, offsets); | ||
}); | ||
offsets[0] += size; | ||
return result; | ||
}, function _pack(value, options, buffer) { | ||
var arrayBuffer = new ArrayBuffer(size); | ||
var uint8Array = new Uint8Array(arrayBuffer); | ||
keys.forEach(function (key) { | ||
var temp = []; | ||
pack(schemas[key], value[key], options, temp); | ||
uint8Array.set(temp); | ||
}); | ||
[].push.apply(buffer, uint8Array); | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var beginOffset = offsets[0]; | ||
var result = {}; | ||
keys.forEach(function (key) { | ||
offsets[0] = beginOffset; | ||
result[key] = Schema.unpack(schemas[key], buffer, options, offsets); | ||
}); | ||
offsets[0] += size; | ||
return result; | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var arrayBuffer = new ArrayBuffer(size); | ||
var uint8Array = new Uint8Array(arrayBuffer); | ||
keys.forEach(function (key) { | ||
if (typeof value[key] === 'undefined') { | ||
return; | ||
} | ||
var temp = []; | ||
Schema.pack(schemas[key], value[key], options, temp); | ||
uint8Array.set(temp); | ||
}); | ||
[].push.apply(buffer, uint8Array); | ||
}, | ||
size: size, | ||
args: arguments, | ||
namespace: 'union' | ||
}); | ||
} | ||
exports.union = union; | ||
var union = Schema.together(unionCreator, function (fn, args) { | ||
fn.namespace = 'union'; | ||
fn.args = args; | ||
}); | ||
Schema.register('union', union); | ||
/** | ||
* 创建条件类型 | ||
* 定义一个枚举结构 | ||
* | ||
* @param {Object} schemas 条件类型结构,第一个字段为条件字段,其他字段为数组。数组第一元素表示命中条件,第二位类型 | ||
* @return {Schema} 返回联合类型 | ||
* @example 调用示例 1 | ||
* @param {Schema} baseSchema 枚举结构的基础类型 | ||
* @param {Array|Object} map 枚举类型字典 | ||
* @return {Schema} 返回构建的数据结构 | ||
'''<example>''' | ||
* @example enumsCreator():map is array | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.cases({ | ||
type: _.shortString, | ||
name: ['name', _.shortString], | ||
age: ['age', _.byte] | ||
}); | ||
var ab = _.pack(_schema, { | ||
type: 'name', | ||
name: 'tom' | ||
}); | ||
var u8a = new Uint8Array(ab); | ||
console.log(u8a); | ||
// -> [4, 110, 97, 109, 101, 3, 116, 111, 109] | ||
console.log(_.unpack(_schema, u8a)); | ||
// -> Object {type: "name", name: "tom"} | ||
var ab2 = _.pack(_schema, { | ||
type: 'age', | ||
age: 23 | ||
}); | ||
var u8a2 = new Uint8Array(ab2); | ||
console.log(u8a2); | ||
// -> [3, 97, 103, 101, 23] | ||
console.log(_.unpack(_schema, u8a2)); | ||
// -> Object {type: "age", age: 23} | ||
var _schema = _.enums(['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'], 'uint8'); | ||
console.log(_.stringify(_schema)); | ||
// -> enums({Sun:0,Mon:1,Tues:2,Wed:3,Thur:4,Fri:5,Sat:6},uint8) | ||
var buffer = _.pack(_schema, 'Tues'); | ||
console.log(buffer.join(' ')); | ||
// -> 2 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> "Tues" | ||
``` | ||
'''</example>''' | ||
'''<example>''' | ||
* @example enumsCreator():map is object | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.enums({ | ||
Unknown: -1, | ||
Continue: 100, | ||
Processing: 100, | ||
OK: 200, | ||
Created: 201, | ||
NotFound: 404 | ||
}, 'int8'); | ||
console.log(_.stringify(_schema)); | ||
// -> enums({Unknown:-1,Continue:100,Processing:100,OK:200,Created:201,NotFound:404},int8) | ||
var buffer = _.pack(_schema, 'Unknown'); | ||
console.log(buffer.join(' ')); | ||
// -> 255 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> "Unknown" | ||
``` | ||
'''</example>''' | ||
'''<example>''' | ||
* @example enumsCreator():fault tolerant | ||
var _ = jpacks; | ||
var _schema = _.enums({ | ||
Unknown: -1, | ||
Continue: 100, | ||
Processing: 100, | ||
OK: 200, | ||
Created: 201, | ||
NotFound: 404 | ||
}, 'int8'); | ||
console.log(_.stringify(_schema)); | ||
// -> enums({Unknown:-1,Continue:100,Processing:100,OK:200,Created:201,NotFound:404},int8) | ||
var buffer = _.pack(_schema, 2); | ||
console.log(buffer.join(' ')); | ||
// -> 2 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> 2 | ||
'''</example>''' | ||
*/ | ||
function cases(schemas) { | ||
if (typeof schemas !== 'object') { | ||
throw new Error('Parameter "schemas" must be a object type.'); | ||
function enumsCreator(map, baseSchema) { | ||
baseSchema = Schema.from(baseSchema); | ||
if (!baseSchema) { | ||
throw new Error('Parameter "baseSchema" is undefined.'); | ||
} | ||
if (schemas instanceof Schema) { | ||
throw new Error('Parameter "schemas" cannot be a Scheam object.'); | ||
if (baseSchema.namespace !== 'number') { | ||
throw new Error('Parameter "baseSchema" is not a numeric type.'); | ||
} | ||
var keys = Object.keys(schemas); | ||
var patternName = keys[0]; | ||
var patternSchema = schemas[patternName]; | ||
keys = keys.slice(1); | ||
return new Schema(function _unpack(buffer, options, offsets) { | ||
var result = {}; | ||
var patternValue = unpack(patternSchema, buffer, options, offsets); | ||
result[patternName] = patternValue; | ||
keys.every(function (key) { | ||
if (patternValue === schemas[key][0]) { | ||
result[key] = unpack(schemas[key][1], buffer, options, offsets); | ||
return false; | ||
} | ||
return true; | ||
if (typeof map !== 'object') { | ||
throw new Error('Parameter "map" must be a object type.'); | ||
} | ||
if (map instanceof Array) { | ||
var temp = {}; | ||
map.forEach(function (item, index) { | ||
temp[item] = index; | ||
}); | ||
return result; | ||
}, function _pack(value, options, buffer) { | ||
var patternValue = value[patternName]; | ||
pack(patternSchema, patternValue, options, buffer); | ||
keys.every(function (key) { | ||
if (patternValue === schemas[key][0]) { | ||
pack(schemas[key][1], value[key], options, buffer); | ||
return false; | ||
map = temp; | ||
} | ||
var keys = Object.keys(map); | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var baseValue = Schema.unpack(baseSchema, buffer, options, offsets); | ||
var result; | ||
keys.every(function (key) { | ||
if (map[key] === baseValue) { | ||
result = key; | ||
return false; | ||
} | ||
return true; | ||
}); | ||
return result || baseValue; | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
if (typeof value === 'number') { | ||
Schema.pack(baseSchema, value, options, buffer); | ||
return; | ||
} | ||
return true; | ||
}); | ||
if (keys.every(function (key) { | ||
if (key === value) { | ||
Schema.pack(baseSchema, map[key], options, buffer); | ||
return false; | ||
} | ||
return true; | ||
})) { | ||
throw new Error('Not find enum "' + value + '".'); | ||
}; | ||
}, | ||
namespace: 'enums', | ||
args: arguments | ||
}); | ||
} | ||
exports.cases = cases; | ||
}; | ||
var enums = Schema.together(enumsCreator, function (fn, args) { | ||
fn.namespace = 'enums'; | ||
fn.args = args; | ||
}); | ||
Schema.register('enums', enums); | ||
/** | ||
* 数据结构 | ||
* 对字符串进行 utf8 编码 | ||
* | ||
* @param {Function} unpack 数据解析的方法 | ||
* [[[ | ||
* @param {ArrayBuffer|Buffer|Array} buffer 数据缓存 | ||
* @param {Object=} options 配置项 | ||
* @param {Array=} offsets 第一个原始为偏移,数值类型是为了能修改值 | ||
* @return {Any} 返回解析后的对象 | ||
* ]]] | ||
* function unpack(buffer, options, offsets) {} | ||
* @param {Function} pack 数据打包的方法 | ||
* [[[ | ||
* @param {Any} value 需要打包的对象 | ||
* @param {Object=} options 配置项 | ||
* @param {buffer=} {Array} 目标字节数组 | ||
* ]]] | ||
* function pack(value, options, buffer) {} | ||
* @constructor 构造数据结构类型 | ||
* param {string} str 原始字符串 | ||
*/ | ||
function Schema(unpack, pack) { | ||
this.unpack = unpack; | ||
this.pack = pack; | ||
function encodeUTF8(str) { | ||
return String(str).replace( | ||
/[\u0080-\u07ff]/g, | ||
function (c) { | ||
var cc = c.charCodeAt(0); | ||
return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); | ||
} | ||
).replace( | ||
/[\u0800-\uffff]/g, | ||
function (c) { | ||
var cc = c.charCodeAt(0); | ||
return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f); | ||
} | ||
); | ||
} | ||
/** | ||
* 标准数值类型 | ||
* 对 utf8 字符串进行解码 | ||
* | ||
* @param {string} str 编码字符串 | ||
*/ | ||
[ | ||
['int8', 'Int8', 1], | ||
['uint8', 'Uint8', 1], | ||
['int16', 'Int16', 2], | ||
['uint16', 'Uint16', 2], | ||
['int32', 'Int32', 4], | ||
['uint32', 'Uint32', 4], | ||
['float32', 'Float32', 4], | ||
['float64', 'Float64', 8] | ||
].forEach(function (item) { | ||
var name = item[0]; | ||
var dataViewType = item[1]; | ||
var size = item[2]; | ||
exports[name] = name; | ||
schemas[name] = new Schema( | ||
(function (dataViewMethod) { | ||
return function unpack(buffer, options, offsets) { | ||
var offset = offsets[0]; | ||
offsets[0] += size; | ||
var dataView; | ||
if (buffer instanceof DataView) { | ||
dataView = buffer; | ||
} else { | ||
dataView = new DataView(buffer); | ||
} | ||
return dataView[dataViewMethod](offset, options.littleEndian); | ||
}; | ||
})('get' + dataViewType), (function (dataViewMethod) { | ||
return function pack(value, options, buffer) { | ||
// value = value || 0; | ||
var arrayBuffer = new ArrayBuffer(size); | ||
var dataView = new DataView(arrayBuffer); | ||
var uint8Array = new Uint8Array(arrayBuffer); | ||
dataView[dataViewMethod](0, value, options.littleEndian); | ||
[].push.apply(buffer, uint8Array); | ||
}; | ||
})('set' + dataViewType) | ||
function decodeUTF8(str) { | ||
return String(str).replace( | ||
/[\u00c0-\u00df][\u0080-\u00bf]/g, | ||
function (c) { | ||
var cc = (c.charCodeAt(0) & 0x1f) << 6 | (c.charCodeAt(1) & 0x3f); | ||
return String.fromCharCode(cc); | ||
} | ||
).replace( | ||
/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, | ||
function (c) { | ||
var cc = (c.charCodeAt(0) & 0x0f) << 12 | (c.charCodeAt(1) & 0x3f) << 6 | (c.charCodeAt(2) & 0x3f); | ||
return String.fromCharCode(cc); | ||
} | ||
); | ||
}); | ||
} | ||
/** | ||
* 类型别名 | ||
* 将字符串转换为字节数组 | ||
* | ||
* @param {string} value 字符串内容 | ||
* @param {string=} encoding 编码类型,仅在 NodeJS 下生效,默认 'utf-8' | ||
* @return {Array} 返回字节数组 | ||
* @return {Schema} 返回解析类型 | ||
'''<example>''' | ||
* @example stringBytes():base | ||
var _ = jpacks; | ||
var buffer = _.pack(_.bytes(20), _.stringBytes('你好世界!Hello')); | ||
console.log(buffer.join(' ')); | ||
// > 228 189 160 229 165 189 228 184 150 231 149 140 239 188 129 72 101 108 108 111 | ||
'''<example>''' | ||
*/ | ||
var alias = [ | ||
['double', 'float64'], | ||
['float', 'float32'], | ||
['byte', 'uint8'], | ||
['word', 'uint32'], | ||
]; | ||
alias.forEach(function (item) { | ||
var name = item[0]; | ||
var schema = item[1]; | ||
exports[name] = schemas[name] = schemas[schema]; | ||
}); | ||
function stringBytes(value, encoding) { | ||
if (typeof Buffer !== 'undefined') { // NodeJS | ||
return new Buffer(value, encoding); | ||
} else { | ||
return encodeUTF8(value).split('').map(function (item) { | ||
return item.charCodeAt(); | ||
}); | ||
} | ||
} | ||
Schema.stringBytes = stringBytes; | ||
/** | ||
* 注册一个数据结构信息 | ||
* 声明指定长度的字符串 | ||
* | ||
* @param {string} name 数据结构名 | ||
* @param {Object} schema 数据结构 | ||
* @param {number|string|Schema} size 字节个数下标类型 | ||
* @return {Schema} 返回数据结构 | ||
'''<example>''' | ||
* @example stringCreator():static | ||
var _ = jpacks; | ||
var _schema = _.string(25); | ||
console.log(_.stringify(_schema)); | ||
// -> string(25) | ||
var buffer = _.pack(_schema, '你好世界!Hello'); | ||
console.log(buffer.join(' ')); | ||
// -> 228 189 160 229 165 189 228 184 150 231 149 140 239 188 129 72 101 108 108 111 0 0 0 0 0 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> 你好世界!Hello | ||
'''<example>''' | ||
'''<example>''' | ||
* @example stringCreator():dynamic | ||
var _ = jpacks; | ||
var _schema = _.string('int8'); | ||
console.log(_.stringify(_schema)); | ||
// -> string('int8') | ||
var buffer = _.pack(_schema, '你好世界!Hello'); | ||
console.log(buffer.join(' ')); | ||
// -> 20 228 189 160 229 165 189 228 184 150 231 149 140 239 188 129 72 101 108 108 111 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> 你好世界!Hello | ||
'''<example>''' | ||
*/ | ||
function register(name, schema) { | ||
schemas[name] = schema; | ||
function stringCreator(size) { | ||
// console.log('stringCreator', Schema.stringify(size)); | ||
var schema = Schema.array('uint8', size); | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var stringBuffer = Schema.unpack(schema, buffer, options, offsets); | ||
if (typeof Buffer !== 'undefined') { // NodeJS | ||
return new Buffer(stringBuffer).toString(); | ||
} | ||
return decodeUTF8(String.fromCharCode.apply(String, stringBuffer)); | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
Schema.pack(schema, stringBytes(value), options, buffer); | ||
}, | ||
namespace: 'string', | ||
args: arguments | ||
}); | ||
} | ||
exports.register = register; | ||
var string = Schema.together(stringCreator, function (fn, args) { | ||
fn.namespace = 'string'; | ||
fn.args = args; | ||
}); | ||
Schema.register('string', string); | ||
/** | ||
* 确保是 ArrayBuffer 类型 | ||
* 短字符串类型 | ||
* | ||
* @param {Array|Buffer} 数组和缓冲区 | ||
* @return {ArrayBuffer} 返回转换后的 ArrayBuffer | ||
* @return {Schema} 返回数据结构 | ||
'''<example>''' | ||
* @example shortString | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.shortString; | ||
console.log(_.stringify(_schema)); | ||
// -> string(uint8) | ||
var buffer = _.pack(_schema, 'shortString'); | ||
console.log(buffer.join(' ')); | ||
// -> 11 115 104 111 114 116 83 116 114 105 110 103 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> shortString | ||
``` | ||
'''</example>''' | ||
*/ | ||
function arrayBufferFrom(buffer) { | ||
if (buffer instanceof ArrayBuffer) { | ||
return buffer; | ||
} | ||
var ab = new ArrayBuffer(buffer.length); | ||
var arr = new Uint8Array(ab, 0, buffer.length); | ||
arr.set(buffer); | ||
return ab; | ||
} | ||
exports.arrayBufferFrom = arrayBufferFrom; | ||
Schema.register('shortString', string('uint8')); | ||
/** | ||
* 解包 | ||
* 长字符串类型 | ||
* | ||
* @param {string|Object|Schema} schema 数据结构信息 | ||
* @param {ArrayBuffer|Buffer} buffer 缓冲区 | ||
* @param {Array=} offsets 读取偏移,会被改写 | ||
* @return {Number|Object} 返回解包的值 | ||
* @return {Schema} 返回数据结构 | ||
'''<example>''' | ||
* @example smallString | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.smallString; | ||
console.log(_.stringify(_schema)); | ||
// -> string(uint16) | ||
var buffer = _.pack(_schema, 'smallString'); | ||
console.log(buffer.join(' ')); | ||
// -> 0 11 115 109 97 108 108 83 116 114 105 110 103 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> smallString | ||
``` | ||
'''</example>''' | ||
*/ | ||
function unpack(schema, buffer, options, offsets) { | ||
if (!schema) { | ||
throw new Error('Parameter "schema" is undefined.'); | ||
} | ||
if (!(schema instanceof Schema)) { | ||
if (typeof schema === 'string') { | ||
schema = schemas[schema]; | ||
Schema.register('smallString', string('uint16')); | ||
/** | ||
* 超长字符串类型 | ||
* | ||
* @return {Schema} 返回数据结构 | ||
'''<example>''' | ||
* @example longString | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.longString; | ||
console.log(_.stringify(_schema)); | ||
// -> string(uint32) | ||
var buffer = _.pack(_schema, 'longString'); | ||
console.log(buffer.join(' ')); | ||
// -> 0 0 0 10 108 111 110 103 83 116 114 105 110 103 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> longString | ||
``` | ||
'''</example>''' | ||
*/ | ||
Schema.register('longString', string('uint32')); | ||
/** | ||
* 以零号字符结尾的字符串 | ||
* | ||
* @type {Schema} | ||
'''<example>''' | ||
* @example cstring() | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.cstring; | ||
console.log(_.stringify(_schema)); | ||
// -> cstring | ||
var buffer = _.pack(_schema, 'Hello 你好!'); | ||
console.log(buffer.join(' ')); | ||
// -> 72 101 108 108 111 32 228 189 160 229 165 189 239 188 129 0 | ||
console.log(_.unpack(_schema, buffer)); | ||
// -> Hello 你好! | ||
``` | ||
'''</example>''' | ||
*/ | ||
var cstring = new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var uint8Array = new Uint8Array(buffer, offsets[0]); | ||
var size = 0; | ||
while (uint8Array[size]) { | ||
size++; | ||
} | ||
var result = Schema.unpack(Schema.string(size), buffer, options, offsets); | ||
offsets[0]++; | ||
return result; | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var bytes = Schema.stringBytes(value); | ||
Schema.pack(Schema.bytes(bytes.length), bytes, options, buffer); | ||
Schema.pack('byte', 0, options, buffer); | ||
}, | ||
namespace: 'cstring', | ||
schema: 'cstring' | ||
}); | ||
Schema.register('cstring', cstring); | ||
Schema.register('pchar', cstring); | ||
/** | ||
* 创建条件类型 | ||
* | ||
* @param {Array of array} patterns 数组第一元素表示命中条件,第二位类型 | ||
* @return {Schema} 返回条件类型 | ||
'''<example>''' | ||
* @example casesCreator | ||
```js | ||
var _ = jpacks; | ||
var _schema = { | ||
type: _.shortString, | ||
data: _.depend('type', _.cases([ | ||
['name', _.shortString], | ||
['age', _.byte] | ||
])) | ||
}; | ||
console.log(_.stringify(_schema)); | ||
// -> {type:string(uint8),data:depend(type,cases([[name,string(uint8)],[age,uint8]]))} | ||
var buffer = _.pack(_schema, { | ||
type: 'name', | ||
data: 'tom' | ||
}); | ||
console.log(buffer.join(' ')); | ||
// -> 4 110 97 109 101 3 116 111 109 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"type":"name","data":"tom"} | ||
var buffer = _.pack(_schema, { | ||
type: 'age', | ||
data: 23 | ||
}); | ||
console.log(buffer.join(' ')); | ||
// -> 3 97 103 101 23 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"type":"age","data":23} | ||
``` | ||
'''</example>''' | ||
*/ | ||
function casesCreator(patterns, value) { | ||
/*<safe>*/ | ||
if (typeof patterns !== 'object') { | ||
throw new Error('Parameter "patterns" must be a object type.'); | ||
} | ||
if (!(buffer instanceof ArrayBuffer)) { | ||
buffer = arrayBufferFrom(buffer); | ||
if (patterns instanceof Schema) { | ||
throw new Error('Parameter "patterns" cannot be a Schema object.'); | ||
} | ||
if (!schema) { | ||
throw new Error('Parameter "schema" is unregister.'); | ||
if (!(patterns instanceof Array)) { | ||
throw new Error('Parameter "patterns" must be a array.'); | ||
} | ||
options = options || {}; | ||
offsets = offsets || [0]; | ||
if (typeof options.littleEndian === 'undefined') { | ||
options.littleEndian = defaultLittleEndian; | ||
if (typeof value === 'undefined') { | ||
throw new Error('Parameter "value" is undefined.'); | ||
} | ||
if (typeof schema.unpack === 'function') { | ||
return schema.unpack(buffer, options, offsets); | ||
/*</safe>*/ | ||
for (var i = 0; i < patterns.length; i++) { | ||
if (patterns[i][0] === value) { | ||
return patterns[i][1]; | ||
} | ||
} | ||
var result = {}; | ||
Object.keys(schema).forEach(function (key) { | ||
result[key] = unpack(schema[key], buffer, options, offsets); | ||
}); | ||
return result; | ||
} | ||
exports.unpack = unpack; | ||
var cases = Schema.together(casesCreator, function (fn, args) { | ||
fn.namespace = 'cases'; | ||
fn.args = args; | ||
}); | ||
Schema.register('cases', cases); | ||
/** | ||
* 组包 | ||
* 声明字段依赖结构 | ||
* | ||
* @param {string|Object|Schema} schema 数据结构信息 | ||
* @param {Number|Object} data 数据 | ||
* @param {Object} options 配置信息 | ||
* @return {ArrayBuffer} | ||
* @param {string} field 字段名 | ||
* @param {Function} schemaCreator 创建数据结构的方法 | ||
* [[[ | ||
* @param {Any} value 传递值 | ||
* @return {Schema} 返回数据结构 | ||
* function schemaCreator(value) {} | ||
* ]]] | ||
'''<example>''' | ||
* @example dependCreator() | ||
```js | ||
var _ = jpacks; | ||
var _schema = _.object({ | ||
length1: 'int8', | ||
length2: 'int8', | ||
data1: _.depend('length1', _.bytes), | ||
data2: _.depend('length2', _.array(_.shortString)) | ||
}); | ||
console.log(_.stringify(_schema)); | ||
// -> object({length1:int8,length2:int8,data1:depend(length1,bytes),data2:depend(length2,array(string(uint8)))}) | ||
var buffer = _.pack(_schema, { | ||
length1: 2, | ||
length2: 3, | ||
data1: [1, 2], | ||
data2: ['甲', '乙', '丙'] | ||
}); | ||
console.log(buffer.join(' ')); | ||
// -> 2 3 1 2 3 231 148 178 3 228 185 153 3 228 184 153 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> {"length1":2,"length2":3,"data1":[1,2],"data2":["甲","乙","丙"]} | ||
``` | ||
'''</example>''' | ||
*/ | ||
function pack(schema, data, options, buffer) { | ||
/*<debug> | ||
console.log('pack(schema: %j)', schema); | ||
</debug>*/ | ||
if (!schema) { | ||
throw new Error('Parameter "schema" is undefined.'); | ||
function dependCreator(field, schemaCreator) { | ||
if (typeof field !== 'string') { | ||
throw new Error('Parameter "field" must be a string.'); | ||
} | ||
var root; | ||
if (!buffer) { | ||
buffer = []; | ||
root = true; | ||
if (typeof schemaCreator !== 'function') { | ||
throw new Error('Parameter "field" must be a function.'); | ||
} | ||
if (!(schema instanceof Schema)) { | ||
if (typeof schema === 'string') { | ||
schema = schemas[schema]; | ||
} | ||
} | ||
if (!schema) { | ||
throw new Error('Parameter "schema" is unregister.'); | ||
} | ||
options = options || {}; | ||
if (typeof options.littleEndian === 'undefined') { | ||
options.littleEndian = defaultLittleEndian; | ||
} | ||
if (typeof schema.pack === 'function') { | ||
schema.pack(data, options, buffer); | ||
} else { | ||
Object.keys(schema).forEach(function (key) { | ||
pack(schema[key], data[key], options, buffer); | ||
}); | ||
} | ||
if (root) { | ||
return arrayBufferFrom(buffer); | ||
} | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
if (!options.$scope) { | ||
throw new Error('Unpack must running in object.'); | ||
} | ||
var fieldValue = options.$scope[field]; | ||
if (typeof fieldValue === 'undefined') { | ||
throw new Error('Field "' + field + '" is undefined.'); | ||
} | ||
return Schema.unpack(schemaCreator(fieldValue), buffer, options, offsets); | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var fieldValue = options.$scope[field]; | ||
if (typeof fieldValue === 'undefined') { | ||
throw new Error('Field "' + field + '" is undefined.'); | ||
} | ||
Schema.pack(schemaCreator(fieldValue), value, options, buffer); | ||
}, | ||
namespace: 'depend', | ||
args: arguments | ||
}); | ||
} | ||
exports.pack = pack; | ||
var depend = Schema.together(dependCreator, function (fn, args) { | ||
fn.namespace = 'depend'; | ||
fn.args = args; | ||
}); | ||
Schema.register('depend', depend); | ||
function dependArray(field, itemSchema) { | ||
return depend(field, Schema.array(itemSchema)); | ||
} | ||
Schema.register('dependArray', dependArray); | ||
/** | ||
* 默认低字节序 | ||
* 构建解析类型,针对大小会改变的数据 | ||
* | ||
* @type {boolean} | ||
* @param {Function} encode 编码器 | ||
* @param {Function} decode 解码器 | ||
* @param {string|Schema} 内容数据格式 | ||
* @param {number|Schema} 大小或大小数据格式 | ||
* @return {Schema} 返回解析类型 | ||
'''<example>''' | ||
* @example parseCreator():_xor | ||
```js | ||
var _ = jpacks; | ||
var _xor = function _xor(buffer) { | ||
return buffer.slice().map(function (item) { | ||
return item ^ 127; | ||
}); | ||
}; | ||
var _schema = _.parse(_xor, _xor, 'float64', 8); | ||
console.log(_.stringify(_schema)); | ||
// -> parse(_xor,_xor,float64,8) | ||
var buffer = _.pack(_schema, 2.94296650666094e+189); | ||
console.log(buffer.join(' ')); | ||
// -> 24 58 92 126 7 41 75 111 | ||
console.log(JSON.stringify(_.unpack(_schema, buffer))); | ||
// -> 2.94296650666094e+189 | ||
``` | ||
'''</example>''' | ||
*/ | ||
var defaultLittleEndian; | ||
/** | ||
* 设置默认低字节序 | ||
* | ||
* @param {boolean} value 默认值 | ||
*/ | ||
function setDefaultLittleEndian(value) { | ||
defaultLittleEndian = value; | ||
function parseCreator(encode, decode, dataSchema, size) { | ||
if (typeof encode !== 'function') { | ||
throw new Error('Parameter "compress" must be a function.'); | ||
} | ||
if (typeof decode !== 'function') { | ||
throw new Error('Parameter "decompress" must be a function.'); | ||
} | ||
var schema = Schema.bytes(size); | ||
return new Schema({ | ||
unpack: function _unpack(buffer, options, offsets) { | ||
var bytes = decode(Schema.unpack(schema, buffer, options, offsets)); | ||
return Schema.unpack(dataSchema, bytes, options); | ||
}, | ||
pack: function _pack(value, options, buffer) { | ||
var bytes = encode(Schema.pack(dataSchema, value, options)); | ||
Schema.pack(schema, bytes, options, buffer); | ||
}, | ||
namespace: 'parse', | ||
args: arguments | ||
}); | ||
} | ||
exports.setDefaultLittleEndian = setDefaultLittleEndian; | ||
var parse = Schema.together(parseCreator, function (fn, args) { | ||
fn.namespace = 'parse'; | ||
fn.args = args; | ||
}); | ||
Schema.register('parse', parse); | ||
return Schema; | ||
} | ||
var root = create(); | ||
root.create = create; | ||
var exports = root; | ||
if (typeof define === 'function') { | ||
@@ -601,0 +1289,0 @@ if (define.amd || define.cmd) { |
@@ -5,3 +5,3 @@ { | ||
"description": "Binary data packing and unpacking.", | ||
"version": "0.0.4", | ||
"version": "0.1.2", | ||
"homepage": "http://github.com/zswang/jpacks", | ||
@@ -38,9 +38,12 @@ "main": "jpacks.js", | ||
"scripts": { | ||
"example": "jdists example.jdists.js -o test/example.js", | ||
"test": "istanbul cover --hook-run-in-context node_modules/mocha/bin/_mocha -- -R spec", | ||
"dist": "jdists src/jpacks.js -o jpacks.js && uglifyjs jpacks.js -o jpacks.min.js -p 5 -c -m", | ||
"mocha": "mocha", | ||
"dist": "jdists src/jpacks.js -o jpacks.js -r deubg,test,remove && uglifyjs jpacks.js -o jpacks.min.js -p 5 -c -m", | ||
"lint": "jshint src/*.js *.json" | ||
}, | ||
"files": [ | ||
"jpacks.js" | ||
"jpacks.js", | ||
"schemas-extend" | ||
] | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44205
4
1378
1