/** * ByteBuffer.js * 1,字节流基本类型封装 * 2,long,int64类型实现 * 3,utf8编码实现 * * 字节流打包: * var buffer=new ByteBuffer(128); * buffer.writeString("test string"); * buffer.writeHex(0x04); * buffer.writeChar("|"); * buffer.writeUint32(1000); buffer.writeInt64(-14725836936900); socket.send(buffer.buffer,buffer.offset); 字节流解包: var len=socket.read(data); var buffer=new ByteBuffer(len); buffer.setBufferData(data); * var str=buffer.readString(); * var int32=buffer.readInt32(); * .... */ var ByteBuffer = function(length) { this.buffer = new ArrayBuffer(length); // this.byteStream=new Uint8Array(this.buffer); this.dataView = new DataView(this.buffer); // 缓冲区偏移量 this.offset = 0; this.setBufferData = function(buffer) { this.buffer = buffer; this.dataView = new DataView(this.buffer); this.offset = 0; return this.dataView; } // copy buffer to another // buffer,m.appendArrayBuffer(m.offset+2,n.buffer,0,5); this.appendArrayBuffer = function(toPos, fromBuffer, fromPos, length) { // console.log("appendArrayBuffer toPos=%d,fromPos=%d,length=%d // \n",toPos,fromPos,length); var uarr = new Uint8Array(fromBuffer); for (var i = fromPos; i < length; i++) { this.dataView.setInt8(toPos++, uarr[i], true); } } // 将uarr复制到当前缓冲区 this.appendUint8Array = function(toPos, uarr) { for (var i = 0; i < uarr.length; i++) { this.dataView.setInt8(toPos++, uarr[i], true); } } this.initHeader = function() { this.offset += 9; } this.setHeader = function(input) { this.dataView.setInt8(0, 0x7c, true); this.dataView.setUint32(1, input, true); this.dataView.setUint32(5, input - 5, true); } // writeChar("a") this.writeChar = function(input) { this.dataView.setInt8(this.offset++, input.charCodeAt(), true); return this.offset; } // writeHex(0x04); this.writeHex = function(input) { this.dataView.setInt8(this.offset++, input); return this.offset; } this.writeUint16 = function(input) { this.dataView.setUint16(this.offset, input, true); this.offset += 2; return this.offset; } this.writeUint32 = function(input) { this.dataView.setUint32(this.offset, input, true); this.offset += 4; } this.writeInt32 = function(input) { this.dataView.setInt32(this.offset, input, true); this.offset += 4; } this.writeInt64 = function(input) { var sign = input < 0; if (sign) input = -1 - input; for (var i = 0; i < 8; i++) { var mod = input % 0x100; input = (input - mod) / 0x100; var v = sign ? mod ^ 0xFF : mod; this.dataView.setUint8(this.offset++, v); } return this.offset; } this.readInt64 = function() { var bytes = new Uint8Array(this.buffer, this.offset, 8); var sign = bytes[7] >> 7; this.offset += 8; var sum = 0; var digits = 1; for (var i = 0; i < 8; i++) { var value = bytes[i]; sum += (sign ? value ^ 0xFF : value) * digits; digits *= 0x100; } return sign ? -1 - sum : sum; } this.writeString = function(input) { var utf8Length = this.UTF8Length(input); this.offset = this.writeUint16(utf8Length); this.stringToUTF8(input); return this.offset; } // ====================== this.readUint32 = function() { var ret = this.dataView.getUint32(this.offset, true); this.offset += 4; return ret; } this.readInt32 = function() { var ret = this.dataView.getInt32(this.offset, true); this.offset += 4; return ret; } this.readInt16 = function() { var ret = this.dataView.getInt16(this.offset, true); this.offset += 2; return ret; } this.readString = function() { var length = this.readInt16(); var uint8Array = new Uint8Array(this.buffer, this.offset, length); var ret = this.parseUTF8(uint8Array, 0, uint8Array.length); this.offset += uint8Array.length; return ret; } this.readFloat32 = function() { var ret = this.dataView.getFloat32(this.offset, true); this.offset += 4; return ret; } this.readFloat64 = function() { var ret = this.dataView.getFloat64(this.offset, true); this.offset += 4; return ret; } this.readList = function(types) { if (types.length <= 0) return; var list = []; var length = this.readInt16(); for (var row = 0; row < length; row++) { var values = {}; for (var i = 0; i < types.length; i++) { var type = types[i]; if (types[i] == "int") { values[type] = this.readInt32(); } else if (types[i] == "short") { values[type] = this.readInt16(); } else if (types[i] == "float") { values[type] = this.readFloat32(); } else if (types[i] == "double") { values[type] = this.readFloat64(); } else if (types[i] == "string") { values[type] = this.readString(); } } list[row] = values; } return list; } this.stringToUTF8 = function(input) { for (var i = 0; i < input.length; i++) { var charCode = input.charCodeAt(i); // Check for a surrogate pair. if (0xD800 <= charCode && charCode <= 0xDBFF) { var lowCharCode = input.charCodeAt(++i); if (isNaN(lowCharCode)) { throw new Error(format(ERROR.MALFORMED_UNICODE, [ charCode, lowCharCode ])); } charCode = ((charCode - 0xD800) << 10) + (lowCharCode - 0xDC00) + 0x10000; } if (charCode <= 0x7F) { this.writeHex(charCode); } else if (charCode <= 0x7FF) { this.writeHex(charCode >> 6 & 0x1F | 0xC0); this.writeHex(charCode & 0x3F | 0x80); } else if (charCode <= 0xFFFF) { this.writeHex(charCode >> 12 & 0x0F | 0xE0); this.writeHex(charCode >> 6 & 0x3F | 0x80); this.writeHex(charCode & 0x3F | 0x80); } else { this.writeHex(charCode >> 18 & 0x07 | 0xF0); this.writeHex(charCode >> 12 & 0x3F | 0x80); this.writeHex(charCode >> 6 & 0x3F | 0x80); this.writeHex(charCode & 0x3F | 0x80); } ; } return this.byteStream; } this.UTF8Length = function(input) { var output = 0; for (var i = 0; i < input.length; i++) { var charCode = input.charCodeAt(i); if (charCode > 0x7FF) { // Surrogate pair means its a 4 byte character if (0xD800 <= charCode && charCode <= 0xDBFF) { i++; output++; } output += 3; } else if (charCode > 0x7F) output += 2; else output++; } return output; } this.parseUTF8 = function(input, offset, length) { var output = ""; var utf16; var pos = offset; while (pos < offset + length) { var byte1 = input[pos++]; if (byte1 < 128) utf16 = byte1; else { var byte2 = input[pos++] - 128; if (byte2 < 0) throw new Error(format(ERROR.MALFORMED_UTF, [ byte1.toString(16), byte2.toString(16), "" ])); if (byte1 < 0xE0) // 2 byte character utf16 = 64 * (byte1 - 0xC0) + byte2; else { var byte3 = input[pos++] - 128; if (byte3 < 0) throw new Error(format(ERROR.MALFORMED_UTF, [ byte1.toString(16), byte2.toString(16), byte3.toString(16) ])); if (byte1 < 0xF0) // 3 byte character utf16 = 4096 * (byte1 - 0xE0) + 64 * byte2 + byte3; else { var byte4 = input[pos++] - 128; if (byte4 < 0) throw new Error(format(ERROR.MALFORMED_UTF, [ byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16) ])); if (byte1 < 0xF8) // 4 byte character utf16 = 262144 * (byte1 - 0xF0) + 4096 * byte2 + 64 * byte3 + byte4; else // longer encodings are not supported throw new Error(format(ERROR.MALFORMED_UTF, [ byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16) ])); } } } if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate // pair { utf16 -= 0x10000; output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead // character utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character } output += String.fromCharCode(utf16); } return output; } this.encode = function(bytes) { for (var i = 0; i < bytes.length; i++) { bytes[i] ^= 0xFF; } return bytes; } }