ProtoTools/server/PBMessageUtils.ts
2020-10-22 15:52:53 +08:00

387 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

///ts:import=ByteArray
import ByteArray = require('../../structure/ByteArray'); ///ts:import:generated
///ts:import=Endian
import Endian = require('../../structure/Endian'); ///ts:import:generated
// ///ts:import=PBMessage
///ts:import=Int64
import Int64 = require('../../structure/Int64'); ///ts:import:generated
const LITTLE_ENDIAN = Endian.LITTLE_ENDIAN;
const TYPE_DOUBLE: number = 1;
const TYPE_FLOAT: number = 2;
const TYPE_INT64: number = 3;
const TYPE_UINT64: number = 4;
const TYPE_INT32: number = 5;
const TYPE_FIXED64: number = 6;
const TYPE_FIXED32: number = 7;
const TYPE_BOOL: number = 8;
const TYPE_STRING: number = 9;
const TYPE_GROUP: number = 10;
const TYPE_MESSAGE: number = 11;
const TYPE_BYTES: number = 12;
const TYPE_UINT32: number = 13;
const TYPE_ENUM: number = 14;
const TYPE_SFIXED32: number = 15;
const TYPE_SFIXED64: number = 16;
const TYPE_SINT32: number = 17;
const TYPE_SINT64: number = 18;
/**
0 必有 属性名字
1 必有 required optional repeated
2 必有 数据类型
3 可选 消息类型名称
4 可选 默认值
*/
declare type MessageStruct =
[/**0 */string, /**1 */number,/**2 */number] |
[/**属性名字 */string, /**选项 */number,/**数据类型 */number,/**如果是子消息,消息名称 */string] |
[/**属性名字 */string, /**选项 */number,/**数据类型 */number,/**如果是子消息,消息名称 */string, /**默认值 */any]
/**
*
* @author 3tion
* javascript 只会使用到 varint32->number string boolean
*
*/
class PBMessageUtils {
/**
*
* 根据message名字得到的结构数据
* @static
* @type
*/
public static structByName: { /**消息名称*/[index: string]:
{
/**索引 */
[index: number]:
MessageStruct
}
} = {};
/**
*
* 根据message名字绑定的类名用于读取消息时创建的对象
* 如果没有注册,则直接使用{}创建对象
* @static
* @type {{ [index: string]: { new () } }}
*/
public static ctorByName: { [index: string]: { new () } } = {};
public static readFrom(msgType: string, bytes: ByteArray, len = -1): Object {
let ctor = PBMessageUtils.ctorByName[msgType];
let msg = ctor ? new ctor : {};
let afterLen = 0;
if (len > -1) {
afterLen = bytes.bytesAvailable - len;
}
let encode = PBMessageUtils.structByName[msgType];
if (!encode) {
Logger.error(ErrorType.Program, `非法的通信类型[${msgType}],堆栈信息:${new Error().stack}`);
return;
}
//检查处理默认值
for (let idx in encode) {
let body = encode[idx];
//0 key
//1 required optional repeated
//2 数据类型
//3 Message
//4 默认值
if (4 in body) {//有默认值
let key = body[0];
//消息中没有对应key的数据先赋值成默认值等待后续处理
if (!(key in msg)) {
msg[key] = body[4];
}
}
}
while (bytes.bytesAvailable > afterLen) {
let tag = bytes.readVarint();
if (tag == 0)
continue;
let idx = tag >>> 3;
let body = encode[idx];
if (!body) {
Logger.error(ErrorType.Program, `读取消息类型为:${msgType},索引${idx}时数据出现错误,找不到对应的数据结构配置`);
return;
}
let name = body[0];
let label = body[1];
let type = body[2];
let subMsgType = body[3];
let value: Object;
if (label != 3 || (tag & 0b111) != 7) {//自定义 tag & 0b111 == 7 为 数组中 undefined的情况
switch (type) {
case TYPE_DOUBLE:
value = bytes.readDouble();
break;
case TYPE_FLOAT:
value = bytes.readFloat();
break;
case TYPE_INT64:
case TYPE_UINT64:
case TYPE_SINT64:
value = bytes.readVarint64();
break;
case TYPE_INT32:
case TYPE_SINT32:
value = PBMessageUtils.decodeZigzag32(bytes.readVarint());
break;
case TYPE_UINT32:
case TYPE_ENUM:
value = bytes.readVarint();
break;
case TYPE_FIXED64:
case TYPE_SFIXED64:
value = PBMessageUtils.readFix64(bytes);//理论上项目不使用
break;
case TYPE_FIXED32:
value = bytes.readUnsignedInt();
break;
case TYPE_BOOL:
value = PBMessageUtils.readBoolean(bytes);
break;
case TYPE_STRING:
value = PBMessageUtils.readString(bytes);
break;
case TYPE_GROUP://(protobuf 已弃用)
value = undefined;
Logger.error(ErrorType.Program, `读取消息类型为:${msgType},索引${idx}时数据出现已弃用的GROUP分组类型(${TYPE_GROUP})`);
break;
case TYPE_MESSAGE://消息
value = PBMessageUtils.readMessage(bytes, subMsgType);
break;
case TYPE_BYTES:
value = PBMessageUtils.readBytes(bytes);
break;
case TYPE_SFIXED32:
value = bytes.readInt();
break;
default:
value = PBMessageUtils.readValue(tag, bytes);
}
}
if (label == 3) {//repeated
let arr = msg[name];
if (!arr) msg[name] = arr = [];
arr.push(value);
}
else {
msg[name] = value;
}
}
return msg;
}
private static readValue(tag: number, bytes: ByteArray): any {
let wireType: number = tag & 7;
let value: any;
switch (wireType) {
case 0: //Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
value = bytes.readVarint();
break;
case 2: //Length-delimi string, bytes, embedded messages, packed repeated fields
value = PBMessageUtils.readString(bytes);
break;
case 5: //32-bit fixed32, sfixed32, float
value = bytes.readInt();
break;
case 1: //64-bit fixed64, sfixed64, double
value = bytes.readDouble();
break;
//case 3://Start group Groups (deprecated)
//break;
//case 4://End group Groups (deprecated)
//break;
default:
Logger.error(ErrorType.Program, "protobuf的wireType未知");
}
return value;
}
private static readFix64(bytes: ByteArray): Object {
let v: Int64 = new Int64;
v.low = bytes.readUnsignedInt();
v.high = bytes.readUnsignedInt();
return v;
}
private static readBoolean(bytes: ByteArray): Object {
return bytes.readVarint() > 0;
}
private static readString(bytes: ByteArray): Object {
let blen: number = bytes.readVarint();
if (blen > 0) {
return bytes.readUTFBytes(blen);
}
return "";
}
/**
*
* 读取消息
* @private
* @static
* @param {number} tag 标签
* @param {ByteArray} bytes 被处理的字节数组
* @param {string} subMsgType 类型标识
* @returns {Object}
*/
private static readMessage(bytes: ByteArray, msgType: string): Object {
let blen: number = bytes.readVarint();
return PBMessageUtils.readFrom(msgType, bytes, blen);
}
private static readBytes(bytes: ByteArray): Object {
let blen: number = bytes.readVarint();
return bytes.readByteArray(blen);
}
public static writeTo(msg: Object, msgType: string, bytes?: ByteArray): ByteArray {
if (msg == undefined) {
return;
}
let messageEncode = PBMessageUtils.structByName[msgType];// msg.mMessageEncode;
if (!messageEncode) {
Logger.error(ErrorType.Program, `非法的通信类型[${msgType}],堆栈信息:${new Error().stack}`);
return;
}
if (!bytes) {
bytes = new ByteArray;
}
for (let numberStr in messageEncode) {
let num = +numberStr;
let body = messageEncode[num];
let label = body[1];
let name = body[0];
if (label == 1 && !(name in msg)) {
continue;
}
let value = msg[name];
if (value == undefined || value === body[4]/* 默认值 */) {
continue;
}
let type = body[2];
let subMsgType = body[3];
let wireType = PBMessageUtils.type2WireType(type);
let tag = (num << 3) | wireType;
if (label == 3) {
for (let key in value) {
let element = value[key];
// 针对repeated中无法处理空的占位数组做处理Protobuf 2 中不支持undefined进行占位 由于 wireType 只使用 0 1 2 3 4 5
// 现在使用 7 作为 undefined 占位使用
PBMessageUtils.writeElementTo(element, type, element == undefined ? ((num << 3) | 7) : tag, bytes, subMsgType);
}
}
else {
PBMessageUtils.writeElementTo(value, type, tag, bytes, subMsgType);
}
}
return bytes;
}
public static writeElementTo(value: any, type: number, tag: number, bytes: ByteArray, subMsgType?: string): void {
bytes.writeVarint(tag);
switch (type) {
case TYPE_FIXED32:
bytes.writeUnsignedInt(value as number);
break;
case TYPE_SFIXED32:
bytes.writeInt(value as number);
break;
case TYPE_FLOAT:
bytes.writeFloat(value as number);
break;
case TYPE_DOUBLE:
case TYPE_FIXED64:
case TYPE_SFIXED64:
bytes.writeDouble(value as number);
break;
case TYPE_INT32:
case TYPE_SINT32:
bytes.writeVarint(PBMessageUtils.zigzag32(value as number));
break;
case TYPE_ENUM:
case TYPE_UINT32:
bytes.writeVarint(value as number);
break;
case TYPE_INT64:
case TYPE_SINT64:
case TYPE_UINT64:
bytes.writeVarint64(value as Int64);
break;
case TYPE_BOOL:
bytes.writeVarint(value ? 1 : 0);
break;
case TYPE_STRING:
let length: number = Buffer.byteLength(value);
bytes.writeVarint(length);
bytes.writeUTFBytes(value);
break;
case TYPE_BYTES:
{
let temp: ByteArray = value as ByteArray;
let length = temp ? temp.length : 0;
bytes.writeVarint(length);
if (length > 0) {
bytes.writeBytes(temp, 0, length);
}
break;
}
case TYPE_MESSAGE:
{
let temp = PBMessageUtils.writeTo(value, subMsgType);
let length = temp ? temp.length : 0;
bytes.writeVarint(length);
if (length > 0) {
bytes.writeBytes(temp, 0, length);
}
break;
}
}
}
public static type2WireType(type: number): number {
switch (type) {
case TYPE_FIXED32:
case TYPE_SFIXED32:
case TYPE_FLOAT:
return 5;
case TYPE_DOUBLE:
case TYPE_FIXED64:
case TYPE_SFIXED64:
return 1;
case TYPE_INT32:
case TYPE_SINT32:
case TYPE_ENUM:
case TYPE_UINT32:
case TYPE_INT64:
case TYPE_SINT64:
case TYPE_UINT64:
case TYPE_BOOL:
return 0;
case TYPE_STRING:
case TYPE_MESSAGE:
case TYPE_BYTES:
return 2;
}
return -1;
}
public static zigzag32(n: number) {
return (n << 1) ^ (n >> 31);
}
public static decodeZigzag32(n: number) {
return n >> 1 ^ (((n & 1) << 31) >> 31);
}
}
export = PBMessageUtils;