From 0aa1a8a005c83808f0167cebe759a6e1b75faf71 Mon Sep 17 00:00:00 2001 From: isaacs Date: Sat, 19 Feb 2011 17:29:01 -0800 Subject: [PATCH] Closes GH-695 Add 'hex' encoding to Buffer --- doc/api/buffers.markdown | 2 ++ lib/buffer.js | 45 ++++++++++++++++++++++++++++++++++++++ src/node.cc | 3 +++ src/node.h | 2 +- src/node_buffer.cc | 2 ++ test/simple/test-buffer.js | 31 ++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/doc/api/buffers.markdown b/doc/api/buffers.markdown index 9c26b61a12..4fc6890fee 100644 --- a/doc/api/buffers.markdown +++ b/doc/api/buffers.markdown @@ -26,6 +26,8 @@ the first 8 bits of each character. This encoding method is depreciated and should be avoided in favor of `Buffer` objects where possible. This encoding will be removed in future versions of Node. +* `'hex'` - Encode each byte as two hexidecimal characters. + ### new Buffer(size) diff --git a/lib/buffer.js b/lib/buffer.js index 25db4a54b6..d5fb174d04 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -17,6 +17,21 @@ SlowBuffer.prototype.inspect = function() { }; +SlowBuffer.prototype.hexSlice = function(start, end) { + var len = this.length; + + if (!start || start < 0) start = 0; + if (end < 0 || start + end > len) end = len - start; + + var out = ''; + for (var i = start; i < end; i ++) { + out += toHex(this[i]); + } + return out; +}; + + + SlowBuffer.prototype.toString = function(encoding, start, end) { encoding = String(encoding || 'utf8').toLowerCase(); start = +start || 0; @@ -28,6 +43,9 @@ SlowBuffer.prototype.toString = function(encoding, start, end) { } switch (encoding) { + case 'hex': + return this.hexSlice(start, end); + case 'utf8': case 'utf-8': return this.utf8Slice(start, end); @@ -51,6 +69,23 @@ SlowBuffer.prototype.toString = function(encoding, start, end) { }; +SlowBuffer.prototype.hexWrite = function(string, offset) { + var len = string.length; + offset = +offset || 0; + + // must be an even number of digits + if (len % 2) { + throw new Error('Invalid hex string'); + } + for (var i = 0; i < len / 2; i ++) { + var byte = parseInt(string.substr(i * 2, 2), 16); + if (isNaN(byte)) throw new Error('Invalid hex string'); + this[offset + i] = byte; + } + return i; +} + + SlowBuffer.prototype.write = function(string, offset, encoding) { // Support both (string, offset, encoding) // and the legacy (string, encoding, offset) @@ -64,6 +99,9 @@ SlowBuffer.prototype.write = function(string, offset, encoding) { encoding = String(encoding || 'utf8').toLowerCase(); switch (encoding) { + case 'hex': + return this.hexWrite(string, offset); + case 'utf8': case 'utf-8': return this.utf8Write(string, offset); @@ -224,6 +262,10 @@ Buffer.prototype.write = function(string, offset, encoding) { var ret; switch (encoding) { + case 'hex': + ret = this.parent.hexWrite(string, this.offset + offset, maxLength); + break; + case 'utf8': case 'utf-8': ret = this.parent.utf8Write(string, this.offset + offset, maxLength); @@ -277,6 +319,9 @@ Buffer.prototype.toString = function(encoding, start, end) { end = end + this.offset; switch (encoding) { + case 'hex': + return this.parent.hexSlice(start, end); + case 'utf8': case 'utf-8': return this.parent.utf8Slice(start, end); diff --git a/src/node.cc b/src/node.cc index 01bd5613ed..8cf25ecbe2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1084,6 +1084,8 @@ enum encoding ParseEncoding(Handle encoding_v, enum encoding _default) { return UCS2; } else if (strcasecmp(*encoding, "binary") == 0) { return BINARY; + } else if (strcasecmp(*encoding, "hex") == 0) { + return HEX; } else if (strcasecmp(*encoding, "raw") == 0) { fprintf(stderr, "'raw' (array of integers) has been removed. " "Use 'binary'.\n"); @@ -1134,6 +1136,7 @@ ssize_t DecodeBytes(v8::Handle val, enum encoding encoding) { if (encoding == UTF8) return str->Utf8Length(); else if (encoding == UCS2) return str->Length() * 2; + else if (encoding == HEX) return str->Length() / 2; return str->Length(); } diff --git a/src/node.h b/src/node.h index e2c58d3445..434796514d 100644 --- a/src/node.h +++ b/src/node.h @@ -44,7 +44,7 @@ do { \ __callback##_TEM); \ } while (0) -enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY}; +enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX}; enum encoding ParseEncoding(v8::Handle encoding_v, enum encoding _default = BINARY); void FatalException(v8::TryCatch &try_catch); diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 8b48f2a2e6..44a6750053 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -86,6 +86,8 @@ static size_t ByteLength (Handle string, enum encoding enc) { return base64_decoded_size(*v, v.length()); } else if (enc == UCS2) { return string->Length() * 2; + } else if (enc == HEX) { + return string->Length() / 2; } else { return string->Length(); } diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 0aad8149f7..f60d29c820 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -410,3 +410,34 @@ assert.equal(12, Buffer.byteLength('Il était tué', 'binary')); // slice(0,0).length === 0 assert.equal(0, Buffer('hello').slice(0, 0).length); + +// test hex toString +console.log('Create hex string from buffer'); +var hexb = new Buffer(256); +for (var i = 0; i < 256; i ++) { + hexb[i] = i; +} +var hexStr = hexb.toString('hex'); +assert.equal(hexStr, + '000102030405060708090a0b0c0d0e0f'+ + '101112131415161718191a1b1c1d1e1f'+ + '202122232425262728292a2b2c2d2e2f'+ + '303132333435363738393a3b3c3d3e3f'+ + '404142434445464748494a4b4c4d4e4f'+ + '505152535455565758595a5b5c5d5e5f'+ + '606162636465666768696a6b6c6d6e6f'+ + '707172737475767778797a7b7c7d7e7f'+ + '808182838485868788898a8b8c8d8e8f'+ + '909192939495969798999a9b9c9d9e9f'+ + 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf'+ + 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf'+ + 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf'+ + 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'+ + 'e0e1e2e3e4e5e6e7e8e9eaebecedeeef'+ + 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); + +console.log('Create buffer from hex string'); +var hexb2 = new Buffer(hexStr, 'hex'); +for (var i = 0; i < 256; i ++) { + assert.equal(hexb2[i], hexb[i]); +}