JavaScript/ ECMA- 262 信件包
这是 JavaScript/ECMA-262 执行 邮件包,高效的二进制分泌格式:
https://msgpack.org/ (中文(简体) )
这个库是一个通用的JavaScript,意思是它与所有主要浏览器和NodeJS兼容. 此外,由于执行方式如下: 类型脚本,类型定义文件(d.ts)总是更新并捆绑在分发中.
注意这是JavaScript的MessagePack的第二个版本. 第一个版本在ES5中被执行,从未发布到npmjs.com,被标记为: 经典。 。 。 .
概要
import { deepStrictEqual } from "assert";
import { encode, decode } from "@eyhn/msgpack-stream";
const object = {
nil: null,
integer: 1,
float: Math.PI,
string: "Hello, world!",
binary: Uint8Array.from([1, 2, 3]),
array: [10, 20, 30],
map: { foo: "bar" },
timestampExt: new Date(),
};
const encoded: Uint8Array = encode(object);
deepStrictEqual(decode(encoded), object);
目录
- 概要
- 目录
- 安装
- API 密码
encode(data: unknown, options?: EncodeOptions): Uint8Arraydecode(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): unknowndecodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): Generator<unknown, void, unknown>decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): Promise<unknown>decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>- 重用编码器和解码器实例
- 扩展类型
- 正在解码 Blob
- 信件包规格
- 先决条件
- 基准
- 分发
- 德诺支持
- 维修
- 许可证
安装
此库发布到 npmjs.com 作为 @ eyhn/ msgpack- stream (英语)。 。 。 .
npm install @eyhn/msgpack-stream
API 密码
encode(data: unknown, options?: EncodeOptions): Uint8Array
密码 data 返回一个字节数组为 Uint8Array如果 data 属于或包括一个非连续物体,例如 function 或一个 symbol。 。 。 .
例如:
import { encode } from "@eyhn/msgpack-stream";
const encoded: Uint8Array = encode({ foo: "bar" });
console.log(encoded);
如果你想转换一个 uint8array 到节点JS Buffer,使用时 Buffer.from(arrayBuffer, offset, length) 为了不复制基础 ArrayBuffer时 Buffer.from(uint8array) 复制它:
import { encode } from "@eyhn/msgpack-stream";
const encoded: Uint8Array = encode({ foo: "bar" });
// `buffer` refers the same ArrayBuffer as `encoded`.
const buffer: Buffer = Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);
console.log(buffer);
EncodeOptions
| 名称 | 类型 | 默认 |
|---|---|---|
| 扩展代码 | 扩展编码 | ExtensionCodec.defaultCodec |
| 最大深度 | 编号 | 100 |
| 初始缓冲大小 | 编号 | 2048 |
| 排序键 | 布尔 | 虚假 |
| 部队32 | 布尔 | 虚假 |
| 强制整流器 | 布尔 | 虚假 |
| 忽略未定义 | 布尔 | 虚假 |
| 上下文 | 用户定义 | - 说吧 |
decode(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): unknown
它解码 buffer 中包含一个已编码信件对象,并返回已解码对象 unknown。 。 。 .
buffer 必须是字节数组, 通常是 Uint8Array 或者说 ArrayBuffer。 。 。 . BufferSource 定义为 ArrayBuffer | ArrayBufferView。 。 。 .
这个 buffer 必须包含单个编码对象。 如果 buffer 包括对象或对象之后的额外字节 buffer 这是空的,它扔 RangeError解码 buffer 包含多个编码对象,使用 decodeMulti() 或者说 decodeMultiStream() (推荐)取而代之.
例如:
import { decode } from "@eyhn/msgpack-stream";
const encoded: Uint8Array;
const object = decode(encoded);
console.log(object);
节点JS Buffer 也可以接受,因为它是 Uint8Array。 。 。 .
DecodeOptions
| 名称 | 类型 | 默认 |
|---|---|---|
| 扩展代码 | 扩展编码 | ExtensionCodec.defaultCodec |
| 最大边距 | 编号 | 4_294_967_295 (UINT32 MAX) (英语) |
| 最大边框 | 编号 | 4_294_967_295 (UINT32 MAX) (英语) |
| 最大箭头 | 编号 | 4_294_967_295 (UINT32 MAX) (英语) |
| 最大映射线 | 编号 | 4_294_967_295 (UINT32 MAX) (英语) |
| 最大扩展线 | 编号 | 4_294_967_295 (UINT32 MAX) (英语) |
| 上下文 | 用户定义 | - 说吧 |
你用得着 max${Type}Length 以限制每个类型已解码的长度.
decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): Generator<unknown, void, unknown>
它解码 buffer ,包括多个MessagePack编码对象,并返回解码对象作为生成器。 另见 decodeMultiStream(),这是此函数的一个同步变体.
此功能不推荐通过 I/O 流解码 MessagePack 二进制, 包括套接字, 因为它是同步的 。 相反, decodeMultiStream() 同步地解码一二进制流,通常花费更少的CPU和内存.
例如:
import { decode } from "@eyhn/msgpack-stream";
const encoded: Uint8Array;
for (const object of decodeMulti(encoded)) {
console.log(object);
}
decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): Promise<unknown>
它解码 stream时 ReadableStreamLike<T> 定义为 ReadableStream<T> | AsyncIterable<T>,在字节数组中,返回已解码的对象为 unknown 类型,以 Promise。 。 。 .
此函数同步工作, 并且可能比同步更高效地使用 CPU 资源 decode(),因为它不等待完成下载.
DecodeAsyncOptions 与 DecodeOptions 页:1 decode()。 。 。 .
此功能旨在与 whg 工作 fetch() 像这样:
import { decodeAsync } from "@eyhn/msgpack-stream";
const MSGPACK_TYPE = "application/x-msgpack";
const response = await fetch(url);
const contentType = response.headers.get("Content-Type");
if (contentType && contentType.startsWith(MSGPACK_TYPE) && response.body != null) {
const object = await decodeAsync(response.body);
// do something with object
} else { /* handle errors */ }
decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>
一样 decodeAsync(),但仅接受 stream 中包含一系列项目,并逐一发出已解码的项目.
例如:
import { decodeArrayStream } from "@eyhn/msgpack-stream";
const stream: AsyncIterator<Uint8Array>;
// in an async function:
for await (const item of decodeArrayStream(stream)) {
console.log(item);
}
decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>
一样 decodeAsync() 财务报告和审定财务报表 decodeArrayStream()输入 stream 必须包含多个信息包编码项目。 这是一个同步变体,用于 decodeMulti()。 。 。 .
换句话说,它可以解码无限流并逐一出出解码项目.
例如:
import { decodeMultiStream } from "@eyhn/msgpack-stream";
const stream: AsyncIterator<Uint8Array>;
// in an async function:
for await (const item of decodeMultiStream(stream)) {
console.log(item);
}
此函数自 v 2. 4. 0 起可用;以前称为 decodeStream()。 。 。 .
重用编码器和解码器实例
Encoder 财务报告和审定财务报表 Decoder 类别通过重用实例来提高性能:
import { deepStrictEqual } from "assert";
import { Encoder, Decoder } from "@eyhn/msgpack-stream";
const encoder = new Encoder();
const decoder = new Decoder();
const encoded: Uint8Array = encoder.encode(object);
deepStrictEqual(decoder.decode(encoded), object);
根据我们的基准 重用 Encoder 例速约20%
比起来 encode() 函数和重用 Decoder 实例速度约2%
比起来 decode() 函数。 注意其结果应因环境而异
和数据结构.
扩展类型
处理 信件包扩展类型,该图书馆提供 ExtensionCodec 课.
这是设置自定义扩展类型处理的示例 Map 财务报告和审定财务报表 Set 类型脚本中的类 :
import { encode, decode, ExtensionCodec } from "@eyhn/msgpack-stream";
const extensionCodec = new ExtensionCodec();
// Set<T>
const SET_EXT_TYPE = 0 // Any in 0-127
extensionCodec.register({
type: SET_EXT_TYPE,
encode: (object: unknown): Uint8Array | null => {
if (object instanceof Set) {
return encode([...object]);
} else {
return null;
}
},
decode: (data: Uint8Array) => {
const array = decode(data) as Array<unknown>;
return new Set(array);
},
});
// Map<T>
const MAP_EXT_TYPE = 1; // Any in 0-127
extensionCodec.register({
type: MAP_EXT_TYPE,
encode: (object: unknown): Uint8Array => {
if (object instanceof Map) {
return encode([...object]);
} else {
return null;
}
},
decode: (data: Uint8Array) => {
const array = decode(data) as Array<[unknown, unknown]>;
return new Map(array);
},
});
const encoded = encode([new Set<any>(), new Map<any, any>()], { extensionCodec });
const decoded = decode(encoded, { extensionCodec });
不是自定义对象的扩展类型必须是 [0, 127]时 [-1, -128] 保留给 MessagePack 本身.
扩展Codec 上下文
当您使用扩展编码器时,可能需要有编码/解码状态来跟踪哪些对象被编码/再创建. 要做到这一点,通过一个 context 页:1 EncodeOptions 财务报告和审定财务报表 DecodeOptions数字 :
import { encode, decode, ExtensionCodec } from "@eyhn/msgpack-stream";
class MyContext {
track(object: any) { /*...*/ }
}
class MyType { /* ... */ }
const extensionCodec = new ExtensionCodec<MyContext>();
// MyType
const MYTYPE_EXT_TYPE = 0 // Any in 0-127
extensionCodec.register({
type: MYTYPE_EXT_TYPE,
encode: (object, context) => {
if (object instanceof MyType) {
context.track(object); // <-- like this
return encode(object.toJSON(), { extensionCodec, context });
} else {
return null;
}
},
decode: (data, extType, context) => {
const decoded = decode(data, { extensionCodec, context });
const my = new MyType(decoded);
context.track(my); // <-- and like this
return my;
},
});
// and later
import { encode, decode } from "@eyhn/msgpack-stream";
const context = new MyContext();
const encoded = = encode({myType: new MyType<any>()}, { extensionCodec, context });
const decoded = decode(encoded, { extensionCodec, context });
处理扩展大小
此库不默认处理 BigInt, 但您可以用 ExtensionCodec 像这样:
import { deepStrictEqual } from "assert";
import { encode, decode, ExtensionCodec } from "@eyhn/msgpack-stream";
const BIGINT_EXT_TYPE = 0; // Any in 0-127
const extensionCodec = new ExtensionCodec();
extensionCodec.register({
type: BIGINT_EXT_TYPE,
encode: (input: unknown) => {
if (typeof input === "bigint") {
if (input <= Number.MAX_SAFE_INTEGER && input >= Number.MIN_SAFE_INTEGER) {
return encode(parseInt(input.toString(), 10));
} else {
return encode(input.toString());
}
} else {
return null;
}
},
decode: (data: Uint8Array) => {
return BigInt(decode(data));
},
});
const value = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1);
const encoded: = encode(value, { extensionCodec });
deepStrictEqual(decode(encoded, { extensionCodec }), value);
时间模块作为时间戳扩展
有人提议在JavaScript中提出新的日期/时间说明:
这个图书馆地图 Date 切换到 MessagePack 时间戳扩展,但您可以重新绘制时间模块(或 时间多填充像这样的时间戳扩展:
import { Instant } from "@std-proposal/temporal";
import { deepStrictEqual } from "assert";
import {
encode,
decode,
ExtensionCodec,
EXT_TIMESTAMP,
encodeTimeSpecToTimestamp,
decodeTimestampToTimeSpec,
} from "@eyhn/msgpack-stream";
const extensionCodec = new ExtensionCodec();
extensionCodec.register({
type: EXT_TIMESTAMP, // override the default behavior!
encode: (input: any) => {
if (input instanceof Instant) {
const sec = input.seconds;
const nsec = Number(input.nanoseconds - BigInt(sec) * BigInt(1e9));
return encodeTimeSpecToTimestamp({ sec, nsec });
} else {
return null;
}
},
decode: (data: Uint8Array) => {
const timeSpec = decodeTimestampToTimeSpec(data);
const sec = BigInt(timeSpec.sec);
const nsec = BigInt(timeSpec.nsec);
return Instant.fromEpochNanoseconds(sec * BigInt(1e9) + nsec);
},
});
const instant = Instant.fromEpochMilliseconds(Date.now());
const encoded = encode(instant, { extensionCodec });
const decoded = decode(encoded, { extensionCodec });
deepStrictEqual(decoded, instant);
如果时间模块实现标准化,这将成为本库中带有主要版本递增的默认.
正在解码 Blob
Blob 是浏览器提供的二进制数据容器。 要读取其内容, 您可以使用 Blob#arrayBuffer() 或者说 Blob#stream()。 。 。 . Blob#stream()
如果目标平台支持,则推荐。 这是因为流出
对大型物体来说,解码速度应该更快。 两种方式,你都需要使用
一个同步 API.
async function decodeFromBlob(blob: Blob): unknown {
if (blob.stream) {
// Blob#stream(): ReadableStream<Uint8Array> (recommended)
return await decodeAsync(blob.stream());
} else {
// Blob#arrayBuffer(): Promise<ArrayBuffer> (if stream() is not available)
return decode(await blob.arrayBuffer());
}
}
信件包规格
本库与MessagePack规格的"2017年8月"修订版相兼容,此时已添加了时间戳分机:
- str/bin 离职,2013年8月增加
- 扩展类型,2013年8月增加
- 时间戳分机类型,2017年8月添加
生活规格如下:
https://github.com/msgpack/msgpack 页面存档备份,存于互联网档案馆
请注意,截至2019年6月,MessagePack规格上没有正式"版本". 见 https://github.com/msgpack/msgpack/issues/195 互联网档案馆的存檔,存档日期2011-12-02 讨论.
信件包映射表
下表显示 JavaScript 值的映射方式 信件包格式 而反之亦然.
| 源值 | 信件包格式 | 数值已解码 |
|---|---|---|
| 无效, 未定义 | 无 | 无效(*1) |
| 布尔( 真, 假) | 布尔家族 | 布尔( 真, 假) |
| 数字( 53- 位英寸) | 输入家庭 | 数字( 53- 位英寸) |
| 数字( 64- 位浮点) | 浮动家族 | 数字( 64- 位浮点) |
| 字符串 | 家庭关系 | 字符串 |
| 线条缓冲维尤 | 宾家 | Uint8 箭头 (*2) |
| 阵列 | 数组家族 | 阵列 |
| 对象 | 地图家族 | 对象(*3) |
| 日期 | 时间戳分机家族 | 日期(*4) |
- *1 两者兼有
null财务报告和审定财务报表undefined被映射到nil(单位:千美元)0xC0)类型,并被解码为null - *2 任意
ArrayBufferView包括节点JSBuffer被映射到bin并解码为Uint8Array - *3 处理
Object,它被视为Record<string, unknown>在类型脚本方面 - *4 MessagePack时间戳可能有纳秒,解码为JavaScript后会丢失
Date。可以通过注册来推翻这种行为-1用于扩展编码器.
先决条件
这是一个通用的JavaScript库,支持主要浏览器和节点JS.
ECMA-262号
- ES5语言特性
- ES2018标准库,包括:
- 已键入数组( ES2015)
- 合成(ES2018)
- ES2015-ES2018中添加的地物
本库使用的标准 ES2018 库可以多填充 核心 js 数据。 。 。 .
如果支持 IE11,则导入 core-js 在应用程序条目点中,如同此库在测试浏览器时所做的那样.
节点JS
需要NodeJS v10,但NodeJS v12或后期推荐,因为它包括了V8的特性 改进V8中的数据视频性能。 。 。 .
V10 前的节点JS 将通过导入工作 @eyhn/msgpack-stream/dist.es2015+umd/msgpack。 。 。 .
类型脚本编译器/ 类型定义
本单元需要类型定义 AsyncIterator, (中文(简体) ), SourceBuffer,Whatwg溪流,等等。 由下列机构提供: "lib": ["ES2021", "DOM"] 内 tsconfig.json。 。 。 .
关于TypeScript编译器版本,只在开发中测试最新的TypeScript.
基准
运行时间性能并不是使用MessagePack的唯一原因,重要的是选择MessagePack库,因此提供了一套基准套件来监控这个库的性能.
V8的内置JSON多年得到改进,esp. JSON.parse() 实值 v8/7.6号文件显著改进如基准结果bellow所示,它是截至2019年最快的去序列器.
然而,MessagePack可以有效处理二进制数据,实际性能取决于情况. 如果表现很重要,你最好用自己的用法做基准.
节点JS/v18.1.0的基准(V8/10.1)
| 操作 | 执行 | 分钟 | 行动 |
|---|---|---|---|
| buf = 从(JSON.stringify(obj)); | 902100 (英语) | 5000块 | 180420 (英语) |
| obj = JSON.parse(buf.toString ("utf-8")); | 898700 (英语) | 5000块 | 179740 (英语) |
| buf = 需要 ("msgpack-lite"). encode(obj;); | 411000 (411000) (中文(简体) ) | 5000块 | 82200 (英语) |
| obj = 要求 ("msgpack-lite").decode (buf;); | 246200 (英语) | 5001 (简体中文) | 49230 (英语) |
| buf = 要求 (""@eyhn/msgpack-stream"). encode(obj;); | 843300 (英语) | 5000块 | 168660 (英语) |
| obj = 需要 (""@eyhn/msgpack-stream").decode(buf; ); | 489300 (英语) | 5000块 | 97860 (英语) |
| buf=/ *@eyhn/msgpack-stream */编码器.encode(obj); | 第1154200号 | 5000块 | 230840 (英语) |
| obj = / * @eyhn/msgpack-stream */decoder.decode(buf) ; (中文(简体) ); | 448900 (英语) | 5000块 | 89780 (英语) |
请注意: JSON 案件使用情况 Buffer 用于模拟 JavaScript 字符串必须被转换成UTF-8编码的字节数组的 I/O, 而MessagePack 模块处理字节数组.
分发
国家预防机制/npmjs.com
以npmjs.com分发的国家预防机制包包括ES2015+和ES2015文件:
dist/由CommomJS编译为ES2019,为NodeJS v10提供dist.es2015+umd/与UMD编译为ES2015dist.es2015+umd/msgpack.min.js- 已修改的文件dist.es2015+umd/msgpack.js- 未缩小的文件
dist.es2015+esm/汇编成ES2015,并配有ES模块,为类似网络包装的捆绑器和NodeJS的无害环境管理模式提供
如果使用NodeJS和/或webpack,他们的模块解析器会自动使用合适的.
CDN / unpkg.com (中文(简体) )
此库可通过 CDN 访问 :
<script crossorigin src="https://unpkg.com/@eyhn/msgpack-stream"></script>
装入 MessagePack 模块.
德诺支持
你可以在德诺使用这个模块.
见 example/deno-*.ts 用于实例.
deno.land/x 尚未支持.
维修
测试
简单的测试:
npm run test
持续整合
这个图书馆使用Travis CI.
测试矩阵 :
- 类型脚本目标
target=es2019编号 :target=es5
- JavaScript 引擎
- 节点JS,浏览器(Chrome,Firefox,Safari,IE11等)
见 测试:* 在包件.json中 财务报告和审定财务报表 .travis.yml (英语) 详细情况.
单元工程
# run tests on NodeJS, Chrome, and Firefox
make test-all
# edit the changelog
code CHANGELOG.md
# bump version
npm version patch|minor|major
# run the publishing task
make publish
更新依赖关系
npm run update-dependencies
许可证
版权2019 腾讯网.
本软件使用ISC许可证:
https://opensource.org/licenses/ISC 页面存档备份,存于互联网档案馆
见 自由主义 详细情况.