From 5e3ad3d09d6c0cef60520adc17af2a1c5fac8cb8 Mon Sep 17 00:00:00 2001
From: "Ryan X. Charles" <ryan@bitpay.com>
Date: Mon, 18 Aug 2014 18:04:47 -0700
Subject: [PATCH] BufferReader

---
 lib/bufferreader.js       |  90 +++++++++++++++++++
 test/test.bufferreader.js | 177 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 267 insertions(+)
 create mode 100644 lib/bufferreader.js
 create mode 100644 test/test.bufferreader.js

diff --git a/lib/bufferreader.js b/lib/bufferreader.js
new file mode 100644
index 0000000..fc06b17
--- /dev/null
+++ b/lib/bufferreader.js
@@ -0,0 +1,90 @@
+var BufferReader = function BufferReader(buf, pos) {
+  if (!(this instanceof BufferReader))
+    return new BufferReader(buf);
+  this.buf = buf;
+  this.pos = pos || 0;
+};
+
+BufferReader.prototype.eof = function eof() {
+  return this.pos >= this.buf.length;
+};
+
+BufferReader.prototype.read = function() {
+  var buf = this.buf.slice(this.pos);
+  this.pos = this.buf.length;
+  return buf;
+};
+
+BufferReader.prototype.readUInt8 = function() {
+  var val = this.buf.readUInt8(this.pos);
+  this.pos = this.pos + 1;
+  return val;
+};
+
+BufferReader.prototype.readUInt16BE = function() {
+  var val = this.buf.readUInt16BE(this.pos);
+  this.pos = this.pos + 2;
+  return val;
+};
+
+BufferReader.prototype.readUInt16LE = function() {
+  var val = this.buf.readUInt16LE(this.pos);
+  this.pos = this.pos + 2;
+  return val;
+};
+
+BufferReader.prototype.readUInt32BE = function() {
+  var val = this.buf.readUInt32BE(this.pos);
+  this.pos = this.pos + 4;
+  return val;
+};
+
+BufferReader.prototype.readUInt32LE = function() {
+  var val = this.buf.readUInt32LE(this.pos);
+  this.pos = this.pos + 4;
+  return val;
+};
+
+//TODO: What if n is so large that it loses precision?
+BufferReader.prototype.readUInt64BE = function() {
+  var val = 0;
+  for (var i = 0; i < 8; i++) {
+    val += Math.pow(256, i) * this.buf[this.pos + 8 - 1 - i];
+  }
+  this.pos = this.pos + 8;
+  return val;
+};
+
+//TODO: What if n is so large that it loses precision?
+BufferReader.prototype.readUInt64LE = function() {
+  var val = 0;
+  for (var i = 0; i < 8; i++) {
+    val += Math.pow(256, i) * this.buf[this.pos + i];
+  }
+  this.pos = this.pos + 8;
+  return val;
+};
+
+BufferReader.prototype.readVarInt = function() {
+  var first = this.readUInt8();
+  switch (first) {
+    case 0xFD:
+      return this.readUInt16LE();
+    case 0xFE:
+      return this.readUInt32LE();
+    case 0xFF:
+      return this.readUInt64LE();
+    default:
+      return first;
+  }
+};
+
+BufferReader.prototype.reverse = function() {
+  var buf = new Buffer(this.buf.length);
+  for (var i = 0; i < buf.length; i++)
+    buf[i] = this.buf[this.buf.length - 1 - i]
+  this.buf = buf;
+  return this;
+};
+
+module.exports = BufferReader;
diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js
new file mode 100644
index 0000000..bca7b63
--- /dev/null
+++ b/test/test.bufferreader.js
@@ -0,0 +1,177 @@
+var BufferReader = require('../lib/bufferreader');
+var should = require('chai').should();
+
+describe('BufferReader', function() {
+  
+  it('should make a new BufferReader', function() {
+    var br = new BufferReader();
+    should.exist(br);
+  });
+
+  describe('#eof', function() {
+
+    it('should return true for a blank br', function() {
+      var br = new BufferReader(new Buffer([]));
+      br.eof().should.equal(true);
+    });
+
+  });
+
+  describe('read', function() {
+    
+    it('should return the same buffer', function() {
+      var buf = new Buffer([0]);
+      var br = new BufferReader(buf);
+      br.read().toString('hex').should.equal(buf.toString('hex'));
+    });
+
+  });
+
+  describe('#readUInt8', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(1);
+      buf.writeUInt8(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt8().should.equal(1);
+    });
+
+  });
+
+  describe('#readUInt16BE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(2);
+      buf.writeUInt16BE(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt16BE().should.equal(1);
+    });
+
+  });
+
+  describe('#readUInt16LE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(2);
+      buf.writeUInt16LE(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt16LE().should.equal(1);
+    });
+
+  });
+
+  describe('#readUInt32BE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(4);
+      buf.writeUInt32BE(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt32BE().should.equal(1);
+    });
+
+  });
+
+  describe('#readUInt32LE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(4);
+      buf.writeUInt32LE(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt32LE().should.equal(1);
+    });
+
+  });
+
+  describe('#readUInt64BE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(8);
+      buf.fill(0);
+      buf.writeUInt32BE(1, 4);
+      var br = new BufferReader(buf);
+      br.readUInt64BE().should.equal(1);
+    });
+
+    it('should return 2^64', function() {
+      var buf = new Buffer(8);
+      buf.fill(0xff);
+      var br = new BufferReader(buf);
+      br.readUInt64BE().should.equal(Math.pow(2, 64));
+    });
+
+  });
+
+  describe('#readUInt64LE', function() {
+
+    it('should return 1', function() {
+      var buf = new Buffer(8);
+      buf.fill(0);
+      buf.writeUInt32LE(1, 0);
+      var br = new BufferReader(buf);
+      br.readUInt64LE().should.equal(1);
+    });
+
+    it('should return 2^30', function() {
+      var buf = new Buffer(8);
+      buf.fill(0);
+      buf.writeUInt32LE(Math.pow(2, 30), 0);
+      var br = new BufferReader(buf);
+      br.readUInt64LE().should.equal(Math.pow(2, 30));
+    });
+
+    it('should return 0', function() {
+      var buf = new Buffer(8);
+      buf.fill(0);
+      var br = new BufferReader(buf);
+      br.readUInt64LE().should.equal(0);
+    });
+
+    it('should return 2^64', function() {
+      var buf = new Buffer(8);
+      buf.fill(0xff);
+      var br = new BufferReader(buf);
+      br.readUInt64LE().should.equal(Math.pow(2, 64));
+    });
+
+  });
+
+  describe('#readVarInt', function() {
+
+    it('should read a 1 byte varint', function() {
+      var buf = new Buffer([50]);
+      var br = new BufferReader(buf);
+      br.readVarInt().should.equal(50);
+    });
+
+    it('should read a 3 byte varint', function() {
+      var buf = new Buffer([253, 253, 0]);
+      var br = new BufferReader(buf);
+      br.readVarInt().should.equal(253);
+    });
+
+    it('should read a 5 byte varint', function() {
+      var buf = new Buffer([254, 0, 0, 0, 0]);
+      buf.writeUInt32LE(50000, 1);
+      var br = new BufferReader(buf);
+      br.readVarInt().should.equal(50000);
+    });
+
+    it('should read a 9 byte varint', function() {
+      var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]);
+      var br = new BufferReader(buf);
+      br.readVarInt().should.equal(Math.pow(2, 64));
+    });
+
+  });
+
+  describe('#reverse', function() {
+    
+    it('should reverse this [0, 1]', function() {
+      var buf = new Buffer([0, 1]);
+      var br = new BufferReader(buf);
+      br.reverse().read().toString('hex').should.equal('0100');
+    });
+
+  });
+
+});