Skip to content

Commit

Permalink
feat(browser): switch from Buffer polyfill to ArrayBuffer (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
darrachequesne authored May 16, 2017
1 parent f2e1e9f commit 8d7ce87
Show file tree
Hide file tree
Showing 7 changed files with 645 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var decoded = notepack.decode(encoded); // { foo: 'bar' }

## Browser

A browser version of notepack is also available (29.5 kB minified)
A browser version of notepack is also available (7.6 kB minified)

```html
<script src="https://rawgit.com/darrachequesne/notepack/master/dist/notepack.js"></script>
Expand Down
276 changes: 276 additions & 0 deletions browser/decode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
'use strict';

function Decoder(buffer) {
this.offset = 0;
if (buffer instanceof ArrayBuffer) {
this.buffer = buffer;
} else if (ArrayBuffer.isView(buffer)) {
this.buffer = buffer.buffer;
} else {
throw new Error('Invalid argument');
}
this.view = new DataView(buffer);
}

function utf8Read(view, offset, length) {
var string = '';
for (var i = offset, end = offset + length; i < end; i++) {
var byte = view.getUint8(i);
if ((byte & 0x80) === 0x00) {
string += String.fromCharCode(byte);
continue;
}
if ((byte & 0xe0) === 0xc0) {
string += String.fromCharCode(
((byte & 0x0f) << 6) |
(view.getUint8(++i) & 0x3f)
);
continue;
}
if ((byte & 0xf0) === 0xe0) {
string += String.fromCharCode(
((byte & 0x0f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0)
);
continue;
}
if ((byte & 0xf8) === 0xf0) {
string += String.fromCharCode(
((byte & 0x07) << 18) |
((view.getUint8(++i) & 0x3f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0)
);
continue;
}
throw new Error('Invalid byte ' + byte.toString(16));
}
return string;
}

Decoder.prototype.array = function (length) {
var value = new Array(length);
for (var i = 0; i < length; i++) {
value[i] = this.parse();
}
return value;
};

Decoder.prototype.map = function (length) {
var key = '', value = {};
for (var i = 0; i < length; i++) {
key = this.parse();
value[key] = this.parse();
}
return value;
};

Decoder.prototype.str = function (length) {
var value = utf8Read(this.view, this.offset, length);
this.offset += length;
return value;
};

Decoder.prototype.bin = function (length) {
var value = this.buffer.slice(this.offset, this.offset + length);
this.offset += length;
return value;
};

Decoder.prototype.parse = function () {
var prefix = this.view.getUint8(this.offset++);
var value, length = 0, type = 0, hi = 0, lo = 0;

if (prefix < 0xc0) {
// positive fixint
if (prefix < 0x80) {
return prefix;
}
// fixmap
if (prefix < 0x90) {
return this.map(prefix & 0x0f);
}
// fixarray
if (prefix < 0xa0) {
return this.array(prefix & 0x0f);
}
// fixstr
return this.str(prefix & 0x1f);
}

// negative fixint
if (prefix > 0xdf) {
return (0xff - prefix + 1) * -1;
}

switch (prefix) {
// nil
case 0xc0:
return null;
// false
case 0xc2:
return false;
// true
case 0xc3:
return true;

// bin
case 0xc4:
length = this.view.getUint8(this.offset);
this.offset += 1;
return this.bin(length);
case 0xc5:
length = this.view.getUint16(this.offset);
this.offset += 2;
return this.bin(length);
case 0xc6:
length = this.view.getUint32(this.offset);
this.offset += 4;
return this.bin(length);

// ext
case 0xc7:
length = this.view.getUint8(this.offset);
type = this.view.getInt8(this.offset + 1);
this.offset += 2;
return [type, this.bin(length)];
case 0xc8:
length = this.view.getUint16(this.offset);
type = this.view.getInt8(this.offset + 2);
this.offset += 3;
return [type, this.bin(length)];
case 0xc9:
length = this.view.getUint32(this.offset);
type = this.view.getInt8(this.offset + 4);
this.offset += 5;
return [type, this.bin(length)];

// float
case 0xca:
value = this.view.getFloat32(this.offset);
this.offset += 4;
return value;
case 0xcb:
value = this.view.getFloat64(this.offset);
this.offset += 8;
return value;

// uint
case 0xcc:
value = this.view.getUint8(this.offset);
this.offset += 1;
return value;
case 0xcd:
value = this.view.getUint16(this.offset);
this.offset += 2;
return value;
case 0xce:
value = this.view.getUint32(this.offset);
this.offset += 4;
return value;
case 0xcf:
hi = this.view.getUint32(this.offset) * Math.pow(2, 32);
lo = this.view.getUint32(this.offset + 4);
this.offset += 8;
return hi + lo;

// int
case 0xd0:
value = this.view.getInt8(this.offset);
this.offset += 1;
return value;
case 0xd1:
value = this.view.getInt16(this.offset);
this.offset += 2;
return value;
case 0xd2:
value = this.view.getInt32(this.offset);
this.offset += 4;
return value;
case 0xd3:
hi = this.view.getInt32(this.offset) * Math.pow(2, 32);
lo = this.view.getUint32(this.offset + 4);
this.offset += 8;
return hi + lo;

// fixext
case 0xd4:
type = this.view.getInt8(this.offset);
this.offset += 1;
if (type === 0x00) {
this.offset += 1;
return void 0;
}
return [type, this.bin(1)];
case 0xd5:
type = this.view.getInt8(this.offset);
this.offset += 1;
return [type, this.bin(2)];
case 0xd6:
type = this.view.getInt8(this.offset);
this.offset += 1;
return [type, this.bin(4)];
case 0xd7:
type = this.view.getInt8(this.offset);
this.offset += 1;
if (type === 0x00) {
hi = this.view.getInt32(this.offset) * Math.pow(2, 32);
lo = this.view.getUint32(this.offset + 4);
this.offset += 8;
return new Date(hi + lo);
}
return [type, this.bin(8)];
case 0xd8:
type = this.view.getInt8(this.offset);
this.offset += 1;
return [type, this.bin(16)];

// str
case 0xd9:
length = this.view.getUint8(this.offset);
this.offset += 1;
return this.str(length);
case 0xda:
length = this.view.getUint16(this.offset);
this.offset += 2;
return this.str(length);
case 0xdb:
length = this.view.getUint32(this.offset);
this.offset += 4;
return this.str(length);

// array
case 0xdc:
length = this.view.getUint16(this.offset);
this.offset += 2;
return this.array(length);
case 0xdd:
length = this.view.getUint32(this.offset);
this.offset += 4;
return this.array(length);

// map
case 0xde:
length = this.view.getUint16(this.offset);
this.offset += 2;
return this.map(length);
case 0xdf:
length = this.view.getUint32(this.offset);
this.offset += 4;
return this.map(length);
}

throw new Error('Could not parse');
};

function decode(buffer) {
var decoder = new Decoder(buffer);
var value = decoder.parse();
if (decoder.offset !== buffer.byteLength) {
throw new Error((buffer.byteLength - decoder.offset) + ' trailing bytes');
}
return value;
}

module.exports = decode;
Loading

0 comments on commit 8d7ce87

Please sign in to comment.