From d934624c487c106bb00616cef5752789b9d10f0d Mon Sep 17 00:00:00 2001 From: Kagami Hiiragi Date: Sat, 27 Dec 2014 14:38:58 +0300 Subject: [PATCH] var_int encode --- lib/index.js | 3 +++ lib/varint.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- test.js | 13 ++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/lib/index.js b/lib/index.js index c360e9d..db3c624 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,4 +5,7 @@ "use strict"; +// uint64_t implementation used for operations with int64. You may +// replace it with other library with the same API. +exports.Int64 = require("int64-native"); exports.Address = require("./address"); diff --git a/lib/varint.js b/lib/varint.js index c1a969d..9af832c 100644 --- a/lib/varint.js +++ b/lib/varint.js @@ -1,17 +1,19 @@ /** * Implement `var_int` encoding/decoding. + * Reference: * @module bitmessage/varint */ "use strict"; var assert = require("assert"); -// TODO(Kagami): Since `node-int64` and `int64-native` APIs are slightly -// differ, there might be need in platform-dependent wrapper. Also think -// that to do with 64bit arithmetic since `node-int64` doesn't implement -// it. -var Int64 = require("int64-native"); +var bitmessage = require("./"); +/** + * Decode var_int. + * @param {Buffer} buf - Buffer that starts with encoded var_int + * @return {var_int} Decoded var_int structure + */ exports.decode = function(buf) { assert(buf.length > 0, "Empty buffer"); var value, length; @@ -28,9 +30,9 @@ exports.decode = function(buf) { break; case 255: var hi = buf.readUInt32BE(1); + assert(hi !== 0, "Impractical var_int"); var lo = buf.readUInt32BE(5); - value = new Int64(hi, lo); - assert(value >= 4294967296, "Impractical var_int"); + value = new bitmessage.Int64(hi, lo); length = 9; break; default: @@ -40,3 +42,59 @@ exports.decode = function(buf) { var rest = buf.slice(length); return {value: value, length: length, rest: rest}; }; + +/** + * Encode number into var_int. + * @param {(number|Int64|Buffer)} value - Input number + * @return {Buffer} Encoded var_int + */ +exports.encode = function(value) { + var buf, buf64, targetStart; + if (typeof value === "number") { + assert(value >= 0, "Value cannot be less than zero"); + if (value < 253) { + buf = new Buffer([value]); + } else if (value < 65536) { + buf = new Buffer(3); + buf[0] = 253; + buf.writeUInt16BE(value, 1); + } else if (value < 4294967296) { + buf = new Buffer(5); + buf[0] = 254; + buf.writeUInt32BE(value, 1); + } else { + // Value may be inaccurate (max safe int = 2^53) but we will still + // try to convert it. + buf = new Buffer(9); + buf[0] = 255; + buf.writeUInt32BE(Math.floor(value / 4294967296), 1); // high32 + buf.writeUInt32BE(value % 4294967296, 5); // low32 + } + } else { + if (value.high32 && value.low32) { + // int64-native instance. + buf = new Buffer(9); + buf[0] = 255; + buf.writeUInt32BE(value.high32(), 1); + buf.writeUInt32BE(value.low32(), 5); + } else { + // Try to convert to buffer. + if (Buffer.isBuffer(value)) { + buf64 = value; + } else if (value.toBuffer) { + // node-int64 instance, rawBuffer = true (prevents extra buffer + // allocation since we copy it's value to new buffer anyway). + buf64 = value.toBuffer(true); + } else { + throw new Error("Value encode error"); + } + assert(buf64.length <= 8, "Buffer too big"); + buf = new Buffer(9); + buf.fill(0); + buf[0] = 255; + targetStart = 1 + (8 - buf64.length); + buf64.copy(buf, targetStart); + } + } + return buf; +}; diff --git a/test.js b/test.js index 0c89ff6..0c2ef41 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,6 @@ var expect = require("chai").expect; var bitmessage = require("./lib"); +var Int64 = bitmessage.Int64; var Address = bitmessage.Address; var varint = require("./lib/varint"); var bmcrypto = require("./lib/crypto"); @@ -36,6 +37,18 @@ describe("var_int", function() { expect(varint.decode.bind(null, Buffer("fe0000ffff", "hex"))).to.throw(Error); expect(varint.decode.bind(null, Buffer("ff00000000ffffffff", "hex"))).to.throw(Error); }); + + it("should encode", function() { + expect(varint.encode(123).toString("hex")).to.equal("7b"); + expect(varint.encode(0x1234).toString("hex")).to.equal("fd1234"); + expect(varint.encode(0x12345678).toString("hex")).to.equal("fe12345678"); + expect(varint.encode(0x1234567890).toString("hex")).to.equal("ff0000001234567890"); + expect(varint.encode(new Int64("0xffffffffffffffff")).toString("hex")).to.equal("ffffffffffffffffff"); + expect(varint.encode(Buffer("1234567890", "hex")).toString("hex")).to.equal("ff0000001234567890"); + expect(varint.encode.bind(null, -123)).to.throw(Error); + expect(varint.encode.bind(null, Buffer("123456789012345678", "hex"))).to.throw(Error); + expect(varint.encode.bind(null, "test")).to.throw(Error); + }); }); describe("Crypto", function() {