import { readInt64, writeInt64 } from './longbits.js'; import ProtocolManager from '../ProtocolManager.js'; const empty_str = ''; const initSize = 128; const maxSize = 655537; const maxShort = 32767; const minShort = -32768; const maxInt = 2147483647; const minInt = -2147483648; // UTF-8编码与解码 // const encoder = new TextEncoder('utf-8'); // const decoder = new TextDecoder('utf-8'); // nodejs的测试环境需要用以下方式特殊处理 const util = require('util'); const encoder = new util.TextEncoder('utf-8'); const decoder = new util.TextDecoder('utf-8'); // 在js中long可以支持的最大值 // const maxLong = 9007199254740992; // const minLong = -9007199254740992; const copy = function copy(original, newLength) { if (original.byteLength > newLength) { throw new Error('newLength is too small'); } const dst = new ArrayBuffer(newLength); new Uint8Array(dst).set(new Uint8Array(original)); return dst; }; function encodeZigzagInt(n) { // 有效位左移一位+符号位右移31位 return (n << 1) ^ (n >> 31); } function decodeZigzagInt(n) { return (n >>> 1) ^ -(n & 1); } const ByteBuffer = function() { this.writeOffset = 0; this.readOffset = 0; this.buffer = new ArrayBuffer(initSize); this.bufferView = new DataView(this.buffer, 0, this.buffer.byteLength); this.setWriteOffset = function(writeOffset) { if (writeOffset > this.buffer.byteLength) { throw new Error('index out of bounds exception: readerIndex: ' + this.readOffset + ', writerIndex: ' + this.writeOffset + '(expected: 0 <= readerIndex <= writerIndex <= capacity:' + this.buffer.byteLength); } this.writeOffset = writeOffset; }; this.setReadOffset = function(readOffset) { if (readOffset > this.writeOffset) { throw new Error('index out of bounds exception: readerIndex: ' + this.readOffset + ', writerIndex: ' + this.writeOffset + '(expected: 0 <= readerIndex <= writerIndex <= capacity:' + this.buffer.byteLength); } this.readOffset = readOffset; }; this.getCapacity = function() { return this.buffer.byteLength - this.writeOffset; }; this.ensureCapacity = function(minCapacity) { while (minCapacity - this.getCapacity() > 0) { const newSize = this.buffer.byteLength * 2; if (newSize > maxSize) { throw new Error('out of memory error'); } this.buffer = copy(this.buffer, newSize); this.bufferView = new DataView(this.buffer, 0, this.buffer.byteLength); } }; this.isReadable = function() { return this.writeOffset > this.readOffset; }; this.writeBoolean = function(value) { if (!(value === true || value === false)) { throw new Error('value must be true of false'); } this.ensureCapacity(1); if (value === true) { this.bufferView.setInt8(this.writeOffset, 1); } else { this.bufferView.setInt8(this.writeOffset, 0); } this.writeOffset++; }; this.readBoolean = function() { const value = this.bufferView.getInt8(this.readOffset); this.readOffset++; return (value === 1); }; this.writeBytes = function(byteArray) { const length = byteArray.byteLength; this.ensureCapacity(length); new Uint8Array(this.buffer).set(new Uint8Array(byteArray), this.writeOffset); this.writeOffset += length; }; this.writeByte = function(value) { this.ensureCapacity(1); this.bufferView.setInt8(this.writeOffset, value); this.writeOffset++; }; this.readByte = function() { const value = this.bufferView.getInt8(this.readOffset); this.readOffset++; return value; }; this.writeShort = function(value) { if (!(minShort <= value && value <= maxShort)) { throw new Error('value must range between minShort:-32768 and maxShort:32767'); } this.ensureCapacity(2); this.bufferView.setInt16(this.writeOffset, value); this.writeOffset += 2; }; this.readShort = function() { const value = this.bufferView.getInt16(this.readOffset); this.readOffset += 2; return value; }; this.writeRawInt = function(value) { if (!(minInt <= value && value <= maxInt)) { throw new Error('value must range between minInt:-2147483648 and maxInt:2147483647'); } this.ensureCapacity(4); this.bufferView.setInt32(this.writeOffset, value); this.writeOffset += 4; }; this.readRawInt = function() { const value = this.bufferView.getInt32(this.readOffset); this.readOffset += 4; return value; }; this.writeInt = function(value) { if (!(minInt <= value && value <= maxInt)) { throw new Error('value must range between minInt:-2147483648 and maxInt:2147483647'); } this.ensureCapacity(5); value = encodeZigzagInt(value); if (value >>> 7 === 0) { this.writeByte(value); return; } if (value >>> 14 === 0) { this.writeByte((value & 0x7F) | 0x80); this.writeByte((value >>> 7)); return; } if (value >>> 21 === 0) { this.writeByte((value & 0x7F) | 0x80); this.writeByte((value >>> 7 | 0x80)); this.writeByte(value >>> 14); return; } if (value >>> 28 === 0) { this.writeByte((value & 0x7F) | 0x80); this.writeByte((value >>> 7 | 0x80)); this.writeByte((value >>> 14 | 0x80)); this.writeByte(value >>> 21); return; } this.writeByte((value & 0x7F) | 0x80); this.writeByte((value >>> 7 | 0x80)); this.writeByte((value >>> 14 | 0x80)); this.writeByte((value >>> 21 | 0x80)); this.writeByte(value >>> 28); }; this.readInt = function() { let b = this.readByte(); let value = b & 0x7F; if ((b & 0x80) !== 0) { b = this.readByte(); value |= (b & 0x7F) << 7; if ((b & 0x80) !== 0) { b = this.readByte(); value |= (b & 0x7F) << 14; if ((b & 0x80) !== 0) { b = this.readByte(); value |= (b & 0x7F) << 21; if ((b & 0x80) !== 0) { b = this.readByte(); value |= (b & 0x7F) << 28; } } } } return decodeZigzagInt(value); }; this.writeLong = function(value) { if (value === null || value === undefined) { throw new Error('value must not be null'); } this.ensureCapacity(9); writeInt64(this, value); }; this.readLong = function() { const buffer = new ArrayBuffer(9); const bufferView = new DataView(buffer, 0, buffer.byteLength); let count = 0; let b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); if ((b & 0x80) !== 0) { b = this.readByte(); bufferView.setUint8(count++, b); } } } } } } } } return readInt64(new Uint8Array(buffer.slice(0, count))).toString(); }; this.writeFloat = function(value) { if (value === null || value === undefined) { throw new Error('value must not be null'); } this.ensureCapacity(4); this.bufferView.setFloat32(this.writeOffset, value); this.writeOffset += 4; }; this.readFloat = function() { const value = this.bufferView.getFloat32(this.readOffset); this.readOffset += 4; return value; }; this.writeDouble = function(value) { if (value === null || value === undefined) { throw new Error('value must not be null'); } this.ensureCapacity(8); this.bufferView.setFloat64(this.writeOffset, value); this.writeOffset += 8; }; this.readDouble = function() { const value = this.bufferView.getFloat64(this.readOffset); this.readOffset += 8; return value; }; this.writeChar = function(value) { if (value === null || value === undefined || value.length === 0) { this.writeString(empty_str); return; } this.writeString(value.charAt(0)); }; this.readChar = function() { return this.readString(); }; this.writeString = function(value) { if (value === null || value === undefined || value.trim().length === 0) { this.writeInt(0); return; } const uint8Array = encoder.encode(value); this.ensureCapacity(5 + uint8Array.length); this.writeInt(uint8Array.length); uint8Array.forEach((value) => this.writeByte(value)); }; this.readString = function() { const length = this.readInt(); if (length <= 0) { return empty_str; } const uint8Array = new Uint8Array(this.buffer.slice(this.readOffset, this.readOffset + length)); const value = decoder.decode(uint8Array); this.readOffset += length; return value; }; this.toBytes = function() { const result = new ArrayBuffer(this.writeOffset); new Uint8Array(result).set(new Uint8Array(this.buffer.slice(0, this.writeOffset))); return result; }; this.writePacketFlag = function(value) { const flag = (value === null) || (value === undefined); this.writeBoolean(!flag); return flag; }; this.writePacket = function(packet, protocolId) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); protocolRegistration.write(this, packet); }; this.readPacket = function(protocolId) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); return protocolRegistration.read(this); }; this.writeBooleanArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeBoolean(element); }); } }; this.readBooleanArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readBoolean()); } } return array; }; this.writeByteArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeByte(element); }); } }; this.readByteArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readByte()); } } return array; }; this.writeShortArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeShort(element); }); } }; this.readShortArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readShort()); } } return array; }; this.writeIntArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeInt(element); }); } }; this.readIntArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readInt()); } } return array; }; this.writeLongArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeLong(element); }); } }; this.readLongArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readLong()); } } return array; }; this.writeFloatArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeFloat(element); }); } }; this.readFloatArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readFloat()); } } return array; }; this.writeDoubleArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeDouble(element); }); } }; this.readDoubleArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readDouble()); } } return array; }; this.writeStringArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeString(element); }); } }; this.readStringArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readString()); } } return array; }; this.writeCharArray = function(array) { if (array === null) { this.writeInt(0); } else { this.writeInt(array.length); array.forEach(element => { this.writeChar(element); }); } }; this.readCharArray = function() { const array = []; const length = this.readInt(); if (length > 0) { for (let index = 0; index < length; index++) { array.push(this.readChar()); } } return array; }; this.writePacketArray = function(array, protocolId) { if (array === null) { this.writeInt(0); } else { const protocolRegistration = ProtocolManager.getProtocol(protocolId); this.writeInt(array.length); array.forEach(element => { protocolRegistration.write(this, element); }); } }; this.readPacketArray = function(protocolId) { const array = []; const length = this.readInt(); if (length > 0) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); for (let index = 0; index < length; index++) { array.push(protocolRegistration.read(this)); } } return array; }; this.writeIntIntMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeInt(key); this.writeInt(value); }); } }; this.readIntIntMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readInt(); const value = this.readInt(); map.set(key, value); } } return map; }; this.writeIntLongMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeInt(key); this.writeLong(value); }); } }; this.readIntLongMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readInt(); const value = this.readLong(); map.set(key, value); } } return map; }; this.writeIntStringMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeInt(key); this.writeString(value); }); } }; this.readIntStringMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readInt(); const value = this.readString(); map.set(key, value); } } return map; }; this.writeIntPacketMap = function(map, protocolId) { if (map === null) { this.writeInt(0); } else { const protocolRegistration = ProtocolManager.getProtocol(protocolId); this.writeInt(map.size); map.forEach((value, key) => { this.writeInt(key); protocolRegistration.write(this, value); }); } }; this.readIntPacketMap = function(protocolId) { const map = new Map(); const size = this.readInt(); if (size > 0) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); for (let index = 0; index < size; index++) { const key = this.readInt(); const value = protocolRegistration.read(this); map.set(key, value); } } return map; }; this.writeLongIntMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeLong(key); this.writeInt(value); }); } }; this.readLongIntMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readLong(); const value = this.readInt(); map.set(key, value); } } return map; }; this.writeLongLongMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeLong(key); this.writeLong(value); }); } }; this.readLongLongMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readLong(); const value = this.readLong(); map.set(key, value); } } return map; }; this.writeLongStringMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeLong(key); this.writeString(value); }); } }; this.readLongStringMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readLong(); const value = this.readString(); map.set(key, value); } } return map; }; this.writeLongPacketMap = function(map, protocolId) { if (map === null) { this.writeInt(0); } else { const protocolRegistration = ProtocolManager.getProtocol(protocolId); this.writeInt(map.size); map.forEach((value, key) => { this.writeLong(key); protocolRegistration.write(this, value); }); } }; this.readLongPacketMap = function(protocolId) { const map = new Map(); const size = this.readInt(); if (size > 0) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); for (let index = 0; index < size; index++) { const key = this.readLong(); const value = protocolRegistration.read(this); map.set(key, value); } } return map; }; this.writeStringIntMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeString(key); this.writeInt(value); }); } }; this.readStringIntMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readString(); const value = this.readInt(); map.set(key, value); } } return map; }; this.writeStringLongMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeString(key); this.writeLong(value); }); } }; this.readStringLongMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readString(); const value = this.readLong(); map.set(key, value); } } return map; }; this.writeStringStringMap = function(map) { if (map === null) { this.writeInt(0); } else { this.writeInt(map.size); map.forEach((value, key) => { this.writeString(key); this.writeString(value); }); } }; this.readStringStringMap = function() { const map = new Map(); const size = this.readInt(); if (size > 0) { for (let index = 0; index < size; index++) { const key = this.readString(); const value = this.readString(); map.set(key, value); } } return map; }; this.writeStringPacketMap = function(map, protocolId) { if (map === null) { this.writeInt(0); } else { const protocolRegistration = ProtocolManager.getProtocol(protocolId); this.writeInt(map.size); map.forEach((value, key) => { this.writeString(key); protocolRegistration.write(this, value); }); } }; this.readStringPacketMap = function(protocolId) { const map = new Map(); const size = this.readInt(); if (size > 0) { const protocolRegistration = ProtocolManager.getProtocol(protocolId); for (let index = 0; index < size; index++) { const key = this.readString(); const value = protocolRegistration.read(this); map.set(key, value); } } return map; }; }; export default ByteBuffer;