Comparing version 1.1.0 to 1.2.0
@@ -201,2 +201,99 @@ 'use strict'; | ||
alphabet.ipv6 = {}; | ||
alphabet.ipv6.enc = { | ||
0: '0', | ||
1: '1', | ||
2: '2', | ||
3: '3', | ||
4: '4', | ||
5: '5', | ||
6: '6', | ||
7: '7', | ||
8: '8', | ||
9: '9', | ||
10: 'A', | ||
11: 'B', | ||
12: 'C', | ||
13: 'D', | ||
14: 'E', | ||
15: 'F', | ||
16: 'G', | ||
17: 'H', | ||
18: 'I', | ||
19: 'J', | ||
20: 'K', | ||
21: 'L', | ||
22: 'M', | ||
23: 'N', | ||
24: 'O', | ||
25: 'P', | ||
26: 'Q', | ||
27: 'R', | ||
28: 'S', | ||
29: 'T', | ||
30: 'U', | ||
31: 'V', | ||
32: 'W', | ||
33: 'X', | ||
34: 'Y', | ||
35: 'Z', | ||
36: 'a', | ||
37: 'b', | ||
38: 'c', | ||
39: 'd', | ||
40: 'e', | ||
41: 'f', | ||
42: 'g', | ||
43: 'h', | ||
44: 'i', | ||
45: 'j', | ||
46: 'k', | ||
47: 'l', | ||
48: 'm', | ||
49: 'n', | ||
50: 'o', | ||
51: 'p', | ||
52: 'q', | ||
53: 'r', | ||
54: 's', | ||
55: 't', | ||
56: 'u', | ||
57: 'v', | ||
58: 'w', | ||
59: 'x', | ||
60: 'y', | ||
61: 'z', | ||
62: '!', | ||
63: '#', | ||
64: '$', | ||
65: '%', | ||
66: '&', | ||
67: '(', | ||
68: ')', | ||
69: '*', | ||
70: '+', | ||
71: '-', | ||
72: ';', | ||
73: '<', | ||
74: '=', | ||
75: '>', | ||
76: '?', | ||
77: '@', | ||
78: '^', | ||
79: '_', | ||
80: '`', | ||
81: '{', | ||
82: '|', | ||
83: '}', | ||
84: '~' | ||
}; | ||
alphabet.ipv6.dec = _.object( | ||
_.map(_.values(alphabet.ipv6.enc), function(v) { /* The keys */ | ||
return v.charCodeAt(0); | ||
}), | ||
_.map(_.keys(alphabet.ipv6.enc), function(v) { /* The values */ | ||
return parseInt(v); | ||
}) | ||
); | ||
module.exports = alphabet; |
'use strict'; | ||
var alphabets = require('./alphabets'); | ||
var v6 = require('ipv6').v6; | ||
var bignum = require('bignum'); | ||
@@ -26,2 +28,69 @@ var NUM_MAXVALUE = Math.pow(2, 32) - 1; | ||
/* Function borrowed from noseglid/canumb (github) */ | ||
function pad(width, number) | ||
{ | ||
return new Array(1 + width - number.length).join('0') + number; | ||
} | ||
function encodeBignumIPv6(num) | ||
{ | ||
var enctable = alphabets.ipv6.enc; | ||
var enc = []; | ||
for (var i = 1; i < 20; ++i) { | ||
enc.push(enctable[num.mod(85).toNumber()]); /* Ranges between 0 - 84 */ | ||
num = num.div(85); | ||
} | ||
enc.push(enctable[num.toNumber()]); /* What's left is also in range 0 - 84 */ | ||
return enc.reverse().join(''); | ||
} | ||
function encodeBufferIPv6(buffer) | ||
{ | ||
if (16 !== buffer.length) { | ||
/* An IPv6 address must be exactly 16 bytes, 128 bits long */ | ||
return false; | ||
} | ||
return encodeBignumIPv6(bignum.fromBuffer(buffer)); | ||
} | ||
function encodeStringIPv6(string) | ||
{ | ||
var addr = new v6.Address(string); | ||
if (!addr.isValid()) { | ||
return false; | ||
} | ||
var num = bignum(addr.parsedAddress.map(function(el) { | ||
return pad(4, el); | ||
}).join(''), 16); | ||
return encodeBignumIPv6(num); | ||
} | ||
function decodeStringIPv6(string) | ||
{ | ||
if (20 !== string.length) { | ||
/* An encoded IPv6 is always (5/4) * 16 = 20 bytes */ | ||
return false; | ||
} | ||
var dectable = alphabets.ipv6.dec; | ||
var i = 0; | ||
var binary = string.split('').reduceRight(function(memo, el) { | ||
var num = bignum(dectable[el.charCodeAt(0)]); | ||
var fact = bignum(85).pow(i++); | ||
var contrib = num.mul(fact); | ||
return memo.add(contrib); | ||
}, bignum(0)); | ||
return v6.Address.fromBigInteger(binary).correctForm(); | ||
} | ||
function decodeBufferIPv6(buffer) | ||
{ | ||
return decodeStringIPv6(buffer.toString()); | ||
} | ||
function encodeBuffer(buffer, encoding) | ||
@@ -131,3 +200,3 @@ { | ||
encoding = encoding || DEFAULT_ENCODING; | ||
if (-1 === [ 'ascii85', 'z85' ].indexOf(encoding)) { | ||
if (-1 === [ 'ascii85', 'z85', 'ipv6' ].indexOf(encoding)) { | ||
return false; | ||
@@ -137,7 +206,7 @@ } | ||
if (data instanceof Buffer) { | ||
return encodeBuffer(data, encoding); | ||
return ('ipv6' === encoding) ? encodeBufferIPv6(data) : encodeBuffer(data, encoding); | ||
} | ||
if (typeof data === 'string') { | ||
return encodeString(data, encoding); | ||
return ('ipv6' === encoding) ? encodeStringIPv6(data) : encodeString(data, encoding); | ||
} | ||
@@ -150,3 +219,3 @@ | ||
encoding = encoding || DEFAULT_ENCODING; | ||
if (-1 === [ 'ascii85', 'z85' ].indexOf(encoding)) { | ||
if (-1 === [ 'ascii85', 'z85', 'ipv6' ].indexOf(encoding)) { | ||
return false; | ||
@@ -156,7 +225,7 @@ } | ||
if (data instanceof Buffer) { | ||
return decodeBuffer(data, encoding); | ||
return ('ipv6' === encoding) ? decodeBufferIPv6(data) : decodeBuffer(data, encoding); | ||
} | ||
if (typeof data === 'string') { | ||
return decodeString(data, encoding); | ||
return ('ipv6' === encoding) ? decodeStringIPv6(data) : decodeString(data, encoding); | ||
} | ||
@@ -163,0 +232,0 @@ |
{ | ||
"name": "base85", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Base85 (Ascii85) encode and decode functionality", | ||
@@ -9,2 +9,6 @@ "main": "lib/base85.js", | ||
}, | ||
"dependencies": { | ||
"bignum": "0.6.x", | ||
"ipv6": "3.1.x" | ||
}, | ||
"devDependencies": { | ||
@@ -11,0 +15,0 @@ "nodeunit": "0.8.x", |
133
README.md
@@ -5,10 +5,15 @@ [![Build Status](https://travis-ci.org/noseglid/base85.png?branch=master)](https://travis-ci.org/noseglid/base85) | ||
Simple utility to manage base85. Where [base64 adds approximately 1/3][Base64], | ||
[base85 only adds about 1/4][Base85]. Of course there's a tradeoff. The Base85 | ||
alphabet includes characters that might not be as friendly as the base64 alphabet. | ||
While it's still only printable characters, the [Ascii85][Base85] specification contains | ||
quotes (`'` and `"`) which needs escaping in many programming languages, | ||
and the [ZeroMQ][Base85ZeroMQ] specification contains `<` and `>` which need escaping | ||
in most (all?) [SGML][SGML] languages. | ||
Base85 encoder/decoder written in native javascript. | ||
Where base64 [adds approximately 1/3][Base64], base85 only [adds about | ||
1/4][Base85]. Of course there's a tradeoff. The Base85 alphabet includes | ||
characters that might not be as friendly as the base64 alphabet. While it's | ||
still only printable characters, the [Ascii85][Base85] specification contains | ||
quotes (`'` and `"`) which needs escaping in many programming languages, and | ||
the [ZeroMQ][Base85ZeroMQ] specification contains `<` and `>` which need | ||
escaping in most (all?) [SGML][SGML] languages. | ||
IPv6 encoding should only be used for encoding IPv6 addresses. When using IPv6, | ||
input for encoding must always be 16 bytes, and input for decoding must always be 20 bytes. | ||
Supported encoding specifications | ||
@@ -18,2 +23,3 @@ | ||
* [ZeroMQ][Base85ZeroMQ] | ||
* [IPv6][Base85IPv6] | ||
@@ -27,61 +33,107 @@ ## Installation | ||
For encoding: | ||
### Encoding: | ||
var base85 = require('base85'); | ||
console.log(base85.encode('Hello, world!')); // 'nm=QNz.92Pz/PV8aP' | ||
console.log(base85.encode('Hello, world!', 'ascii85')); // ' <~87cURD_*#TDfTZ)+T~>' | ||
For decoding: | ||
var z85 = base85.encode('Hello, world!'); | ||
console.log(z85); // nm=QNz.92Pz/PV8aP | ||
### Decoding: | ||
var base85 = require('base85'); | ||
var decoded = base85.decode('vqG:5Cw?IqayPd#az#9uAbn%daz>L5wPF#evpK6}vix96y?$k6z*q'); | ||
console.log(decoded.toString('utf8')); // 'all work and no play makes jack a dull boy' | ||
console.log(decoded.toString('utf8')); // all work and no play makes jack a dull boy | ||
## Bugs | ||
### IPv6 ([RFC1924][Base85IPv6]), can take [any correctly formatted IPv6 address](http://en.wikipedia.org/wiki/IPv6): | ||
Doesn't support the z-abbreviation for [Ascii85][Base85]. This means that data encoded with this | ||
support will cause the library to return false. An all-zero input buffer will be encoded | ||
as `<~!!!!!~>`, rather than `<~z~>` | ||
var base85 = require('base85'); | ||
Doesn't support [IPv6 encoding specification (RFC1924)][Base85IPv6] for now. This baby requires | ||
requires 128-bit arithmetic, which is rather problematic. I'm thrilled to see that the author | ||
of the RFC took this in consideration, specifically - quote from the [RFC][Base85IPv6]: "This is not | ||
considered a serious drawback in the representation, but a flaw of the processor designs." | ||
Silly processor designers. | ||
var ipv6 = base85.encode('2001:db8:100:f101::1', 'ipv6'); | ||
console.log(ipv6); // 9R}vSQZ1W=8fRv3*HAqn | ||
var decoded = base85.decode('9R}vSQZ1W=8fRv3*HAqn', 'ipv6'); | ||
console.log(decoded); // 2001:db8:100:f101::1 | ||
## API | ||
### `encode(data[, encoding])` | ||
### `encode(data [, encoding])` | ||
> _Encodes the specified data. If encoding is `ascii85`, the encoded data will be prepended | ||
> with `<~` and appended with `~>`._ | ||
> Encodes the specified data. If encoding is `ascii85`, the encoded data will be prepended | ||
> with `<~` and appended with `~>`. | ||
> | ||
> **data** The data to encode, may be a `String` or a [Buffer][NodeBuffer]. | ||
> **data** | ||
>> The data to encode, may be a `String` or a [Buffer][NodeBuffer]. | ||
> | ||
> **encoding** Which specification to use when encoding `data`. May be either | ||
> `ascii85` ([Adobe][Base85]) or `z85` ([ZeroMQ][Base85ZeroMQ]). | ||
> Default is `z85`. | ||
> **encoding** | ||
>> Which specification to use when encoding `data`. Valid values are: | ||
>> `ascii85`, `z85` or `ipv6`. Default is `z85`. | ||
>> | ||
>> For `ipv6`, if `data` is a buffer, it is expected to be the binary representation | ||
>> of an IPv6 address (16 bytes). It **cannot** be a textual representation. If it is a string, | ||
>> it can be on any valid IPv6 form (e.g. `::1` or `1080:0:0:0:8:800:200c:417a`, | ||
>> parsing is done using [javascript-ipv6][JavaScriptIPv6]). | ||
> | ||
> **returns** A `String` with the encoded data. | ||
> **returns** | ||
>> A `String` with the encoded data. | ||
### `decode(data[, encoding])` | ||
### `decode(data [, encoding])` | ||
> _Decodes the specified data. If encoding is `ascii85`, the data is expected | ||
> Decodes the specified data. If encoding is `ascii85`, the data is expected | ||
> to start with `<~` and and end with `~>`. No checks are actually made for | ||
> this, but output will be unexpected if this is not the case._ | ||
> this, but output will be unexpected if this is not the case. If encoding is | ||
> `ipv6`, the length of data must be exactly 20 bytes. `ipv6` encoding cannot | ||
> be used with arbitrary data. | ||
> | ||
> _A buffer is always returned as data may not be representable in a string. | ||
> A buffer is always returned as data may not be representable in a string. | ||
> If you know it is, you can easily convert it to a string using the | ||
> [Buffer.toString()][NodeBufferToString] utility._ | ||
> [Buffer.toString()][NodeBufferToString] utility. | ||
> | ||
> **data** The data to decode. May be a `String` or a [Buffer][NodeBuffer]. | ||
> Expected to be enclosed in `<~` and `~>`. | ||
> **data** | ||
>> The data to decode. May be a `String` or a [Buffer][NodeBuffer]. | ||
>> Expected to be enclosed in `<~` and `~>`. | ||
> | ||
> **encoding** Which specification `data` is encoded with. May be either | ||
> `ascii85` ([Adobe][Base85]) or `z85` ([ZeroMQ][Base85ZeroMQ]). | ||
> Default is `z85`. | ||
> **encoding** | ||
>> Which specification `data` is encoded with. Valid values are: | ||
>> `ascii85`, `z85` or `ipv6`. Default is `z85`. | ||
> | ||
> **returns** A [Buffer][NodeBuffer] With the decoded data, or **boolean** `false` if the buffer could not be decoded. When testing if the result succeeded, [always use operators with 3 characters][JSCompare] ('===' or '!=='). | ||
> **returns** | ||
>> A [Buffer][NodeBuffer] With the decoded data, or **boolean** `false` | ||
>> if the buffer could not be decoded. When testing if the result succeeded, | ||
>> [always use operators with 3 characters][JSCompare] ('===' or '!=='). | ||
> | ||
## Which specification to use? | ||
ZeroMQ appears to be a better specification for most applications. It doesn't | ||
include quotes in its alphabet which makes it useful in many quoted languages | ||
(such as C, C++, JavaScript, Java, Python, Perl, Ruby... the list goes on). | ||
Neither does it add the 4 extra enclosing bytes Ascii85 does. There may, | ||
however, be some problems using it in SGML and its derivatives since | ||
both less-than `<` and greater-than `>` are part of the alphabet. But | ||
then again, Ascii85 has that as well. | ||
Ascii85 appears to be the most used of the base85 specifications however. As for why | ||
completely eludes me. This may very well be the only reason to pick Ascii85. | ||
If you control both decoding and encoding side, use ZeroMQ. | ||
If you need interoperability with Ascii85, use that. | ||
As IPv6 encoding only supports exactly 128 bits (16 bytes), this is not very useful for | ||
arbitrary data. Only use IPv6 if you're actually encoding IPv6 addresses. | ||
## Bugs | ||
Doesn't support the z-abbreviation for [Ascii85][Base85]. This means that data encoded with this | ||
support will cause the library to return false. An all-zero input buffer will be encoded | ||
as `<~!!!!!~>`, rather than `<~z~>` | ||
[IPv6 encoding specification (RFC1924)][Base85IPv6] requires 128-bit arithmetic, | ||
which is rather problematic. I'm thrilled to see that the author of the RFC took this | ||
in consideration, specifically - quote from the [RFC][Base85IPv6]: "This is not | ||
considered a serious drawback in the representation, but a flaw of the processor designs." | ||
Silly processor designers. Currently, this is implemented using an arbitrary precision algorithm, | ||
it's slow but it does the job. Now let's poke those processor designers for 128-bit processors. | ||
[Base64]: http://en.wikipedia.org/wiki/Base64 | ||
@@ -95,1 +147,2 @@ [Base85]: http://en.wikipedia.org/wiki/Ascii85 | ||
[SGML]: https://en.wikipedia.org/wiki/Standard_Generalized_Markup_Language | ||
[JavaScriptIPv6]: https://github.com/beaugunderson/javascript-ipv6 |
20596
8
473
145
2
+ Addedbignum@0.6.x
+ Addedipv6@3.1.x
+ Added@isaacs/cliui@8.0.2(transitive)
+ Addedansi-regex@5.0.16.1.0(transitive)
+ Addedansi-styles@4.3.06.2.1(transitive)
+ Addedasync@0.2.10(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbignum@0.6.2(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedcli@0.4.5(transitive)
+ Addedcliff@0.1.10(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcolors@0.6.21.0.3(transitive)
+ Addedcross-spawn@7.0.6(transitive)
+ Addedcycle@1.0.3(transitive)
+ Addedeastasianwidth@0.2.0(transitive)
+ Addedemoji-regex@8.0.09.2.2(transitive)
+ Addedeyes@0.1.8(transitive)
+ Addedforeground-child@3.3.0(transitive)
+ Addedglob@11.0.1(transitive)
+ Addedipv6@3.1.3(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedisstream@0.1.2(transitive)
+ Addedjackspeak@4.0.3(transitive)
+ Addedlru-cache@11.0.2(transitive)
+ Addedminimatch@10.0.1(transitive)
+ Addedminipass@7.1.2(transitive)
+ Addedpackage-json-from-dist@1.0.1(transitive)
+ Addedpath-key@3.1.1(transitive)
+ Addedpath-scurry@2.0.0(transitive)
+ Addedpkginfo@0.3.1(transitive)
+ Addedshebang-command@2.0.0(transitive)
+ Addedshebang-regex@3.0.0(transitive)
+ Addedsignal-exit@4.1.0(transitive)
+ Addedsprintf@0.1.5(transitive)
+ Addedstack-trace@0.0.10(transitive)
+ Addedstring-width@4.2.35.1.2(transitive)
+ Addedstrip-ansi@6.0.17.1.0(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedwinston@0.8.3(transitive)
+ Addedwrap-ansi@7.0.08.1.0(transitive)