From d58cdb341901b15d1b27cd6190924cc78ced2575 Mon Sep 17 00:00:00 2001 From: Richard Schneider Date: Wed, 31 Jan 2018 07:41:18 +1300 Subject: [PATCH] Change TXT values to array of buffers (breaking) (#26) * fix: txt data is an array of strings * fix: a consistent API for TXT data * docs: update TXT info * feat: empty buffer is not allowed * fix: allow empty strings and buffers * fix: rnull is like old rtxt --- README.md | 6 +++- index.js | 84 +++++++++++++++++++++++++++++++++++++++++++++++++------ test.js | 33 ++++++++++++++++++++-- 3 files changed, 111 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9662813..76ebcf3 100644 --- a/README.md +++ b/README.md @@ -132,10 +132,14 @@ Currently the different available records are ``` js { - data: Buffer('some text') + data: 'text' || Buffer || [ Buffer || 'text' ] } ``` +When encoding, scalar values are converted to an array and strings are converted to UTF-8 encoded Buffers. An error is `thrown` if a string or Buffer is empty. + +When decoding, an array of Buffer is always returned. + #### `NS` ``` js diff --git a/index.js b/index.js index 6bc9949..2c31976 100644 --- a/index.js +++ b/index.js @@ -284,13 +284,81 @@ rsoa.encodingLength = function (data) { return 22 + name.encodingLength(data.mname) + name.encodingLength(data.rname) } -const rtxt = exports.txt = exports.null = {} -const rnull = rtxt +const rtxt = exports.txt = {} rtxt.encode = function (data, buf, offset) { + if (!Array.isArray(data)) data = [data] + for (let i = 0; i < data.length; i++) { + if (typeof data[i] === 'string') { + data[i] = Buffer.from(data[i]) + } + if (!Buffer.isBuffer(data[i])) { + throw new Error('Must be a Buffer') + } + } + if (!buf) buf = Buffer.allocUnsafe(rtxt.encodingLength(data)) if (!offset) offset = 0 + const oldOffset = offset + offset += 2 + + data.forEach(function (d) { + buf[offset++] = d.length + d.copy(buf, offset, 0, d.length) + offset += d.length + }) + + buf.writeUInt16BE(offset - oldOffset - 2, oldOffset) + rtxt.encode.bytes = offset - oldOffset + return buf +} + +rtxt.encode.bytes = 0 + +rtxt.decode = function (buf, offset) { + if (!offset) offset = 0 + const oldOffset = offset + let remaining = buf.readUInt16BE(offset) + offset += 2 + + let data = [] + while (remaining > 0) { + const len = buf[offset++] + --remaining + if (remaining < len) { + throw new Error('Buffer overflow') + } + data.push(buf.slice(offset, offset + len)) + offset += len + remaining -= len + } + + rtxt.decode.bytes = offset - oldOffset + return data +} + +rtxt.decode.bytes = 0 + +rtxt.encodingLength = function (data) { + if (!Array.isArray(data)) data = [data] + let length = 2 + data.forEach(function (buf) { + if (typeof buf === 'string') { + length += Buffer.byteLength(buf) + 1 + } else { + length += buf.length + 1 + } + }) + return length +} + +const rnull = exports.null = {} + +rnull.encode = function (data, buf, offset) { + if (!buf) buf = Buffer.allocUnsafe(rnull.encodingLength(data)) + if (!offset) offset = 0 + if (typeof data === 'string') data = Buffer.from(data) if (!data) data = Buffer.allocUnsafe(0) @@ -302,13 +370,13 @@ rtxt.encode = function (data, buf, offset) { offset += len buf.writeUInt16BE(offset - oldOffset - 2, oldOffset) - rtxt.encode.bytes = offset - oldOffset + rnull.encode.bytes = offset - oldOffset return buf } -rtxt.encode.bytes = 0 +rnull.encode.bytes = 0 -rtxt.decode = function (buf, offset) { +rnull.decode = function (buf, offset) { if (!offset) offset = 0 const oldOffset = offset const len = buf.readUInt16BE(offset) @@ -318,13 +386,13 @@ rtxt.decode = function (buf, offset) { const data = buf.slice(offset, offset + len) offset += len - rtxt.decode.bytes = offset - oldOffset + rnull.decode.bytes = offset - oldOffset return data } -rtxt.decode.bytes = 0 +rnull.decode.bytes = 0 -rtxt.encodingLength = function (data) { +rnull.encodingLength = function (data) { if (!data) return 2 return (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data)) + 2 } diff --git a/test.js b/test.js index c5c61c6..2b6c466 100644 --- a/test.js +++ b/test.js @@ -12,9 +12,36 @@ tape('unknown', function (t) { }) tape('txt', function (t) { - testEncoder(t, packet.txt, Buffer.allocUnsafe(0)) - testEncoder(t, packet.txt, Buffer.from('hello world')) - testEncoder(t, packet.txt, Buffer.from([0, 1, 2, 3, 4, 5])) + testEncoder(t, packet.txt, []) + testEncoder(t, packet.txt, ['hello world']) + testEncoder(t, packet.txt, ['hello', 'world']) + testEncoder(t, packet.txt, [Buffer.from([0, 1, 2, 3, 4, 5])]) + testEncoder(t, packet.txt, ['a', 'b', Buffer.from([0, 1, 2, 3, 4, 5])]) + testEncoder(t, packet.txt, ['', Buffer.allocUnsafe(0)]) + t.end() +}) + +tape('txt-scalar-string', function (t) { + const buf = packet.txt.encode('hi') + const val = packet.txt.decode(buf) + t.ok(val.length === 1, 'array length') + t.ok(val[0].toString() === 'hi', 'data') + t.end() +}) + +tape('txt-scalar-buffer', function (t) { + const data = Buffer.from([0, 1, 2, 3, 4, 5]) + const buf = packet.txt.encode(data) + const val = packet.txt.decode(buf) + t.ok(val.length === 1, 'array length') + t.ok(val[0].equals(data), 'data') + t.end() +}) + +tape('txt-invalid-data', function (t) { + t.throws(function () { packet.txt.encode(null) }, 'null') + t.throws(function () { packet.txt.encode(undefined) }, 'undefined') + t.throws(function () { packet.txt.encode(10) }, 'number') t.end() })