mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
204 lines
6.1 KiB
TypeScript
204 lines
6.1 KiB
TypeScript
// 解码 OneTalk 的同步推送数据
|
|
|
|
export type MessagePackValue =
|
|
| null
|
|
| boolean
|
|
| number
|
|
| bigint
|
|
| string
|
|
| Uint8Array
|
|
| MessagePackValue[]
|
|
| { [key: string]: MessagePackValue };
|
|
|
|
class MessagePackDecoder {
|
|
private readonly bytes: Uint8Array;
|
|
private readonly view: DataView;
|
|
private readonly textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
private offset = 0;
|
|
|
|
constructor(bytes: Uint8Array) {
|
|
this.bytes = bytes;
|
|
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
}
|
|
|
|
get done(): boolean {
|
|
return this.offset === this.bytes.byteLength;
|
|
}
|
|
|
|
private decodeArray(length: number): MessagePackValue[] {
|
|
return Array.from({ length }, () => this.decode());
|
|
}
|
|
|
|
private decodeMap(length: number): { [key: string]: MessagePackValue } {
|
|
const result: { [key: string]: MessagePackValue } = {};
|
|
|
|
for (let index = 0; index < length; index += 1) {
|
|
const key = this.decode();
|
|
if (typeof key !== "string" && typeof key !== "number" && typeof key !== "bigint") {
|
|
throw new Error("Unsupported MessagePack map key");
|
|
}
|
|
result[String(key)] = this.decode();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private decodeString(length: number): string {
|
|
return this.textDecoder.decode(this.readBytes(length));
|
|
}
|
|
|
|
private readBytes(length: number): Uint8Array {
|
|
this.ensureAvailable(length);
|
|
const start = this.offset;
|
|
this.offset += length;
|
|
return this.bytes.subarray(start, this.offset);
|
|
}
|
|
|
|
private readUint8(): number {
|
|
this.ensureAvailable(1);
|
|
return this.view.getUint8(this.offset++);
|
|
}
|
|
|
|
private readInt8(): number {
|
|
this.ensureAvailable(1);
|
|
const value = this.view.getInt8(this.offset);
|
|
this.offset += 1;
|
|
return value;
|
|
}
|
|
|
|
private readUint16(): number {
|
|
this.ensureAvailable(2);
|
|
const value = this.view.getUint16(this.offset);
|
|
this.offset += 2;
|
|
return value;
|
|
}
|
|
|
|
private readInt16(): number {
|
|
this.ensureAvailable(2);
|
|
const value = this.view.getInt16(this.offset);
|
|
this.offset += 2;
|
|
return value;
|
|
}
|
|
|
|
private readUint32(): number {
|
|
this.ensureAvailable(4);
|
|
const value = this.view.getUint32(this.offset);
|
|
this.offset += 4;
|
|
return value;
|
|
}
|
|
|
|
private readInt32(): number {
|
|
this.ensureAvailable(4);
|
|
const value = this.view.getInt32(this.offset);
|
|
this.offset += 4;
|
|
return value;
|
|
}
|
|
|
|
private readBigUint64(): bigint {
|
|
this.ensureAvailable(8);
|
|
const value = this.view.getBigUint64(this.offset);
|
|
this.offset += 8;
|
|
return value;
|
|
}
|
|
|
|
private readBigInt64(): bigint {
|
|
this.ensureAvailable(8);
|
|
const value = this.view.getBigInt64(this.offset);
|
|
this.offset += 8;
|
|
return value;
|
|
}
|
|
|
|
private readFloat32(): number {
|
|
this.ensureAvailable(4);
|
|
const value = this.view.getFloat32(this.offset);
|
|
this.offset += 4;
|
|
return value;
|
|
}
|
|
|
|
private readFloat64(): number {
|
|
this.ensureAvailable(8);
|
|
const value = this.view.getFloat64(this.offset);
|
|
this.offset += 8;
|
|
return value;
|
|
}
|
|
|
|
private ensureAvailable(length: number): void {
|
|
if (length < 0 || this.offset + length > this.bytes.byteLength) {
|
|
throw new Error("Unexpected end of MessagePack data");
|
|
}
|
|
}
|
|
|
|
/** 解码一个完整的 MessagePack 值。 */
|
|
decode(): MessagePackValue {
|
|
const prefix = this.readUint8();
|
|
|
|
if (prefix <= 0x7f) return prefix;
|
|
if (prefix >= 0xe0) return prefix - 0x100;
|
|
if ((prefix & 0xf0) === 0x80) return this.decodeMap(prefix & 0x0f);
|
|
if ((prefix & 0xf0) === 0x90) return this.decodeArray(prefix & 0x0f);
|
|
if ((prefix & 0xe0) === 0xa0) return this.decodeString(prefix & 0x1f);
|
|
|
|
switch (prefix) {
|
|
case 0xc0:
|
|
return null;
|
|
case 0xc2:
|
|
return false;
|
|
case 0xc3:
|
|
return true;
|
|
case 0xc4:
|
|
return this.readBytes(this.readUint8());
|
|
case 0xc5:
|
|
return this.readBytes(this.readUint16());
|
|
case 0xc6:
|
|
return this.readBytes(this.readUint32());
|
|
case 0xca:
|
|
return this.readFloat32();
|
|
case 0xcb:
|
|
return this.readFloat64();
|
|
case 0xcc:
|
|
return this.readUint8();
|
|
case 0xcd:
|
|
return this.readUint16();
|
|
case 0xce:
|
|
return this.readUint32();
|
|
case 0xcf:
|
|
return this.readBigUint64();
|
|
case 0xd0:
|
|
return this.readInt8();
|
|
case 0xd1:
|
|
return this.readInt16();
|
|
case 0xd2:
|
|
return this.readInt32();
|
|
case 0xd3:
|
|
return this.readBigInt64();
|
|
case 0xd9:
|
|
return this.decodeString(this.readUint8());
|
|
case 0xda:
|
|
return this.decodeString(this.readUint16());
|
|
case 0xdb:
|
|
return this.decodeString(this.readUint32());
|
|
case 0xdc:
|
|
return this.decodeArray(this.readUint16());
|
|
case 0xdd:
|
|
return this.decodeArray(this.readUint32());
|
|
case 0xde:
|
|
return this.decodeMap(this.readUint16());
|
|
case 0xdf:
|
|
return this.decodeMap(this.readUint32());
|
|
default:
|
|
throw new Error(`Unsupported MessagePack prefix 0x${prefix.toString(16)}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 解码 Base64 编码的 OneTalk 同步推送数据。 */
|
|
export function decodeOneTalkSyncPushData(encoded: string): MessagePackValue {
|
|
const binary = atob(encoded);
|
|
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
const decoder = new MessagePackDecoder(bytes);
|
|
const value = decoder.decode();
|
|
|
|
if (!decoder.done) throw new Error("Unexpected trailing MessagePack bytes");
|
|
return value;
|
|
}
|