///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;