diff --git a/.aegir.js b/.aegir.js new file mode 100644 index 0000000..5166cb4 --- /dev/null +++ b/.aegir.js @@ -0,0 +1,11 @@ +'use strict' + +module.exports = { + webpack: { + node: { + // this is needed until protocol-buffers stops using node core APIs in browser code + os: true, + Buffer: true + } + } +} diff --git a/.travis.yml b/.travis.yml index 00fadd1..7bd82f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ os: - osx - windows -script: npx nyc -s npm run test -- --bail +script: npx nyc -s npm run test:node -- --bail after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov jobs: @@ -26,5 +26,17 @@ jobs: - npx aegir dep-check - npm run lint + - stage: test + name: chrome + addons: + chrome: stable + script: npx aegir test -t browser -t webworker + + - stage: test + name: firefox + addons: + firefox: latest + script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless + notifications: email: false diff --git a/bench/index.js b/bench/index.js index f330b99..9bac72c 100644 --- a/bench/index.js +++ b/bench/index.js @@ -1,6 +1,5 @@ 'use strict' -const { Buffer } = require('buffer') const Benchmark = require('benchmark') if (typeof window !== 'undefined') { window.Benchmark = Benchmark @@ -13,11 +12,12 @@ const proto = require('./bench.proto') const messages = protobuf(proto) const messagesBuf = protons(proto) const messagesNpm = protonsNpm(proto) +const uint8ArrayFromString = require('uint8arrays/from-string') const EXAMPLE = { foo: 'hello', hello: 42, - payload: Buffer.from('a'), + payload: uint8ArrayFromString('a'), meh: { b: { tmp: { diff --git a/package.json b/package.json index ec80302..eb38ac9 100644 --- a/package.json +++ b/package.json @@ -8,26 +8,27 @@ "url": "https://github.com/ipfs/protons" }, "dependencies": { - "buffer": "^5.5.0", "protocol-buffers-schema": "^3.3.1", "signed-varint": "^2.0.1", + "uint8arrays": "^1.0.0", "varint": "^5.0.0" }, "devDependencies": { - "ipfs-utils": "^2.3.0", - "aegir": "^21.4.0", + "aegir": "^25.0.0", "benchmark": "^2.1.4", "protocol-buffers": "^4.1.0", "protons": "^1.0.0", "tape": "^4.8.0" }, "scripts": { - "test": "tape test/*.js", - "build": "aegir build", + "test": "aegir test", + "test:browser": "aegir test --target browser", + "test:node": "aegir test --target node", "lint": "aegir lint", - "release": "aegir release --test=false", - "release-minor": "aegir release --type minor --test=false", - "release-major": "aegir release --type major --test=false", + "release": "aegir release", + "release-minor": "aegir release --type minor", + "release-major": "aegir release --type major", + "build": "aegir build", "bench": "node bench" }, "bugs": { diff --git a/src/compile/decode.js b/src/compile/decode.js index fc87eb1..37c9166 100644 --- a/src/compile/decode.js +++ b/src/compile/decode.js @@ -1,8 +1,8 @@ /* eslint max-depth: 1 */ 'use strict' -var varint = require('varint') -var defined = require('./utils').defined +const varint = require('varint') +const defined = require('./utils').defined function toSentenceCase (string) { return `${string.substring(0, 1).toUpperCase()}${string.substring(1)}` @@ -60,18 +60,18 @@ function addPropertyAccessors (obj, name, value, defaultValue) { } function compileDecode (m, resolve, enc) { - var requiredFields = [] - var fields = {} - var oneofFields = [] - var vals = [] + const requiredFields = [] + const fields = {} + const oneofFields = [] + const vals = [] for (var i = 0; i < enc.length; i++) { - var field = m.fields[i] + const field = m.fields[i] fields[field.tag] = i - var def = field.options && field.options.default - var resolved = resolve(field.type, m.id, false) + const def = field.options && field.options.default + const resolved = resolve(field.type, m.id, false) vals[i] = [def, resolved && resolved.values] m.fields[i].packed = field.repeated && field.options && field.options.packed && field.options.packed !== 'false' @@ -85,12 +85,12 @@ function compileDecode (m, resolve, enc) { } } - function decodeField (e, field, obj, buf, offset, i) { - var name = field.name + function decodeField (e, field, obj, buf, dataView, offset, i) { + const name = field.name if (field.oneof) { // clear already defined oneof fields - var props = Object.keys(obj) + const props = Object.keys(obj) for (var j = 0; j < props.length; j++) { if (oneofFields.indexOf(props[j]) > -1) { const sentenceCase = toSentenceCase(props[j]) @@ -106,10 +106,10 @@ function compileDecode (m, resolve, enc) { let value if (e.message) { - var len = varint.decode(buf, offset) + const len = varint.decode(buf, offset) offset += varint.decode.bytes - var decoded = e.decode(buf, offset, offset + len) + const decoded = e.decode(buf, dataView, offset, offset + len) if (field.map) { value = obj[name] || {} @@ -123,19 +123,20 @@ function compileDecode (m, resolve, enc) { } else { if (field.repeated) { value = obj[name] || [] - value.push(e.decode(buf, offset)) + value.push(e.decode(buf, dataView, offset)) } else { - value = e.decode(buf, offset) + value = e.decode(buf, dataView, offset) } } addPropertyAccessors(obj, name, value) offset += e.decode.bytes + return offset } - return function decode (buf, offset, end) { + return function decode (buf, view, offset, end) { if (offset == null) { offset = 0 } @@ -148,6 +149,10 @@ function compileDecode (m, resolve, enc) { throw new Error('Decoded message is not valid') } + if (!view) { + view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength) + } + var oldOffset = offset var obj = {} var field @@ -223,7 +228,7 @@ function compileDecode (m, resolve, enc) { var i = fields[tag] if (i == null) { - offset = skip(prefix & 7, buf, offset) + offset = skip(prefix & 7, buf, view, offset) continue } @@ -236,16 +241,16 @@ function compileDecode (m, resolve, enc) { packedEnd += offset while (offset < packedEnd) { - offset = decodeField(e, field, obj, buf, offset, i) + offset = decodeField(e, field, obj, buf, view, offset, i) } } else { - offset = decodeField(e, field, obj, buf, offset, i) + offset = decodeField(e, field, obj, buf, view, offset, i) } } } } -var skip = function (type, buffer, offset) { +var skip = function (type, buffer, view, offset) { switch (type) { case 0: varint.decode(buffer, offset) diff --git a/src/compile/encode.js b/src/compile/encode.js index e0c1b03..0406357 100644 --- a/src/compile/encode.js +++ b/src/compile/encode.js @@ -1,24 +1,24 @@ 'use strict' -const { Buffer } = require('buffer') + var defined = require('./utils').defined var varint = require('varint') function compileEncode (m, resolve, enc, oneofs, encodingLength) { - var oneofsKeys = Object.keys(oneofs) - var encLength = enc.length - var ints = {} - for (var i = 0; i < encLength; i++) { + const oneofsKeys = Object.keys(oneofs) + const encLength = enc.length + const ints = {} + for (let i = 0; i < encLength; i++) { ints[i] = { p: varint.encode(m.fields[i].tag << 3 | 2), h: varint.encode(m.fields[i].tag << 3 | enc[i].type) } - var field = m.fields[i] + const field = m.fields[i] m.fields[i].packed = field.repeated && field.options && field.options.packed && field.options.packed !== 'false' } - function encodeField (buf, offset, h, e, packed, innerVal) { - var j = 0 + function encodeField (buf, view, offset, h, e, packed, innerVal) { + let j = 0 if (!packed) { for (j = 0; j < h.length; j++) { buf[offset++] = h[j] @@ -30,28 +30,30 @@ function compileEncode (m, resolve, enc, oneofs, encodingLength) { offset += varint.encode.bytes } - e.encode(innerVal, buf, offset) + e.encode(innerVal, buf, view, offset) + return offset + e.encode.bytes } - return function encode (obj, buf, offset) { - if (offset == null) { - offset = 0 - } + return function encode (obj, buf, view, offset = 0) { if (buf == null) { - buf = Buffer.allocUnsafe(encodingLength(obj)) + buf = new Uint8Array(encodingLength(obj)) } - var oldOffset = offset - var objKeys = Object.keys(obj) - var i = 0 + if (view == null) { + view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength) + } + + const oldOffset = offset + const objKeys = Object.keys(obj) + let i = 0 // oneof checks - var match = false + let match = false for (i = 0; i < oneofsKeys.length; i++) { - var name = oneofsKeys[i] - var prop = oneofs[i] + const name = oneofsKeys[i] + const prop = oneofs[i] if (objKeys.indexOf(prop) > -1) { if (match) { throw new Error('only one of the properties defined in oneof ' + name + ' can be set') @@ -62,10 +64,10 @@ function compileEncode (m, resolve, enc, oneofs, encodingLength) { } for (i = 0; i < encLength; i++) { - var e = enc[i] - var field = m.fields[i] // was f - var val = obj[field.name] - var j = 0 + const e = enc[i] + const field = m.fields[i] // was f + let val = obj[field.name] + let j = 0 if (!defined(val)) { if (field.required) { @@ -73,13 +75,13 @@ function compileEncode (m, resolve, enc, oneofs, encodingLength) { } continue } - var p = ints[i].p - var h = ints[i].h + const p = ints[i].p + const h = ints[i].h - var packed = field.packed + const packed = field.packed if (field.map) { - var tmp = Object.keys(val) + const tmp = Object.keys(val) for (j = 0; j < tmp.length; j++) { tmp[j] = { key: tmp[j], @@ -90,7 +92,7 @@ function compileEncode (m, resolve, enc, oneofs, encodingLength) { } if (packed) { - var packedLen = 0 + let packedLen = 0 for (j = 0; j < val.length; j++) { if (!Object.prototype.hasOwnProperty.call(val, j)) { continue @@ -109,16 +111,17 @@ function compileEncode (m, resolve, enc, oneofs, encodingLength) { } if (field.repeated) { - var innerVal + let innerVal for (j = 0; j < val.length; j++) { innerVal = val[j] if (!defined(innerVal)) { continue } - offset = encodeField(buf, offset, h, e, packed, innerVal) + + offset = encodeField(buf, view, offset, h, e, packed, innerVal) } } else { - offset = encodeField(buf, offset, h, e, packed, val) + offset = encodeField(buf, view, offset, h, e, packed, val) } } diff --git a/src/compile/encoding-length.js b/src/compile/encoding-length.js index 54eddb2..450e251 100644 --- a/src/compile/encoding-length.js +++ b/src/compile/encoding-length.js @@ -4,28 +4,28 @@ var defined = require('./utils').defined var varint = require('varint') function compileEncodingLength (m, enc, oneofs) { - var oneofsKeys = Object.keys(oneofs) - var encLength = enc.length + const oneofsKeys = Object.keys(oneofs) + const encLength = enc.length - var hls = new Array(encLength) + const hls = new Array(encLength) - for (var i = 0; i < m.fields.length; i++) { + for (let i = 0; i < m.fields.length; i++) { hls[i] = varint.encodingLength(m.fields[i].tag << 3 | enc[i].type) - var field = m.fields[i] + const field = m.fields[i] m.fields[i].packed = field.repeated && field.options && field.options.packed && field.options.packed !== 'false' } return function encodingLength (obj) { - var length = 0 - var i = 0 - var j = 0 + let length = 0 + let i = 0 + let j = 0 for (i = 0; i < oneofsKeys.length; i++) { - var name = oneofsKeys[i] - var props = oneofs[name] + const name = oneofsKeys[i] + const props = oneofs[name] - var match = false + let match = false for (j = 0; j < props.length; j++) { if (defined(obj[props[j]])) { if (match) { @@ -37,11 +37,11 @@ function compileEncodingLength (m, enc, oneofs) { } for (i = 0; i < encLength; i++) { - var e = enc[i] - var field = m.fields[i] - var val = obj[field.name] - var hl = hls[i] - var len + const e = enc[i] + const field = m.fields[i] + let val = obj[field.name] + const hl = hls[i] + let len if (!defined(val)) { if (field.required) { @@ -52,7 +52,7 @@ function compileEncodingLength (m, enc, oneofs) { } if (field.map) { - var tmp = Object.keys(val) + const tmp = Object.keys(val) for (j = 0; j < tmp.length; j++) { tmp[j] = { key: tmp[j], @@ -64,7 +64,7 @@ function compileEncodingLength (m, enc, oneofs) { } if (field.packed) { - var packedLen = 0 + let packedLen = 0 for (j = 0; j < val.length; j++) { if (!defined(val[j])) { continue diff --git a/src/compile/encodings.js b/src/compile/encodings.js deleted file mode 100644 index c742f57..0000000 --- a/src/compile/encodings.js +++ /dev/null @@ -1,293 +0,0 @@ -'use strict' - -var varint = require('varint') -var svarint = require('signed-varint') -const { Buffer } = require('buffer') - -var encoder = function (type, encode, decode, encodingLength) { - encode.bytes = decode.bytes = 0 - - return { - type: type, - encode: encode, - decode: decode, - encodingLength: encodingLength - } -} - -exports.make = encoder - -exports.bytes = (function () { - var bufferLength = function (val) { - return Buffer.isBuffer(val) ? val.length : Buffer.byteLength(val) - } - - var encodingLength = function (val) { - var len = bufferLength(val) - return varint.encodingLength(len) + len - } - - var encode = function (val, buffer, offset) { - var oldOffset = offset - var len = bufferLength(val) - - varint.encode(len, buffer, offset) - offset += varint.encode.bytes - - if (val instanceof Uint8Array) buffer.set(val, offset) - else buffer.write(val, offset, len) - offset += len - - encode.bytes = offset - oldOffset - return buffer - } - - var decode = function (buffer, offset) { - var oldOffset = offset - - var len = varint.decode(buffer, offset) - offset += varint.decode.bytes - - var val = buffer.slice(offset, offset + len) - offset += val.length - - decode.bytes = offset - oldOffset - return val - } - - return encoder(2, encode, decode, encodingLength) -})() - -exports.string = (function () { - var encodingLength = function (val) { - var len = Buffer.byteLength(val) - return varint.encodingLength(len) + len - } - - var encode = function (val, buffer, offset) { - var oldOffset = offset - var len = Buffer.byteLength(val) - - varint.encode(len, buffer, offset, 'utf-8') - offset += varint.encode.bytes - - buffer.write(val, offset, len) - offset += len - - encode.bytes = offset - oldOffset - return buffer - } - - var decode = function (buffer, offset) { - var oldOffset = offset - - var len = varint.decode(buffer, offset) - offset += varint.decode.bytes - - var val = buffer.toString('utf-8', offset, offset + len) - offset += len - - decode.bytes = offset - oldOffset - return val - } - - return encoder(2, encode, decode, encodingLength) -})() - -exports.bool = (function () { - var encodingLength = function (val) { - return 1 - } - - var encode = function (val, buffer, offset) { - buffer[offset] = val ? 1 : 0 - encode.bytes = 1 - return buffer - } - - var decode = function (buffer, offset) { - var bool = buffer[offset] > 0 - decode.bytes = 1 - return bool - } - - return encoder(0, encode, decode, encodingLength) -})() - -exports.int32 = (function () { - var decode = function (buffer, offset) { - var val = varint.decode(buffer, offset) - decode.bytes = varint.decode.bytes - return val > 2147483647 ? val - 4294967296 : val - } - - var encode = function (val, buffer, offset) { - varint.encode(val < 0 ? val + 4294967296 : val, buffer, offset) - encode.bytes = varint.encode.bytes - return buffer - } - - var encodingLength = function (val) { - return varint.encodingLength(val < 0 ? val + 4294967296 : val) - } - - return encoder(0, encode, decode, encodingLength) -})() - -exports.int64 = (function () { - var decode = function (buffer, offset) { - var val = varint.decode(buffer, offset) - if (val >= Math.pow(2, 63)) { - var limit = 9 - while (buffer[offset + limit - 1] === 0xff) limit-- - limit = limit || 9 - var subset = Buffer.allocUnsafe(limit) - buffer.copy(subset, 0, offset, offset + limit) - subset[limit - 1] = subset[limit - 1] & 0x7f - val = -1 * varint.decode(subset, 0) - decode.bytes = 10 - } else { - decode.bytes = varint.decode.bytes - } - return val - } - - var encode = function (val, buffer, offset) { - if (val < 0) { - var last = offset + 9 - varint.encode(val * -1, buffer, offset) - offset += varint.encode.bytes - 1 - buffer[offset] = buffer[offset] | 0x80 - while (offset < last - 1) { - offset++ - buffer[offset] = 0xff - } - buffer[last] = 0x01 - encode.bytes = 10 - } else { - varint.encode(val, buffer, offset) - encode.bytes = varint.encode.bytes - } - return buffer - } - - var encodingLength = function (val) { - return val < 0 ? 10 : varint.encodingLength(val) - } - - return encoder(0, encode, decode, encodingLength) -})() - -exports.sint32 = -exports.sint64 = (function () { - return encoder(0, svarint.encode, svarint.decode, svarint.encodingLength) -})() - -exports.uint32 = -exports.uint64 = -exports.enum = -exports.varint = (function () { - return encoder(0, varint.encode, varint.decode, varint.encodingLength) -})() - -// we cannot represent these in javascript so we just use buffers -exports.fixed64 = -exports.sfixed64 = (function () { - var encodingLength = function (val) { - return 8 - } - - var encode = function (val, buffer, offset) { - buffer.set(val, offset) - encode.bytes = 8 - return buffer - } - - var decode = function (buffer, offset) { - var val = buffer.slice(offset, offset + 8) - decode.bytes = 8 - return val - } - - return encoder(1, encode, decode, encodingLength) -})() - -exports.double = (function () { - var encodingLength = function (val) { - return 8 - } - - var encode = function (val, buffer, offset) { - buffer.writeDoubleLE(val, offset) - encode.bytes = 8 - return buffer - } - - var decode = function (buffer, offset) { - var val = buffer.readDoubleLE(offset) - decode.bytes = 8 - return val - } - - return encoder(1, encode, decode, encodingLength) -})() - -exports.fixed32 = (function () { - var encodingLength = function (val) { - return 4 - } - - var encode = function (val, buffer, offset) { - buffer.writeUInt32LE(val, offset) - encode.bytes = 4 - return buffer - } - - var decode = function (buffer, offset) { - var val = buffer.readUInt32LE(offset) - decode.bytes = 4 - return val - } - - return encoder(5, encode, decode, encodingLength) -})() - -exports.sfixed32 = (function () { - var encodingLength = function (val) { - return 4 - } - - var encode = function (val, buffer, offset) { - buffer.writeInt32LE(val, offset) - encode.bytes = 4 - return buffer - } - - var decode = function (buffer, offset) { - var val = buffer.readInt32LE(offset) - decode.bytes = 4 - return val - } - - return encoder(5, encode, decode, encodingLength) -})() - -exports.float = (function () { - var encodingLength = function (val) { - return 4 - } - - var encode = function (val, buffer, offset) { - buffer.writeFloatLE(val, offset) - encode.bytes = 4 - return buffer - } - - var decode = function (buffer, offset) { - var val = buffer.readFloatLE(offset) - decode.bytes = 4 - return val - } - - return encoder(5, encode, decode, encodingLength) -})() diff --git a/src/compile/encodings/bool.js b/src/compile/encodings/bool.js new file mode 100644 index 0000000..7abdc43 --- /dev/null +++ b/src/compile/encodings/bool.js @@ -0,0 +1,21 @@ +'use strict' + +const encoder = require('./encoder') + +function boolEncodingLength () { + return 1 +} + +function boolEncode (value, buffer, dataView, offset) { + buffer[offset] = value ? 1 : 0 + boolEncode.bytes = 1 +} + +function boolDecode (buffer, dataView, offset) { + const bool = buffer[offset] > 0 + boolDecode.bytes = 1 + + return bool +} + +module.exports = encoder(0, boolEncode, boolDecode, boolEncodingLength) diff --git a/src/compile/encodings/bytes.js b/src/compile/encodings/bytes.js new file mode 100644 index 0000000..b06ac90 --- /dev/null +++ b/src/compile/encodings/bytes.js @@ -0,0 +1,42 @@ +'use strict' + +const varint = require('varint') +const encoder = require('./encoder') + +function bytesBufferLength (val) { + return val.byteLength +} + +function bytesEncodingLength (val) { + const len = bytesBufferLength(val) + return varint.encodingLength(len) + len +} + +function bytesEncode (val, buffer, dataView, offset) { + const oldOffset = offset + const len = bytesBufferLength(val) + + varint.encode(len, buffer, offset) + offset += varint.encode.bytes + + buffer.set(val, offset) + offset += len + + bytesEncode.bytes = offset - oldOffset +} + +function bytesDecode (buffer, dataView, offset) { + const oldOffset = offset + + const len = varint.decode(buffer, offset) + offset += varint.decode.bytes + + const val = buffer.slice(offset, offset + len) + offset += val.length + + bytesDecode.bytes = offset - oldOffset + + return val +} + +module.exports = encoder(2, bytesEncode, bytesDecode, bytesEncodingLength) diff --git a/src/compile/encodings/double.js b/src/compile/encodings/double.js new file mode 100644 index 0000000..e6a6354 --- /dev/null +++ b/src/compile/encodings/double.js @@ -0,0 +1,21 @@ +'use strict' + +const encoder = require('./encoder') + +function doubleEncodingLength () { + return 8 +} + +function doubleEncode (val, buffer, dataView, offset) { + dataView.setFloat64(offset, val, true) + doubleEncode.bytes = 8 +} + +function doubleDecode (buffer, dataView, offset) { + const val = dataView.getFloat64(offset, true) + doubleDecode.bytes = 8 + + return val +} + +module.exports = encoder(1, doubleEncode, doubleDecode, doubleEncodingLength) diff --git a/src/compile/encodings/encoder.js b/src/compile/encodings/encoder.js new file mode 100644 index 0000000..02868cc --- /dev/null +++ b/src/compile/encodings/encoder.js @@ -0,0 +1,14 @@ +'use strict' + +function encoder (type, encode, decode, encodingLength) { + encode.bytes = decode.bytes = 0 + + return { + type: type, + encode: encode, + decode: decode, + encodingLength: encodingLength + } +} + +module.exports = encoder diff --git a/src/compile/encodings/fixed32.js b/src/compile/encodings/fixed32.js new file mode 100644 index 0000000..a9f0ded --- /dev/null +++ b/src/compile/encodings/fixed32.js @@ -0,0 +1,21 @@ +'use strict' + +const encoder = require('./encoder') + +function fixed32EncodingLength (val) { + return 4 +} + +function fixed32Encode (val, buffer, dataView, offset) { + dataView.setUint32(offset, val, true) + fixed32Encode.bytes = 4 +} + +function fixed32Decode (buffer, dataView, offset) { + const val = dataView.getUint32(offset, true) + fixed32Decode.bytes = 4 + + return val +} + +module.exports = encoder(5, fixed32Encode, fixed32Decode, fixed32EncodingLength) diff --git a/src/compile/encodings/fixed64.js b/src/compile/encodings/fixed64.js new file mode 100644 index 0000000..1c5c75a --- /dev/null +++ b/src/compile/encodings/fixed64.js @@ -0,0 +1,25 @@ +'use strict' + +const encoder = require('./encoder') + +function fixed64EncodingLength () { + return 8 +} + +function fixed64Encode (val, buffer, dataView, offset) { + for (const byte of val) { + buffer[offset] = byte + offset++ + } + + fixed64Encode.bytes = 8 +} + +function fixed64Decode (buffer, dataView, offset) { + const val = buffer.slice(offset, offset + 8) + fixed64Decode.bytes = 8 + + return val +} + +module.exports = encoder(1, fixed64Encode, fixed64Decode, fixed64EncodingLength) diff --git a/src/compile/encodings/float.js b/src/compile/encodings/float.js new file mode 100644 index 0000000..f996364 --- /dev/null +++ b/src/compile/encodings/float.js @@ -0,0 +1,21 @@ +'use strict' + +const encoder = require('./encoder') + +function floatEncodingLength () { + return 4 +} + +function floatEncode (val, buffer, dataView, offset) { + dataView.setFloat32(offset, val, true) + floatEncode.bytes = 4 +} + +function floatDecode (buffer, dataView, offset) { + const val = dataView.getFloat32(offset, true) + floatDecode.bytes = 4 + + return val +} + +module.exports = encoder(5, floatEncode, floatDecode, floatEncodingLength) diff --git a/src/compile/encodings/index.js b/src/compile/encodings/index.js new file mode 100644 index 0000000..2069778 --- /dev/null +++ b/src/compile/encodings/index.js @@ -0,0 +1,22 @@ +'use strict' + +exports.make = require('./encoder') +exports.bytes = require('./bytes') +exports.string = require('./string') +exports.bool = require('./bool') +exports.int32 = require('./int32') +exports.int64 = require('./int64') +exports.sint32 = +exports.sint64 = require('./sint64') +exports.uint32 = +exports.uint64 = +exports.enum = +exports.varint = require('./varint') + +// we cannot represent these in javascript so we just use buffers +exports.fixed64 = +exports.sfixed64 = require('./fixed64') +exports.double = require('./double') +exports.fixed32 = require('./fixed32') +exports.sfixed32 = require('./sfixed32') +exports.float = require('./float') diff --git a/src/compile/encodings/int32.js b/src/compile/encodings/int32.js new file mode 100644 index 0000000..9d9fe82 --- /dev/null +++ b/src/compile/encodings/int32.js @@ -0,0 +1,22 @@ +'use strict' + +const varint = require('varint') +const encoder = require('./encoder') + +function in32Encode (val, buffer, dataView, offset) { + varint.encode(val < 0 ? val + 4294967296 : val, buffer, offset) + in32Encode.bytes = varint.encode.bytes +} + +function int32Decode (buffer, dataView, offset) { + const val = varint.decode(buffer, offset) + int32Decode.bytes = varint.decode.bytes + + return val > 2147483647 ? val - 4294967296 : val +} + +function int32EncodingLength (val) { + return varint.encodingLength(val < 0 ? val + 4294967296 : val) +} + +module.exports = encoder(0, in32Encode, int32Decode, int32EncodingLength) diff --git a/src/compile/encodings/int64.js b/src/compile/encodings/int64.js new file mode 100644 index 0000000..fcb01ec --- /dev/null +++ b/src/compile/encodings/int64.js @@ -0,0 +1,49 @@ +'use strict' + +const varint = require('varint') +const encoder = require('./encoder') + +function int64Encode (val, buffer, dataView, offset) { + if (val < 0) { + const last = offset + 9 + varint.encode(val * -1, buffer, offset) + + offset += varint.encode.bytes - 1 + buffer[offset] = buffer[offset] | 0x80 + + while (offset < last - 1) { + offset++ + buffer[offset] = 0xff + } + buffer[last] = 0x01 + + int64Encode.bytes = 10 + } else { + varint.encode(val, buffer, offset) + int64Encode.bytes = varint.encode.bytes + } +} + +function int64Decode (buffer, dataView, offset) { + let val = varint.decode(buffer, offset) + + if (val >= Math.pow(2, 63)) { + let limit = 9 + while (buffer[offset + limit - 1] === 0xff) limit-- + limit = limit || 9 + const subset = buffer.subarray(offset, offset + limit) + subset[limit - 1] = subset[limit - 1] & 0x7f + val = -1 * varint.decode(subset, 0) + int64Decode.bytes = 10 + } else { + int64Decode.bytes = varint.decode.bytes + } + + return val +} + +function int64EncodingLength (val) { + return val < 0 ? 10 : varint.encodingLength(val) +} + +module.exports = encoder(0, int64Encode, int64Decode, int64EncodingLength) diff --git a/src/compile/encodings/sfixed32.js b/src/compile/encodings/sfixed32.js new file mode 100644 index 0000000..875db0f --- /dev/null +++ b/src/compile/encodings/sfixed32.js @@ -0,0 +1,21 @@ +'use strict' + +const encoder = require('./encoder') + +function sfixed32EncodingLength (val) { + return 4 +} + +function sfixed32Encode (val, buffer, dataView, offset) { + dataView.setInt32(offset, val, true) + sfixed32Encode.bytes = 4 +} + +function sfixed32Decode (buffer, dataView, offset) { + const val = dataView.getInt32(offset, true) + sfixed32Decode.bytes = 4 + + return val +} + +module.exports = encoder(5, sfixed32Encode, sfixed32Decode, sfixed32EncodingLength) diff --git a/src/compile/encodings/sint64.js b/src/compile/encodings/sint64.js new file mode 100644 index 0000000..614b3df --- /dev/null +++ b/src/compile/encodings/sint64.js @@ -0,0 +1,19 @@ +'use strict' + +const svarint = require('signed-varint') +const encoder = require('./encoder') + +function svarintEncode (val, buffer, dataView, offset) { + svarint.encode(val, buffer, offset) + + svarintEncode.bytes = svarint.encode.bytes +} + +function svarintDecode (buffer, dataView, offset) { + const val = svarint.decode(buffer, offset) + svarintDecode.bytes = svarint.decode.bytes + + return val +} + +module.exports = encoder(0, svarintEncode, svarintDecode, svarint.encodingLength) diff --git a/src/compile/encodings/string.js b/src/compile/encodings/string.js new file mode 100644 index 0000000..a865c4d --- /dev/null +++ b/src/compile/encodings/string.js @@ -0,0 +1,41 @@ +'use strict' + +const varint = require('varint') +const uint8ArrayFromString = require('uint8arrays/from-string') +const uint8ArrayToString = require('uint8arrays/to-string') +const encoder = require('./encoder') + +function stringEncodingLength (val) { + const len = uint8ArrayFromString(val).byteLength + return varint.encodingLength(len) + len +} + +function stringEncode (val, buffer, dataView, offset) { + const oldOffset = offset + const len = uint8ArrayFromString(val).byteLength + + varint.encode(len, buffer, offset, 'utf-8') + offset += varint.encode.bytes + + const arr = uint8ArrayFromString(val) + buffer.set(arr, offset) + offset += arr.length + + stringEncode.bytes = offset - oldOffset +} + +function stringDecode (buffer, dataView, offset) { + const oldOffset = offset + + const len = varint.decode(buffer, offset) + offset += varint.decode.bytes + + const val = uint8ArrayToString(buffer.subarray(offset, offset + len)) + offset += len + + stringDecode.bytes = offset - oldOffset + + return val +} + +module.exports = encoder(2, stringEncode, stringDecode, stringEncodingLength) diff --git a/src/compile/encodings/varint.js b/src/compile/encodings/varint.js new file mode 100644 index 0000000..a1d267a --- /dev/null +++ b/src/compile/encodings/varint.js @@ -0,0 +1,19 @@ +'use strict' + +const varint = require('varint') +const encoder = require('./encoder') + +function varintEncode (val, buffer, dataView, offset) { + varint.encode(val, buffer, offset) + + varintEncode.bytes = varint.encode.bytes +} + +function varintDecode (buffer, dataView, offset) { + const val = varint.decode(buffer, offset) + varintDecode.bytes = varint.decode.bytes + + return val +} + +module.exports = encoder(0, varintEncode, varintDecode, varint.encodingLength) diff --git a/src/compile/index.js b/src/compile/index.js index 2a5d703..69b9310 100644 --- a/src/compile/index.js +++ b/src/compile/index.js @@ -1,14 +1,14 @@ 'use strict' -var encodings = require('./encodings') -var compileDecode = require('./decode') -var compileEncode = require('./encode') -var compileEncodingLength = require('./encoding-length') -var varint = require('varint') +const encodings = require('./encodings') +const compileDecode = require('./decode') +const compileEncode = require('./encode') +const compileEncodingLength = require('./encoding-length') +const varint = require('varint') -var flatten = function (values) { +const flatten = function (values) { if (!values) return null - var result = {} + const result = {} Object.keys(values).forEach(function (k) { result[k] = values[k].value }) @@ -16,11 +16,11 @@ var flatten = function (values) { } module.exports = function (schema, extraEncodings) { - var messages = {} - var enums = {} - var cache = {} + const messages = {} + const enums = {} + const cache = {} - var visit = function (schema, prefix) { + const visit = function (schema, prefix) { if (schema.enums) { schema.enums.forEach(function (e) { e.id = prefix + (prefix ? '.' : '') + e.name @@ -35,8 +35,8 @@ module.exports = function (schema, extraEncodings) { m.fields.forEach(function (f) { if (!f.map) return - var name = 'Map_' + f.map.from + '_' + f.map.to - var map = { + const name = 'Map_' + f.map.from + '_' + f.map.to + const map = { name: name, enums: [], messages: [], @@ -71,33 +71,33 @@ module.exports = function (schema, extraEncodings) { visit(schema, '') - var compileEnum = function (e) { - var values = Object.keys(e.values || []).map(function (k) { + const compileEnum = function (e) { + const values = Object.keys(e.values || []).map(function (k) { return parseInt(e.values[k].value, 10) }) - var encode = function encode (val, buf, offset) { + const encode = function enumEncode (val, buf, view, offset) { if (!values.length || values.indexOf(val) === -1) { throw new Error('Invalid enum value: ' + val) } varint.encode(val, buf, offset) - encode.bytes = varint.encode.bytes + enumEncode.bytes = varint.encode.bytes return buf } - var decode = function decode (buf, offset) { + const decode = function enumDecode (buf, view, offset) { var val = varint.decode(buf, offset) if (!values.length || values.indexOf(val) === -1) { throw new Error('Invalid enum value: ' + val) } - decode.bytes = varint.decode.bytes + enumDecode.bytes = varint.decode.bytes return val } return encodings.make(0, encode, decode, varint.encodingLength) } - var compileMessage = function (m, exports) { + const compileMessage = function (m, exports) { m.messages.forEach(function (nested) { exports[nested.name] = resolve(nested.name, m.id) }) @@ -110,7 +110,7 @@ module.exports = function (schema, extraEncodings) { exports.message = true exports.name = m.name - var oneofs = {} + const oneofs = {} m.fields.forEach(function (f) { if (!f.oneof) return @@ -118,13 +118,13 @@ module.exports = function (schema, extraEncodings) { oneofs[f.oneof].push(f.name) }) - var enc = m.fields.map(function (f) { + const enc = m.fields.map(function (f) { return resolve(f.type, m.id) }) - var encodingLength = compileEncodingLength(m, enc, oneofs) - var encode = compileEncode(m, resolve, enc, oneofs, encodingLength) - var decode = compileDecode(m, resolve, enc) + const encodingLength = compileEncodingLength(m, enc, oneofs) + const encode = compileEncode(m, resolve, enc, oneofs, encodingLength) + const decode = compileDecode(m, resolve, enc) // end of compilation - return all the things @@ -138,11 +138,11 @@ module.exports = function (schema, extraEncodings) { return exports } - var resolve = function (name, from, compile) { + const resolve = function (name, from, compile) { if (extraEncodings && extraEncodings[name]) return extraEncodings[name] if (encodings[name]) return encodings[name] - var m = (from ? from + '.' + name : name).split('.') + const m = (from ? from + '.' + name : name).split('.') .map(function (part, i, list) { return list.slice(0, i).concat(name).join('.') }) @@ -155,7 +155,7 @@ module.exports = function (schema, extraEncodings) { if (!m) throw new Error('Could not resolve ' + name) if (m.values) return compileEnum(m) - var res = cache[m.id] || compileMessage(m, cache[m.id] = {}) + const res = cache[m.id] || compileMessage(m, cache[m.id] = {}) return res } diff --git a/src/index.js b/src/index.js index 2a76852..ede5874 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,6 @@ 'use strict' var schema = require('protocol-buffers-schema') -const { Buffer } = require('buffer') var compile = require('./compile') var flatten = function (values) { @@ -17,7 +16,7 @@ module.exports = function (proto, opts) { if (!opts) opts = {} if (!proto) throw new Error('Pass in a .proto string or a protobuf-schema parsed object') - var sch = (typeof proto === 'object' && !Buffer.isBuffer(proto)) ? proto : schema.parse(proto) + var sch = (typeof proto === 'object' && !(proto instanceof Uint8Array)) ? proto : schema.parse(proto) // to not make toString,toJSON enumarable we make a fire-and-forget prototype var Messages = function () { diff --git a/test/basic.js b/test/basic.js deleted file mode 100644 index 648e97d..0000000 --- a/test/basic.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobufNpm = require('protocol-buffers') -var protobuf = require('../') -var proto = require('./test.proto') -var Basic = protobuf(proto).Basic -var BasicNpm = protobufNpm(proto).Basic - -tape('basic encode', function (t) { - var first = { - num: 1, - payload: Buffer.from('lol') - } - - var b1 = Basic.encode(first) - - var bn1 = BasicNpm.encode(first) - - t.same(b1, bn1) - - var b2 = Basic.encode({ - num: 1, - payload: Buffer.from('lol'), - meeeh: 42 - }) - - var b3 = Basic.encode({ - num: 1, - payload: 'lol', - meeeh: 42 - }) - - t.same(b2, b1) - t.same(b3, b1) - - t.end() -}) - -tape('basic encode + decode', function (t) { - var b1 = Basic.encode({ - num: 1, - payload: Buffer.from('lol') - }) - - var o1 = Basic.decode(b1) - - t.same(o1.num, 1) - t.same(o1.payload, Buffer.from('lol')) - - var b2 = Basic.encode({ - num: 1, - payload: Buffer.from('lol'), - meeeh: 42 - }) - - var o2 = Basic.decode(b2) - - t.same(o2, o1) - t.end() -}) - -tape('basic accessors', function (t) { - var b1 = Basic.encode({ - num: 1, - payload: Buffer.from('lol') - }) - - var o1 = Basic.decode(b1) - - t.ok(o1.hasNum) - t.ok(o1.hasNum()) - - t.ok(o1.setNum) - o1.setNum(5) - - t.ok(o1.getNum) - t.same(o1.getNum(), 5) - - t.ok(o1.clearNum) - - o1.clearNum() - - t.same(o1.getNum(), undefined) - - const methods = Object.keys(o1) - - t.notOk(methods.includes('getNum')) - t.notOk(methods.includes('setNum')) - t.notOk(methods.includes('hasNum')) - t.notOk(methods.includes('clearNum')) - - t.end() -}) - -tape('basic encode + decode floats', function (t) { - var b1 = Basic.encode({ - num: 1.1, - payload: Buffer.from('lol') - }) - - var o1 = Basic.decode(b1) - - t.same(o1.num, 1.1) - t.same(o1.payload, Buffer.from('lol')) - t.end() -}) diff --git a/test/basic.spec.js b/test/basic.spec.js new file mode 100644 index 0000000..c228701 --- /dev/null +++ b/test/basic.spec.js @@ -0,0 +1,109 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobufNpm = require('protocol-buffers') +const protobuf = require('../src') +const proto = require('./test.proto') +const Basic = protobuf(proto).Basic +const BasicNpm = protobufNpm(proto).Basic +const uint8ArrayFromString = require('uint8arrays/from-string') +const uint8ArrayToString = require('uint8arrays/to-string') + +describe('basic', () => { + it('should encode basic object', () => { + const first = { + num: 1, + payload: uint8ArrayFromString('lol') + } + + const b1 = Basic.encode(first) + + const bn1 = BasicNpm.encode({ + ...first, + payload: 'lol' // old version does not support Uint8Arrays + }) + + expect(uint8ArrayToString(b1, 'base16')).to.equal(uint8ArrayToString(bn1, 'base16')) + + const b2 = Basic.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meeeh: 42 + }) + + const b3 = Basic.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meeeh: 42 + }) + + expect(b2).to.deep.equal(b1) + expect(b3).to.deep.equal(b1) + }) + + it('should encode and decode basic object', () => { + const b1 = Basic.encode({ + num: 1, + payload: uint8ArrayFromString('lol') + }) + + const o1 = Basic.decode(b1) + + expect(o1).to.have.property('num', 1) + expect(o1).to.have.deep.property('payload', uint8ArrayFromString('lol')) + + const b2 = Basic.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meeeh: 42 + }) + + const o2 = Basic.decode(b2) + + expect(o2).to.deep.equal(o1) + }) + + it('should add basic accessors', () => { + const b1 = Basic.encode({ + num: 1, + payload: uint8ArrayFromString('lol') + }) + + const o1 = Basic.decode(b1) + + expect(o1).to.have.property('hasNum').that.is.a('function') + expect(o1.hasNum()).to.be.true() + + expect(o1).to.have.property('setNum').that.is.a('function') + o1.setNum(5) + + expect(o1).to.have.property('getNum').that.is.a('function') + expect(o1.getNum(5)).to.equal(5) + + expect(o1).to.have.property('clearNum').that.is.a('function') + o1.clearNum() + + expect(o1.getNum(5)).to.be.undefined() + + const methods = Object.keys(o1) + + expect(methods).to.not.include('getNum') + expect(methods).to.not.include('setNum') + expect(methods).to.not.include('hasNum') + expect(methods).to.not.include('clearNum') + }) + + it('should encode and decode floats in a basic object', () => { + const b1 = Basic.encode({ + num: 1.1, + payload: uint8ArrayFromString('lol') + }) + + const o1 = Basic.decode(b1) + + expect(o1).to.have.property('num', 1.1) + expect(o1).to.have.deep.property('payload', uint8ArrayFromString('lol')) + }) +}) diff --git a/test/booleans.js b/test/booleans.js deleted file mode 100644 index f8a8114..0000000 --- a/test/booleans.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../src') -var proto = require('./test.proto') -var Booleans = protobuf(proto).Booleans - -tape('booleans encode + decode', function (t) { - var b1 = Booleans.encode({ - bool1: true, - bool2: false - }) - - var o1 = Booleans.decode(b1) - - t.same(o1, { - bool1: true, - bool2: false - }) - - t.end() -}) - -tape('booleans encode + decode + optional', function (t) { - var b1 = Booleans.encode({ - bool1: true - }) - - var o1 = Booleans.decode(b1) - - t.same(o1, { - bool1: true, - bool2: false - }) - t.notOk(o1.hasBool2()) - - t.end() -}) diff --git a/test/booleans.spec.js b/test/booleans.spec.js new file mode 100644 index 0000000..bf83de5 --- /dev/null +++ b/test/booleans.spec.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const proto = require('./test.proto') +const Booleans = protobuf(proto).Booleans + +describe('booleans', () => { + it('should encode and decode booleans', () => { + const b1 = Booleans.encode({ + bool1: true, + bool2: false + }) + + const o1 = Booleans.decode(b1) + + expect(o1).to.deep.equal({ + bool1: true, + bool2: false + }) + }) + + it('should encode and decode optional booleans', () => { + const b1 = Booleans.encode({ + bool1: true + }) + const o1 = Booleans.decode(b1) + + expect(o1).to.deep.equal({ + bool1: true, + bool2: false + }) + expect(o1.hasBool2()).to.be.false() + }) +}) diff --git a/test/bytes.js b/test/bytes.js deleted file mode 100644 index 30ecfed..0000000 --- a/test/bytes.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../src') -var Bytes = protobuf(require('./test.proto')).Bytes - -tape('bytes encode + decode', function (t) { - var b1 = Bytes.encode({ - req: Uint8Array.from([0, 1, 2, 3]) - }) - - var o1 = Bytes.decode(b1) - - t.same(o1, { - req: Buffer.from([0, 1, 2, 3]), - opt: null - }) - t.notOk(o1.hasOpt()) - - t.end() -}) diff --git a/test/bytes.spec.js b/test/bytes.spec.js new file mode 100644 index 0000000..b199111 --- /dev/null +++ b/test/bytes.spec.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Bytes = protobuf(require('./test.proto')).Bytes + +describe('bytes', () => { + it('should encode and decode bytes', () => { + const b1 = Bytes.encode({ + req: Uint8Array.from([0, 1, 2, 3]), + opt: Uint8Array.from([4, 5, 6, 7]) + }) + + const o1 = Bytes.decode(b1) + + expect(o1).to.deep.equal({ + req: Uint8Array.from([0, 1, 2, 3]), + opt: Uint8Array.from([4, 5, 6, 7]) + }) + }) + + it('should encode and decode optional bytes', () => { + const b1 = Bytes.encode({ + req: Uint8Array.from([0, 1, 2, 3]) + }) + const o1 = Bytes.decode(b1) + + expect(o1).to.deep.equal({ + req: Uint8Array.from([0, 1, 2, 3]), + opt: null + }) + expect(o1.hasOpt()).to.be.false() + }) +}) diff --git a/test/corrupted.js b/test/corrupted.js deleted file mode 100644 index ceae87a..0000000 --- a/test/corrupted.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') - -var protoStr = 'enum AbcType {\n' + - ' IGNORE = 0;\n' + - ' ACK_CONFIRMATION_TOKEN = 1;\n' + - '}\n' + - 'message AbcAcknowledgeConfirmationToken { // 0x01\n' + - ' optional uint64 confirmation_token = 1;\n' + - ' extensions 1000 to max;\n' + - '}\n' + - 'message ABC {\n' + - ' required AbcType type = 9;\n' + - ' required uint32 api_version = 8;\n' + - ' optional AbcAcknowledgeConfirmationToken ack_confirmation_token = 1;\n' + - ' extensions 1000 to max;\n' + - '}\n' + - 'message Open {\n' + - ' required bytes feed = 1;\n' + - ' required bytes nonce = 2;\n' + - '}' - -var messages = protobuf(protoStr) - -tape('invalid message decode', function (t) { - var didFail = false - try { - messages.ABC.decode(Uint8Array.from([8, 182, 168, 235, 144, 178, 41])) - } catch (e) { - didFail = true - } - t.same(didFail, true, 'bad input') - t.end() -}) - -tape('non buffers should fail', function (t) { - var didFail = false - try { - messages.ABC.decode({}) - } catch (e) { - didFail = true - } - t.same(didFail, true, 'bad input') - t.end() -}) - -tape('protocol parser test case', function (t) { - var didFail = false - var buf = Buffer.from('cec1', 'hex') - try { - messages.Open.decode(buf) - } catch (err) { - didFail = true - } - t.same(didFail, true, 'bad input') - t.end() -}) diff --git a/test/corrupted.spec.js b/test/corrupted.spec.js new file mode 100644 index 0000000..82f92c3 --- /dev/null +++ b/test/corrupted.spec.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const uint8ArrayFromString = require('uint8arrays/from-string') +const protobuf = require('../src') + +const protoStr = 'enum AbcType {\n' + + ' IGNORE = 0;\n' + + ' ACK_CONFIRMATION_TOKEN = 1;\n' + + '}\n' + + 'message AbcAcknowledgeConfirmationToken { // 0x01\n' + + ' optional uint64 confirmation_token = 1;\n' + + ' extensions 1000 to max;\n' + + '}\n' + + 'message ABC {\n' + + ' required AbcType type = 9;\n' + + ' required uint32 api_version = 8;\n' + + ' optional AbcAcknowledgeConfirmationToken ack_confirmation_token = 1;\n' + + ' extensions 1000 to max;\n' + + '}\n' + + 'message Open {\n' + + ' required bytes feed = 1;\n' + + ' required bytes nonce = 2;\n' + + '}' + +const messages = protobuf(protoStr) + +describe('corrupted', () => { + it('should fail to decode an invalid message', () => { + expect(() => { + messages.ABC.decode(Uint8Array.from([8, 182, 168, 235, 144, 178, 41])) + }).to.throw(/not valid/) + }) + + it('should fail to decode non-byte arrays', () => { + expect(() => { + messages.ABC.decode({}) + }).to.throw(/not valid/) + }) + + it('should fail to decode a base16 message', () => { + const buf = uint8ArrayFromString('cec1', 'base16') + + expect(() => { + messages.Open.decode(buf) + }).to.throw(/Could not decode varint/) + }) +}) diff --git a/test/custom-types.js b/test/custom-types.js deleted file mode 100644 index 0e2cda3..0000000 --- a/test/custom-types.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../src') -var CustomType = protobuf(require('./test.proto')).CustomType - -tape('custom types encode + decode', function (t) { - var b1 = CustomType.encode({ - req: { - num: 5, - payload: Uint8Array.from([]) - } - }) - - var o1 = CustomType.decode(b1) - - t.same(o1, { - req: { - num: 5, - payload: Buffer.from([]) - }, - opt: null - }) - t.notOk(o1.hasOpt()) - - t.end() -}) diff --git a/test/custom-types.spec.js b/test/custom-types.spec.js new file mode 100644 index 0000000..eab554b --- /dev/null +++ b/test/custom-types.spec.js @@ -0,0 +1,55 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const CustomType = protobuf(require('./test.proto')).CustomType + +describe('custom types', () => { + it('should encode and decode custom types', () => { + var b1 = CustomType.encode({ + req: { + num: 5, + payload: Uint8Array.from([]) + }, + opt: { + num: 6, + payload: Uint8Array.from([]) + } + }) + + var o1 = CustomType.decode(b1) + + expect(o1).to.deep.equal({ + req: { + num: 5, + payload: Uint8Array.from([]) + }, + opt: { + num: 6, + payload: Uint8Array.from([]) + } + }) + }) + + it('should encode and decode custom types with optional fields', () => { + var b1 = CustomType.encode({ + req: { + num: 5, + payload: Uint8Array.from([]) + } + }) + + var o1 = CustomType.decode(b1) + + expect(o1).to.deep.equal({ + req: { + num: 5, + payload: Uint8Array.from([]) + }, + opt: null + }) + expect(o1.hasOpt()).to.be.false() + }) +}) diff --git a/test/defaults.js b/test/defaults.js deleted file mode 100644 index 7b2386b..0000000 --- a/test/defaults.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Defaults = protobuf(require('./test.proto')).Defaults - -tape('defaults decode', function (t) { - var o1 = Defaults.decode(Buffer.alloc(0)) // everything default - - var b2 = Defaults.encode({ - num: 10, - foos: [1] - }) - - var b3 = Defaults.encode({ - num: 10, - foo2: 2 - }) - - t.same(Defaults.decode(b3), { - num: 10, - foo1: 2, - foo2: 2, - foos: [] - }, '1 default') - - t.same(o1, { - num: 42, - foo1: 2, - foo2: 1, - foos: [] - }, 'all defaults') - - t.same(Defaults.decode(b2), { - num: 10, - foo1: 2, - foo2: 1, - foos: [1] - }, '2 defaults') - - t.end() -}) diff --git a/test/defaults.spec.js b/test/defaults.spec.js new file mode 100644 index 0000000..08852b9 --- /dev/null +++ b/test/defaults.spec.js @@ -0,0 +1,44 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Defaults = protobuf(require('./test.proto')).Defaults + +describe('default', () => { + it('should decode with defaults', () => { + const o1 = Defaults.decode(new Uint8Array()) // everything default + + const b2 = Defaults.encode({ + num: 10, + foos: [1] + }) + + const b3 = Defaults.encode({ + num: 10, + foo2: 2 + }) + + expect(Defaults.decode(b3)).to.deep.equal({ + num: 10, + foo1: 2, + foo2: 2, + foos: [] + }) + + expect(o1).to.deep.equal({ + num: 42, + foo1: 2, + foo2: 1, + foos: [] + }) + + expect(Defaults.decode(b2)).to.deep.equal({ + num: 10, + foo1: 2, + foo2: 1, + foos: [1] + }) + }) +}) diff --git a/test/enums.js b/test/enums.js deleted file mode 100644 index a1977dd..0000000 --- a/test/enums.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var messages = protobuf(require('./test.proto')) - -tape('enums', function (t) { - var e = messages.FOO - - t.same(e, { A: 1, B: 2 }, 'enum is defined') - t.end() -}) - -tape('hex enums', function (t) { - var e = messages.FOO_HEX - - t.same(e, { A: 1, B: 2 }, 'enum is defined using hex') - t.end() -}) diff --git a/test/enums.spec.js b/test/enums.spec.js new file mode 100644 index 0000000..1da47df --- /dev/null +++ b/test/enums.spec.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const messages = protobuf(require('./test.proto')) + +describe('enums', () => { + it('should encode and decode enums', () => { + const e = messages.FOO + + expect(e).to.deep.equal({ A: 1, B: 2 }) + }) + + it('should encode and decode hex enums', () => { + const e = messages.FOO_HEX + + expect(e).to.deep.equal({ A: 1, B: 2 }) + }) +}) diff --git a/test/float.js b/test/float.js deleted file mode 100644 index 84e1ed5..0000000 --- a/test/float.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Float = protobuf(require('./test.proto')).Float - -tape('float encode + decode', function (t) { - var arr = new Float32Array(3) - arr[0] = 1.1 - arr[1] = 0 - arr[2] = -2.3 - - var obj = { - float1: arr[0], - float2: arr[1], - float3: arr[2] - } - - var b1 = Float.encode(obj) - - var o1 = Float.decode(b1) - - t.same(o1, obj) - - t.end() -}) diff --git a/test/float.spec.js b/test/float.spec.js new file mode 100644 index 0000000..a9eaa48 --- /dev/null +++ b/test/float.spec.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Float = protobuf(require('./test.proto')).Float + +describe('floats', () => { + it('should encode and decode floats', () => { + const arr = new Float32Array(3) + arr[0] = 1.1 + arr[1] = 0 + arr[2] = -2.3 + + const obj = { + float1: arr[0], + float2: arr[1], + float3: arr[2] + } + + const b1 = Float.encode(obj) + + const o1 = Float.decode(b1) + + expect(o1).to.deep.equal(obj) + }) +}) diff --git a/test/integers.js b/test/integers.js deleted file mode 100644 index 911172e..0000000 --- a/test/integers.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Integers = protobuf(require('./test.proto')).Integers - -tape('integers encode + decode', function (t) { - var b1 = Integers.encode({ - sint32: 1, - sint64: 2, - int32: 3, - uint32: 4, - int64: 5 - }) - - var o1 = Integers.decode(b1) - - t.same(o1, { - sint32: 1, - sint64: 2, - int32: 3, - uint32: 4, - int64: 5 - }) - - t.end() -}) - -tape('integers encode + decode + negative', function (t) { - var b1 = Integers.encode({ - sint32: -1, - sint64: -2, - int32: -3, - uint32: 0, - int64: -1 * Math.pow(2, 52) - 5 - }) - - var o1 = Integers.decode(b1) - - t.same(o1, { - sint32: -1, - sint64: -2, - int32: -3, - uint32: 0, - int64: -1 * Math.pow(2, 52) - 5 - }) - - t.end() -}) - -tape('integers encode + decode + optional', function (t) { - var b1 = Integers.encode({ - sint32: null - }) - var b2 = Integers.encode({ - sint32: 0 - }) - - // sint32 is optional, verify that setting it to null does not - // cause a value to be written into the encoded buffer - t.ok(b1.length < b2.length) - - var o1 = Integers.decode(b1) - t.notOk(o1.hasSint32()) - - var o2 = Integers.decode(b2) - t.equal(o2.sint32, 0) - - t.end() -}) diff --git a/test/integers.spec.js b/test/integers.spec.js new file mode 100644 index 0000000..2b7bf69 --- /dev/null +++ b/test/integers.spec.js @@ -0,0 +1,72 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Integers = protobuf(require('./test.proto')).Integers + +describe('integers', () => { + it('should encode and decode integers', () => { + const b1 = Integers.encode({ + sint32: 1, + sint64: 2, + int32: 3, + uint32: 4, + int64: 5, + fixed32: 6 + }) + + const o1 = Integers.decode(b1) + + expect(o1).to.deep.equal({ + sint32: 1, + sint64: 2, + int32: 3, + uint32: 4, + int64: 5, + fixed32: 6 + }) + }) + + it('should encode and decode negative integers', () => { + const b1 = Integers.encode({ + sint32: -1, + sint64: -2, + int32: -3, + uint32: 0, + int64: -1 * Math.pow(2, 52) - 5, + fixed32: 0 + }) + + const o1 = Integers.decode(b1) + + expect(o1).to.deep.equal({ + sint32: -1, + sint64: -2, + int32: -3, + uint32: 0, + int64: -1 * Math.pow(2, 52) - 5, + fixed32: 0 + }) + }) + + it('should encode and decode optional integers', () => { + const b1 = Integers.encode({ + sint32: null + }) + const b2 = Integers.encode({ + sint32: 0 + }) + + // sint32 is optional, verify that setting it to null does not + // cause a value to be written into the encoded buffer + expect(b1.length).to.be.lessThan(b2.length) + + const o1 = Integers.decode(b1) + expect(o1.hasSint32()).to.be.false() + + const o2 = Integers.decode(b2) + expect(o2.sint32).to.equal(0) + }) +}) diff --git a/test/map.js b/test/map.js deleted file mode 100644 index cff888e..0000000 --- a/test/map.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Map = protobuf(require('./test.proto')).Map - -tape('map encode + decode', function (t) { - var b1 = Map.encode({ - foo: { - hello: 'world' - } - }) - - var o1 = Map.decode(b1) - - t.same(o1.foo, { hello: 'world' }) - - var doc = { - foo: { - hello: 'world', - hi: 'verden' - } - } - - var b2 = Map.encode(doc) - var o2 = Map.decode(b2) - - t.same(o2, doc) - t.end() -}) diff --git a/test/map.spec.js b/test/map.spec.js new file mode 100644 index 0000000..e8de565 --- /dev/null +++ b/test/map.spec.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Map = protobuf(require('./test.proto')).Map + +describe('maps', () => { + it('should encode and decode maps', () => { + const b1 = Map.encode({ + foo: { + hello: 'world' + } + }) + + const o1 = Map.decode(b1) + + expect(o1).to.have.deep.property('foo', { hello: 'world' }) + }) + + it('should encode and decode maps with multiple fields', () => { + const doc = { + foo: { + hello: 'world', + hi: 'verden' + } + } + + const b2 = Map.encode(doc) + const o2 = Map.decode(b2) + + expect(o2).to.deep.equal(doc) + }) +}) diff --git a/test/nan.js b/test/nan.js deleted file mode 100644 index e4b3d39..0000000 --- a/test/nan.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') - -var protoStr = 'message MyMessage {\n' + - ' optional uint32 my_number = 1;\n' + - ' required string my_other = 2;\n' + - '}' - -var messages = protobuf(protoStr) - -tape('NaN considered not defined', function (t) { - var didFail = false - var error - var encoded - var decoded - var testString = 'hello!' - var properResult = Buffer.from([0x12, 0x06, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21]) - try { - encoded = messages.MyMessage.encode({ my_number: NaN, my_other: testString }) - decoded = messages.MyMessage.decode(encoded) - t.same(decoded.my_other, testString, 'object is parsable') - t.same(encoded, properResult, 'object was encoded properly') - } catch (e) { - error = e - didFail = true - } - t.same(didFail, false, error ? 'parsing error: ' + error.toString() : 'no parsing error') - t.end() -}) diff --git a/test/nan.spec.js b/test/nan.spec.js new file mode 100644 index 0000000..4e11c85 --- /dev/null +++ b/test/nan.spec.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') + +const protoStr = 'message MyMessage {\n' + + ' optional uint32 my_number = 1;\n' + + ' required string my_other = 2;\n' + + '}' + +const messages = protobuf(protoStr) + +describe('NaN', () => { + it('should consider NaN as not defined', () => { + const testString = 'hello!' + const properResult = Uint8Array.from([0x12, 0x06, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x21]) + const encoded = messages.MyMessage.encode({ + my_number: NaN, + my_other: testString + }) + const decoded = messages.MyMessage.decode(encoded) + + expect(decoded).to.have.property('my_other', testString) + expect(encoded).to.deep.equal(properResult) + }) +}) diff --git a/test/nested.js b/test/nested.js deleted file mode 100644 index bbd77fd..0000000 --- a/test/nested.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -const TextEncoder = require('ipfs-utils/src/text-encoder') - -var Nested = protobuf(require('./test.proto')).Nested - -tape('nested encode', function (t) { - var b1 = Nested.encode({ - num: 1, - payload: new TextEncoder().encode('lol'), - meh: { - num: 2, - payload: new TextEncoder().encode('bar') - } - }) - - var b2 = Nested.encode({ - num: 1, - payload: new TextEncoder().encode('lol'), - meeeh: 42, - meh: { - num: 2, - payload: new TextEncoder().encode('bar') - } - }) - - t.same(b2, b1) - t.end() -}) - -tape('nested encode + decode', function (t) { - var b1 = Nested.encode({ - num: 1, - payload: new TextEncoder().encode('lol'), - meh: { - num: 2, - payload: new TextEncoder().encode('bar') - } - }) - - var o1 = Nested.decode(b1) - - t.same(o1.num, 1) - t.same(o1.payload, Buffer.from('lol')) - t.ok(o1.meh, 'has nested property') - t.same(o1.meh.num, 2) - t.same(o1.meh.payload, Buffer.from('bar')) - - var b2 = Nested.encode({ - num: 1, - payload: new TextEncoder().encode('lol'), - meeeh: 42, - meh: { - num: 2, - payload: new TextEncoder().encode('bar') - } - }) - - var o2 = Nested.decode(b2) - - t.same(o2, o1) - t.end() -}) diff --git a/test/nested.spec.js b/test/nested.spec.js new file mode 100644 index 0000000..d84dff5 --- /dev/null +++ b/test/nested.spec.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const uint8ArrayFromString = require('uint8arrays/from-string') + +const Nested = protobuf(require('./test.proto')).Nested + +describe('nested', () => { + it('should encode nested objects', () => { + const b1 = Nested.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meh: { + num: 2, + payload: uint8ArrayFromString('bar') + } + }) + + const b2 = Nested.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meeeh: 42, + meh: { + num: 2, + payload: uint8ArrayFromString('bar') + } + }) + + expect(b2).to.deep.equal(b1) + }) + + it('should decode nested objects', () => { + const b1 = Nested.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meh: { + num: 2, + payload: uint8ArrayFromString('bar') + } + }) + + const o1 = Nested.decode(b1) + + expect(o1).to.have.property('num', 1) + expect(o1).to.have.deep.property('payload', uint8ArrayFromString('lol')) + expect(o1).to.have.deep.property('meh') + + expect(o1).to.have.deep.property('meh', { + num: 2, + payload: uint8ArrayFromString('bar') + }) + + const b2 = Nested.encode({ + num: 1, + payload: uint8ArrayFromString('lol'), + meeeh: 42, + meh: { + num: 2, + payload: uint8ArrayFromString('bar') + } + }) + + const o2 = Nested.decode(b2) + + expect(o2).to.deep.equal(o1) + }) +}) diff --git a/test/notpacked.js b/test/notpacked.js deleted file mode 100644 index 2f201ad..0000000 --- a/test/notpacked.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var proto = require('./test.proto') -var NotPacked = protobuf(proto).NotPacked -var FalsePacked = protobuf(proto).FalsePacked - -tape('NotPacked encode + FalsePacked decode', function (t) { - var b1 = NotPacked.encode({ - id: [9847136125], - value: 10000 - }) - - var o1 = FalsePacked.decode(b1) - - t.same(o1.id.length, 1) - t.same(o1.id[0], 9847136125) - - t.end() -}) - -tape('FalsePacked encode + NotPacked decode', function (t) { - var b1 = FalsePacked.encode({ - id: [9847136125], - value: 10000 - }) - - var o1 = NotPacked.decode(b1) - - t.same(o1.id.length, 1) - t.same(o1.id[0], 9847136125) - - t.end() -}) diff --git a/test/notpacked.spec.js b/test/notpacked.spec.js new file mode 100644 index 0000000..a11d690 --- /dev/null +++ b/test/notpacked.spec.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const proto = require('./test.proto') +const NotPacked = protobuf(proto).NotPacked +const FalsePacked = protobuf(proto).FalsePacked + +describe('not packed', () => { + it('should encode NotPacked and decode FalsePacked', () => { + const b1 = NotPacked.encode({ + id: [9847136125], + value: 10000 + }) + + const o1 = FalsePacked.decode(b1) + + expect(o1).to.have.deep.property('id', [9847136125]) + }) + + it('should encode FalsePacked and decode NotPacked', () => { + const b1 = FalsePacked.encode({ + id: [9847136125], + value: 10000 + }) + + const o1 = NotPacked.decode(b1) + + expect(o1).to.have.deep.property('id', [9847136125]) + }) +}) diff --git a/test/oneof.js b/test/oneof.js deleted file mode 100644 index 9c7d3b3..0000000 --- a/test/oneof.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var proto = protobuf(require('./test.proto')) -var Property = proto.Property -var PropertyNoOneof = proto.PropertyNoOneof - -var data = { - name: 'Foo', - desc: 'optional description', - int_value: 12345 -} - -tape('oneof encode', function (t) { - t.ok(Property.encode(data), 'oneof encode') - t.end() -}) - -tape('oneof encode + decode', function (t) { - var buf = Property.encode(data) - var out = Property.decode(buf) - t.deepEqual(out, data) - t.end() -}) - -tape('oneof encode of overloaded json throws', function (t) { - var invalidData = { - name: 'Foo', - desc: 'optional description', - string_value: 'Bar', // ignored - bool_value: true, // ignored - int_value: 12345 // retained, was last entered - } - try { - Property.encode(invalidData) - } catch (err) { - t.ok(true, 'should throw') - t.end() - } -}) - -tape('oneof encode + decode of overloaded oneof buffer', function (t) { - var invalidData = { - name: 'Foo', - desc: 'optional description', - string_value: 'Bar', // retained, has highest tag number - bool_value: true, // ignored - int_value: 12345 // ignored - } - var validData = { - name: 'Foo', - desc: 'optional description', - string_value: 'Bar' - } - - var buf = PropertyNoOneof.encode(invalidData) - var out = Property.decode(buf) - t.deepEqual(validData, out) - t.end() -}) diff --git a/test/oneof.spec.js b/test/oneof.spec.js new file mode 100644 index 0000000..f0d4c42 --- /dev/null +++ b/test/oneof.spec.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const proto = protobuf(require('./test.proto')) +const Property = proto.Property +const PropertyNoOneof = proto.PropertyNoOneof + +const data = { + name: 'Foo', + desc: 'optional description', + int_value: 12345 +} + +describe('onof', () => { + it('should encode oneof', () => { + expect(Property.encode(data)).to.be.ok() + }) + + it('should encode and decode oneof', () => { + const buf = Property.encode(data) + const out = Property.decode(buf) + + expect(out).to.deep.equal(data) + }) + + it('should throw when encoding overloaded json', () => { + expect(() => { + Property.encode({ + name: 'Foo', + desc: 'optional description', + string_value: 'Bar', // ignored + bool_value: true, // ignored + int_value: 12345 // retained, was last entered + }) + }).to.throw(/only one of the properties defined in oneof value can be set/) + }) + + it('should encode and decode overloaded oneof buffer', () => { + const invalidData = { + name: 'Foo', + desc: 'optional description', + string_value: 'Bar', // retained, has highest tag number + bool_value: true, // ignored + int_value: 12345 // ignored + } + const validData = { + name: 'Foo', + desc: 'optional description', + string_value: 'Bar' + } + + const buf = PropertyNoOneof.encode(invalidData) + const out = Property.decode(buf) + expect(validData).to.deep.equal(out) + }) +}) diff --git a/test/optional.js b/test/optional.js deleted file mode 100644 index 49696a3..0000000 --- a/test/optional.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var proto = require('./test.proto') -var Optional = protobuf(proto).Optional - -tape('optional encode + decode has zero value', function (t) { - const o1 = {} - const b1 = Optional.encode(o1) - const o2 = Optional.decode(b1) - - t.same(o1.value, undefined) - t.same(o2.value, 0) - t.end() -}) - -tape('optional accessors', function (t) { - const o1 = Optional.decode(Optional.encode({})) - - t.ok(o1.hasValue) - t.notOk(o1.hasValue()) - - t.ok(o1.setValue) - o1.setValue(5) - t.ok(o1.hasValue()) - - t.ok(o1.getValue) - t.same(o1.getValue(), 5) - - t.ok(o1.clearValue) - o1.clearValue() - - t.notOk(o1.hasValue()) - t.same(o1.getValue(), undefined) - - const methods = Object.keys(o1) - - t.notOk(methods.includes('getValue')) - t.notOk(methods.includes('setValue')) - t.notOk(methods.includes('hasValue')) - t.notOk(methods.includes('clearValue')) - - t.end() -}) - -tape('optional accessors with zero values', function (t) { - const o1 = Optional.decode(Optional.encode({})) - - t.notOk(o1.hasValue()) - - o1.setValue(0) - t.ok(o1.hasValue()) - - t.ok(o1.getValue) - t.same(o1.getValue(), 0) - - const o2 = Optional.decode(Optional.encode(o1)) - - t.ok(o2.hasValue()) - t.same(o2.getValue(), 0) - - t.end() -}) diff --git a/test/optional.spec.js b/test/optional.spec.js new file mode 100644 index 0000000..adc4461 --- /dev/null +++ b/test/optional.spec.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const proto = require('./test.proto') +const Optional = protobuf(proto).Optional + +describe('optional', () => { + it('should encode and decode zero value for optional value', () => { + const o1 = {} + const b1 = Optional.encode(o1) + const o2 = Optional.decode(b1) + + expect(o1).to.not.have.property('value') + expect(o2).to.have.property('value', 0) + }) + + it('should create accessors for optional values', () => { + const o1 = Optional.decode(Optional.encode({})) + + expect(o1).to.have.property('hasValue').that.is.a('function') + expect(o1.hasValue()).to.be.false() + + expect(o1).to.have.property('setValue').that.is.a('function') + o1.setValue(5) + expect(o1.hasValue()).to.be.true() + + expect(o1).to.have.property('getValue').that.is.a('function') + expect(o1.getValue()).to.equal(5) + + expect(o1).to.have.property('clearValue').that.is.a('function') + o1.clearValue() + + expect(o1.hasValue()).to.be.false() + expect(o1.getValue()).to.be.undefined() + }) + + it('should have non-enumerable accessors for optional values', () => { + const o1 = Optional.decode(Optional.encode({})) + const methods = Object.keys(o1) + + expect(methods).to.not.include('getValue') + expect(methods).to.not.include('setValue') + expect(methods).to.not.include('hasValue') + expect(methods).to.not.include('clearValue') + }) + + it('should return zero values from optional accessors when no value has been set', () => { + const o1 = Optional.decode(Optional.encode({})) + + expect(o1.hasValue()).to.be.false() + o1.setValue(0) + expect(o1.hasValue()).to.be.true() + + expect(o1.getValue).to.be.a('function') + expect(o1.getValue()).to.equal(0) + + const o2 = Optional.decode(Optional.encode(o1)) + + expect(o2.hasValue()).to.be.ok() + expect(o2.getValue()).to.equal(0) + }) +}) diff --git a/test/packed.js b/test/packed.js deleted file mode 100644 index 9fb86c0..0000000 --- a/test/packed.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Packed = protobuf(require('./test.proto')).Packed - -tape('Packed encode', function (t) { - var b1 = Packed.encode({ - packed: [ - 12, - 13, - 14 - ] - }) - - var b2 = Packed.encode({ - packed: [ - 12, - 13, - 14 - ], - meeh: 42 - }) - - t.same(b2, b1) - t.end() -}) - -tape('Packed encode + decode', function (t) { - var b1 = Packed.encode({ - packed: [ - 12, - 13, - 14 - ] - }) - - var o1 = Packed.decode(b1) - - t.same(o1.packed.length, 3) - t.same(o1.packed[0], 12) - t.same(o1.packed[1], 13) - t.same(o1.packed[2], 14) - - var b2 = Packed.encode({ - packed: [ - 12, - 13, - 14 - ], - meeh: 42 - }) - - var o2 = Packed.decode(b2) - - t.same(o2, o1) - t.end() -}) diff --git a/test/packed.spec.js b/test/packed.spec.js new file mode 100644 index 0000000..ae7eaa2 --- /dev/null +++ b/test/packed.spec.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Packed = protobuf(require('./test.proto')).Packed + +describe('packed', () => { + it('should encode packed fields', () => { + const b1 = Packed.encode({ + packed: [ + 12, + 13, + 14 + ] + }) + + const b2 = Packed.encode({ + packed: [ + 12, + 13, + 14 + ], + meeh: 42 + }) + + expect(b2).to.deep.equal(b1) + }) + + it('should decode packed fields', () => { + const b1 = Packed.encode({ + packed: [ + 12, + 13, + 14 + ] + }) + + const o1 = Packed.decode(b1) + + expect(o1).to.have.deep.property('packed', [ + 12, + 13, + 14 + ]) + + const b2 = Packed.encode({ + packed: [ + 12, + 13, + 14 + ], + meeh: 42 + }) + + const o2 = Packed.decode(b2) + + expect(o2).to.deep.equal(o1) + }) +}) diff --git a/test/repeated.js b/test/repeated.js deleted file mode 100644 index 4929c3b..0000000 --- a/test/repeated.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var Repeated = protobuf(require('./test.proto')).Repeated -const TextEncoder = require('ipfs-utils/src/text-encoder') - -tape('repeated encode', function (t) { - var b1 = Repeated.encode({ - list: [{ - num: 1, - payload: new TextEncoder().encode('lol') - }, { - num: 2, - payload: new TextEncoder().encode('lol1') - }] - }) - - var b2 = Repeated.encode({ - list: [{ - num: 1, - payload: new TextEncoder().encode('lol') - }, { - num: 2, - payload: new TextEncoder().encode('lol1'), - meeeeh: 100 - }], - meeh: 42 - }) - - t.same(b2, b1) - t.end() -}) - -tape('repeated encode + decode', function (t) { - var b1 = Repeated.encode({ - list: [{ - num: 1, - payload: new TextEncoder().encode('lol') - }, { - num: 2, - payload: new TextEncoder().encode('lol1') - }] - }) - - var o1 = Repeated.decode(b1) - - t.same(o1.list.length, 2) - t.same(o1.list[0].num, 1) - t.same(o1.list[0].payload, Buffer.from('lol')) - t.same(o1.list[1].num, 2) - t.same(o1.list[1].payload, Buffer.from('lol1')) - - var b2 = Repeated.encode({ - list: [{ - num: 1, - payload: new TextEncoder().encode('lol') - }, { - num: 2, - payload: new TextEncoder().encode('lol1'), - meeeeh: 100 - }], - meeh: 42 - }) - - var o2 = Repeated.decode(b2) - - t.same(o2, o1) - t.end() -}) diff --git a/test/repeated.spec.js b/test/repeated.spec.js new file mode 100644 index 0000000..6ddc60a --- /dev/null +++ b/test/repeated.spec.js @@ -0,0 +1,74 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Repeated = protobuf(require('./test.proto')).Repeated +const uint8ArrayFromString = require('uint8arrays/from-string') + +describe('repeated', () => { + it('should encode repeated fields', () => { + const b1 = Repeated.encode({ + list: [{ + num: 1, + payload: uint8ArrayFromString('lol') + }, { + num: 2, + payload: uint8ArrayFromString('lol1') + }] + }) + + const b2 = Repeated.encode({ + list: [{ + num: 1, + payload: uint8ArrayFromString('lol') + }, { + num: 2, + payload: uint8ArrayFromString('lol1'), + meeeeh: 100 + }], + meeh: 42 + }) + + expect(b2).to.deep.equal(b1) + }) + + it('should decode repeated fields', () => { + const b1 = Repeated.encode({ + list: [{ + num: 1, + payload: uint8ArrayFromString('lol') + }, { + num: 2, + payload: uint8ArrayFromString('lol1') + }] + }) + + const o1 = Repeated.decode(b1) + + expect(o1).to.have.deep.nested.property('list', [{ + num: 1, + payload: uint8ArrayFromString('lol') + }, { + num: 2, + payload: uint8ArrayFromString('lol1') + }]) + + const b2 = Repeated.encode({ + list: [{ + num: 1, + payload: uint8ArrayFromString('lol') + }, { + num: 2, + payload: uint8ArrayFromString('lol1'), + meeeeh: 100 + }], + meeh: 42 + }) + + const o2 = Repeated.decode(b2) + + expect(o2).to.deep.equal(o1) + }) +}) diff --git a/test/strings.js b/test/strings.js deleted file mode 100644 index be1a5f9..0000000 --- a/test/strings.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../src') -var Strings = protobuf(require('./test.proto')).Strings - -tape('strings encode + decode', function (t) { - var b1 = Strings.encode({ - name: 'hello', - desc: 'world' - }) - - var o1 = Strings.decode(b1) - - t.same(o1, { - name: 'hello', - desc: 'world' - }) - - t.end() -}) - -tape('strings encode + decode + omitted', function (t) { - var b1 = Strings.encode({ - name: 'hello' - }) - - var o1 = Strings.decode(b1) - - t.same(o1.name, 'hello') - t.notOk(o1.hasDesc()) - - t.end() -}) - -tape('strings empty', function (t) { - var b1 = Strings.encode({ - name: '' - }) - - var o1 = Strings.decode(b1) - - t.same(o1.name, '') - t.notOk(o1.hasDesc()) - - t.end() -}) diff --git a/test/strings.spec.js b/test/strings.spec.js new file mode 100644 index 0000000..972317a --- /dev/null +++ b/test/strings.spec.js @@ -0,0 +1,45 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const Strings = protobuf(require('./test.proto')).Strings + +describe('strings', () => { + it('should encode and decode strings', () => { + const b1 = Strings.encode({ + name: 'hello', + desc: 'world' + }) + + const o1 = Strings.decode(b1) + + expect(o1).to.deep.equal({ + name: 'hello', + desc: 'world' + }) + }) + + it('should encode and decode optional strings', () => { + const b1 = Strings.encode({ + name: 'hello' + }) + + const o1 = Strings.decode(b1) + + expect(o1).to.have.property('name', 'hello') + expect(o1.hasDesc()).to.be.false() + }) + + it('should encode and decode empty strings', () => { + const b1 = Strings.encode({ + name: '' + }) + + const o1 = Strings.decode(b1) + + expect(o1).to.have.property('name', '') + expect(o1.hasDesc()).to.be.false() + }) +}) diff --git a/test/test.proto.js b/test/test.proto.js index 18e32e5..af59cec 100644 --- a/test/test.proto.js +++ b/test/test.proto.js @@ -33,6 +33,7 @@ message Integers { optional int32 int32 = 3; optional uint32 uint32 = 4; optional int64 int64 = 5; + optional fixed32 fixed32 = 6; } message Float { diff --git a/test/utf-8.js b/test/utf-8.js deleted file mode 100644 index 35a58c1..0000000 --- a/test/utf-8.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -var tape = require('tape') -var protobuf = require('../') -var UTF8 = protobuf(require('./test.proto')).UTF8 - -tape('strings can be utf-8', function (t) { - var ex = { - foo: 'ビッグデータ「人間の解釈が必要」「量の問題ではない」論と、もう一つのビッグデータ「人間の解釈が必要」「量の問題ではない」論と、もう一つの', - bar: 42 - } - var b1 = UTF8.encode(ex) - - t.same(UTF8.decode(b1), ex) - t.end() -}) diff --git a/test/utf-8.spec.js b/test/utf-8.spec.js new file mode 100644 index 0000000..de28354 --- /dev/null +++ b/test/utf-8.spec.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ + +'use strict' + +const { expect } = require('aegir/utils/chai') +const protobuf = require('../src') +const UTF8 = protobuf(require('./test.proto')).UTF8 + +describe('utf-8', () => { + it('should encode multi-byte strings', () => { + const ex = { + foo: 'ビッグデータ「人間の解釈が必要」「量の問題ではない」論と、もう一つのビッグデータ「人間の解釈が必要」「量の問題ではない」論と、もう一つの', + bar: 42 + } + + const b1 = UTF8.encode(ex) + const b2 = UTF8.decode(b1) + + expect(b2).to.deep.equal(ex) + }) +})