v1.0.0: common encodings for browsers and node.js

This commit is contained in:
AJ ONeal 2019-10-08 03:58:46 -06:00
parent e0302b9b3f
commit 96fcbdbcad
15 changed files with 1195 additions and 2 deletions

368
README.md
View File

@ -1,3 +1,367 @@
# omnibuffer.js
# @root/encoding
Leightweight, Zero-dependency, translation between Unicode, Buffers, Base64, Hex, Binary Strings, UCS-2, UTF-8, etc.
Lightweight, Zero-dependency, translation between Unicode Strings, Binary Strings, Buffers, Base64, Hex, UCS-2, UTF-8, etc.
Works identically on all platforms:
- [x] Web Browsers
- Chrome
- Firefox
- Microsoft Edge
- Internet Explorer
- [x] Node.js
# Usage
```js
var Enc = require('@root/encoding');
Enc.strToBuf('Hello, 世界!');
```
# Use cases
Typically you want to use this in a browser when you need to convert user input to some sort
of Byte Array for hashing or encoding in an ancient format.
For example:
- [x] Hashing passwords
- [x] Secure Remote Password
- [x] JWT and JWS signing and verifying
- [x] ASN1 parsing and packing
- [x] DER
- [x] x509
- [x] CSR
- [x] PEM
The purpose of this library is to make it easy to support common string and buffer encoding and decoding
in both Browsers and node with minimal code.
# Examples
Strings and Byte Arrays
```js
var Enc = require('@root/encoding/bytes');
Enc.binToStr(bin);
Enc.binToBuf(bin);
Enc.bufToBin(buf);
Enc.bufToStr(buf);
Enc.strToBin(str);
Enc.strToBuf(str);
```
Hex
```js
var Enc = require('@root/encoding/hex');
Enc.hexToBuf(hex);
Enc.hexToStr(hex);
Enc.bufToHex(buf);
Enc.strToHex(str);
```
Base64
```js
var Enc = require('@root/encoding/base64');
Enc.base64ToBuf(b64);
Enc.base64ToStr(b64);
Enc.bufToBase64(buf);
Enc.strToBase64(str);
```
URL Safe Base64
(all of `base64To*()` accept URL Safe Base64)
```js
var Enc = require('@root/encoding/base64');
Enc.base64ToUrlBase64(b64);
Enc.urlBase64ToBase64(u64);
Enc.bufToUrlBase64(buf);
Enc.strToUrlBase64(str);
```
Base64 and Hex
```
require('@root/encoding/base64');
require('@root/encoding/hex');
var Enc = require('@root/encoding');
Enc.hexToBase64(hex);
Enc.base64ToHex(b64);
```
# Browser API
(the Node API signatures are the same, but implemented with `Buffer`)
Conversions between these formats are supported:
- Strings and Buffers
- Hex
- Base64
## Strings and Buffers
JavaScript has two types of strings:
- _Binary Strings_, which we call `bin`
- _Unicode Strings_, which we call `str` (USC-2, essentially UTF-16)
- treated as UTF-8 for the purposes of `encodeURIComponent`
JavaScript has two (and a half) ways to support Byte Arrays:
- `Array`, which we call `arr`
- `Uint8Array`, which we call `buf` (of the `ArrayBuffer` family)
- `Buffer` (node-only, but implemented as `Uint8Array`)
The API for the conversions is centered around `Uint8Array` (`Buffer`) but,
for browser compatibility, sometimes requires the use of _Binary Strings_.
**API**
We provide conversions directly to each of the following:
| Name | Type | Description |
| :---- | :------------- | :-------------------------------------------- |
| `str` | Unicode String | Handled by `split('')` as two-byte characters |
| `bin` | Binary String | Handled by `split('')` as single-byte chars |
| `buf` | Byte Array | Truncated to single-byte chars |
The names and signatures of the functions are as follows:
To Buffer
- Binary String to Buffer
- binToBuf(bin)
- Unicode String (UTF-8) to Buffer
- strToBuf(str)
To Unicode String
- Binary String to Unicode String (UTF-8)
- binToStr(bin)
- Buffer to Unicode String (UTF-8)
- bufToStr(buf)
To Binary String
- Buffer to Binary String
- bufToBin(buf)
- Unicode String to Binary String
- strToBin(str)
It's very easy to convert from Binary Strings to Byte Arrays (`Uint8Array.from(bin.split(''))`)
and from `Uint8Array` to Binary String (`Array.from(buf).join('')`).
The real value is converting between Unicode Strings to (UTF-8) Binary Strings, and back:
```js
function toBin(str) {
var escstr = encodeURIComponent(str);
return escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
}
```
```js
function toStr(bin) {
var escstr = bin.replace(/(.)/g, function(m, p) {
var code = p
.charCodeAt(0)
.toString(16)
.toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
});
return decodeURIComponent(escstr);
}
```
## Hex
JavaScript does not have a native way to create hex, aside from small numbers:
```js
(12345).toString(16);
```
The hex functions convert to and from hexidecimal:
| Name | Type | Description |
| :---- | :--------- | :--------------------------------------------- |
| `hex` | Hex String | Handled by `split('')` as half-byte characters |
To Hex
- Binary String to Hex
- Enc.bufToHex(Enc.binToBuf(bin))
- Byte Array to Hex
- bufToHex
- Unicode String (UTF-8) to Hex
- strToHex
From Hex
- Hex to Binary String
- Enc.hexToBuf(Enc.bufToBin(hex))
- Hex to Byte Array
- hexToBuf
- Hex to Unicode String (UTF-8)
- hexToStr
However, assuming you have a single-byte string, it's very easy to convert back and forth:
```js
function toHex(any) {
var hex = [];
var i, h;
var len = any.byteLength || any.length;
for (i = 0; i < len; i += 1) {
h = any[i].toString(16);
if (h.length % 2) {
h = '0' + h;
}
hex.push(h);
}
return hex.join('').toLowerCase();
}
```
```js
function fromHex(hex) {
var arr = hex.match(/.{2}/g).map(function(h) {
return parseInt(h, 16);
});
return Uint8Array.from(arr);
}
```
## Base64
Browser JavaScript _does_ have a native way convert between Binary Strings and Base64:
```js
var b64 = btoa('An ASCII string is a Binary String');
// Note: A Unicode String is NOT
```
```js
var bin = atob('SGVsbG8sIOS4lueVjCE=');
```
However, it does **not** have a native way to convert between Unicode Strings and Binary Strings,
nor to and from URL Safe Base64.
The base64 module provides simpler conversions to and from Base 64 and URL Safe Base64:
| Name | Type | Description |
| :---- | :-------------- | :-------------------------------------------------------- |
| `b64` | Base64 | Standard Base64 as handled by `btoa` and `atob` |
| `u64` | URL Safe Base64 | Replaces `+` with `-` and `/` with `_`, and omits padding |
To Base64
- Unicode String (UTF-8) to Base64
- strToBase64(str)
- Binary String to Base64
- Enc.bufToBase64(Enc.binToBuf(bin))
- Byte Array to Base64
- bufToBase64(buf)
From Base64 (and URL Safe Base64)
- Base64 to Unicode String (UTF-8)
- base64ToStr(b64)
- Base64 to Binary String
- Enc.bufToBin(Enc.base64ToBuf(b64)))
- Base64 to Byte Array
- base64ToBuf(b64)
To URL Safe Base64
- Base64 to URL Safe Base64
- base64ToUrlBase64(b64);
- URL Safe Base64 to Base64
- urlBase64ToBase64(u64);
- Binary String to URL Safe Base64
- Enc.bufToUrlBase64(Enc.binToBuf(bin));
- Byte Array to URL Safe Base64
- bufToUrlBase64(buf);
- Unicode String (UTF-8) to URL Safe Base64
- strToUrlBase64(str);
# FAQ
## Why yet another encoding library?
We write code that works both in node and in browsers,
and we like to keep it small, light, and focused.
By using browser native functions rather than 're-inventing the wheel'
## Why not 'browserified' Buffer?
The most common 'browserified' `Buffer` implementations are quite large -
either because they don't use browser-native code or they guarantee perfect
compatibility with node's `Buffer`, which isn't necessary for most people.
On the other hand, Browsers have already been able to translate between
Base64, UTF-8, Binary Strings, and Byte Arrays (Buffers) all the way back
since _before_ IE6!
Using these browser-native methods eliminates hundreds of lines of code:
- `btoa` Binary String to Base64 (ASCII)
- `atob` Base64 (ASCII) to Binary String
- `encodeURIComponent` Unicode String to Hex-Escaped String
- `decodeURIComponent` Hex-Escaped String to Unicode String
- `String.prototype.charCodeAt` ASCII to Byte
- `String.fromCharCode` Byte to ASCII
The code is typically also much easier to read. In many cases the conversion is only one line long.
Since a node `Buffer` is actually an `ArrayBuffer`, node's `Buffer` really only has the advantage
of convenient conversions, so that's really all that needs to be implemented.
In the case of ancient browsers which do not support `Uint8Array`, the native `Array` is still
the best substitute.
## Why use this in node?
Unless you're writing code that's intended to work in the browser, you probably shouldn't -
Node's `Buffer` does the job quite well.
The one function you may still be interested in, which Node's `Buffer` omits, is this one:
```js
function toUrlSafeBase64(base64) {
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
```
HOWEVER, if you believe that browser users would benefit from your library, this is a much
better alternative for simple use cases where you're dealing with small bits of code.

59
base64.js Normal file
View File

@ -0,0 +1,59 @@
'use strict';
var Enc = require('./bytes.js');
// to Base64
function bufToBase64(buf) {
// we want to maintain api compatability with browser APIs,
// so we assume that this could be a Uint8Array
return Buffer.from(buf).toString('base64');
}
Enc.bufToBase64 = bufToBase64;
Enc.strToBase64 = function(str) {
return Buffer.from(str).toString('base64');
};
// from Base64
function base64ToBuf(b64) {
// node handles URL Safe Base64 automatically
return Buffer.from(b64, 'base64');
}
Enc.base64ToBuf = base64ToBuf;
Enc.base64ToStr = function(b64) {
return base64ToBuf(b64).toString('utf8');
};
// URL Safe Base64
Enc.urlBase64ToBase64 = function(u64) {
var r = u64 % 4;
if (2 === r) {
u64 += '==';
} else if (3 === r) {
u64 += '=';
}
return u64.replace(/-/g, '+').replace(/_/g, '/');
};
Enc.base64ToUrlBase64 = function(b64) {
return b64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
};
Enc.bufToUrlBase64 = function(buf) {
return Enc.base64ToUrlBase64(bufToBase64(buf));
};
Enc.strToUrlBase64 = function(str) {
return Enc.base64ToUrlBase64(bufToBase64(Buffer.from(str)));
};
module.exports = Enc;

62
browser/base64.js Normal file
View File

@ -0,0 +1,62 @@
'use strict';
var Enc = require('./bytes.js');
// To Base64
Enc.bufToBase64 = function(u8) {
var bin = '';
u8.forEach(function(i) {
bin += String.fromCharCode(i);
});
return btoa(bin);
};
Enc.strToBase64 = function(str) {
return btoa(Enc.strToBin(str));
};
// From Base64
function _base64ToBin(b64) {
return atob(Enc.urlBase64ToBase64(b64));
}
Enc._base64ToBin = _base64ToBin;
Enc.base64ToBuf = function(b64) {
return Enc.binToBuf(_base64ToBin(b64));
};
Enc.base64ToStr = function(b64) {
return Enc.binToStr(_base64ToBin(b64));
};
// URL Safe Base64
Enc.urlBase64ToBase64 = function(u64) {
var r = u64 % 4;
if (2 === r) {
u64 += '==';
} else if (3 === r) {
u64 += '=';
}
return u64.replace(/-/g, '+').replace(/_/g, '/');
};
Enc.base64ToUrlBase64 = function(b64) {
return b64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
};
Enc.bufToUrlBase64 = function(buf) {
return Enc.base64ToUrlBase64(Enc.bufToBase64(buf));
};
Enc.strToUrlBase64 = function(str) {
return Enc.bufToUrlBase64(Enc.strToBuf(str));
};
module.exports = Enc;

70
browser/bytes.js Normal file
View File

@ -0,0 +1,70 @@
'use strict';
var Enc = module.exports;
// to Binary String
Enc.bufToBin = function(buf) {
var bin = '';
// cannot use .map() because Uint8Array would return only 0s
buf.forEach(function(ch) {
bin += String.fromCharCode(ch);
});
return bin;
};
Enc.strToBin = function(str) {
// Note: TextEncoder might be faster (or it might be slower, I don't know),
// but it doesn't solve the double-utf8 problem and MS Edge still has users without it
var escstr = encodeURIComponent(str);
// replaces any uri escape sequence, such as %0A,
// with binary escape, such as 0x0A
var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(_, p1) {
return String.fromCharCode('0x' + p1);
});
return binstr;
};
// to Buffer
Enc.binToBuf = function(bin) {
var arr = bin.split('').map(function(ch) {
return ch.charCodeAt(0);
});
return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};
Enc.strToBuf = function(str) {
return Enc.binToBuf(Enc.strToBin(str));
};
// to Unicode String
Enc.binToStr = function(binstr) {
var escstr = binstr.replace(/(.)/g, function(m, p) {
var code = p
.charCodeAt(0)
.toString(16)
.toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
});
return decodeURIComponent(escstr);
};
Enc.bufToStr = function(buf) {
return Enc.binToStr(Enc.bufToBin(buf));
};
// Base64 + Hex
Enc.base64ToHex = function(b64) {
return Enc.bufToHex(Enc.base64ToBuf(b64));
};
Enc.hexToBase64 = function(hex) {
return btoa(Enc._hexToBin(hex));
};

70
browser/hex.js Normal file
View File

@ -0,0 +1,70 @@
'use strict';
var Enc = require('./bytes.js');
// To Hex
Enc.bufToHex = function(u8) {
var hex = [];
var i, h;
var len = u8.byteLength || u8.length;
for (i = 0; i < len; i += 1) {
h = u8[i].toString(16);
if (2 !== h.length) {
h = '0' + h;
}
hex.push(h);
}
return hex.join('').toLowerCase();
};
Enc.numToHex = function(d) {
d = d.toString(16); // .padStart(2, '0');
if (d.length % 2) {
return '0' + d;
}
return d;
};
Enc.strToHex = function(str) {
return Enc._binToHex(Enc.strToBin(str));
};
Enc._binToHex = function(bin) {
return bin
.split('')
.map(function(ch) {
var h = ch.charCodeAt(0).toString(16);
if (2 !== h.length) {
h = '0' + h;
}
return h;
})
.join('');
};
// From Hex
Enc.hexToBuf = function(hex) {
var arr = [];
hex.match(/.{2}/g).forEach(function(h) {
arr.push(parseInt(h, 16));
});
return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};
Enc.hexToStr = function(hex) {
return Enc.binToStr(_hexToBin(hex));
};
function _hexToBin(hex) {
return hex.replace(/([0-9A-F]{2})/gi, function(_, p1) {
return String.fromCharCode('0x' + p1);
});
}
Enc._hexToBin = _hexToBin;
module.exports = Enc;

16
build.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# TODO convert to JS
cat browser/base64.js browser/hex.js browser/bytes.js encoding.js > all.tmp.js
sed -i '' '/use strict/d' all.tmp.js
sed -i '' '/require/d' all.tmp.js
sed -i '' '/exports/d' all.tmp.js
echo ';(function () {' > all.js
echo "'use strict';" >> all.js
echo "var Enc = window.Encoding = {};" >> all.js
cat all.tmp.js >> all.js
rm all.tmp.js
echo '}());' >> all.js
mv all.js dist/encoding.all.js
uglifyjs dist/encoding.all.js > dist/encoding.all.min.js

50
bytes.js Normal file
View File

@ -0,0 +1,50 @@
'use strict';
var Enc = module.exports;
/*
Enc.bufToUint8 = function bufToUint8(buf) {
return new Uint8Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));
};
*/
// from Binary Strings
Enc.binToBuf = function(bin) {
return Buffer.from(bin, 'binary');
};
Enc.binToStr = function(bin) {
return Enc.binToBuf(bin).toString('utf8');
};
// from Buffer
Enc.bufToBin = function(buf) {
return Buffer.from(buf).toString('binary');
};
Enc.bufToStr = function(buf) {
return Buffer.from(buf).toString('utf8');
};
// from Unicode Strings
Enc.strToBin = function(str) {
return Buffer.from(str).toString('binary');
};
Enc.strToBuf = function(str) {
// default is 'utf8'
return Buffer.from(str);
};
// Base64 and Hex
Enc.base64ToHex = function(b64) {
return Buffer.from(b64, 'base64').toString('hex');
};
Enc.hexToBase64 = function(hex) {
return Buffer.from(hex, 'hex').toString('base64');
};

199
dist/encoding.all.js vendored Normal file
View File

@ -0,0 +1,199 @@
;(function () {
'use strict';
var Enc = window.Encoding = {};
// To Base64
Enc.bufToBase64 = function(u8) {
var bin = '';
u8.forEach(function(i) {
bin += String.fromCharCode(i);
});
return btoa(bin);
};
Enc.strToBase64 = function(str) {
return btoa(Enc.strToBin(str));
};
// From Base64
function _base64ToBin(b64) {
return atob(Enc.urlBase64ToBase64(b64));
}
Enc._base64ToBin = _base64ToBin;
Enc.base64ToBuf = function(b64) {
return Enc.binToBuf(_base64ToBin(b64));
};
Enc.base64ToStr = function(b64) {
return Enc.binToStr(_base64ToBin(b64));
};
// URL Safe Base64
Enc.urlBase64ToBase64 = function(u64) {
var r = u64 % 4;
if (2 === r) {
u64 += '==';
} else if (3 === r) {
u64 += '=';
}
return u64.replace(/-/g, '+').replace(/_/g, '/');
};
Enc.base64ToUrlBase64 = function(b64) {
return b64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
};
Enc.bufToUrlBase64 = function(buf) {
return Enc.base64ToUrlBase64(Enc.bufToBase64(buf));
};
Enc.strToUrlBase64 = function(str) {
return Enc.bufToUrlBase64(Enc.strToBuf(str));
};
// To Hex
Enc.bufToHex = function(u8) {
var hex = [];
var i, h;
var len = u8.byteLength || u8.length;
for (i = 0; i < len; i += 1) {
h = u8[i].toString(16);
if (2 !== h.length) {
h = '0' + h;
}
hex.push(h);
}
return hex.join('').toLowerCase();
};
Enc.numToHex = function(d) {
d = d.toString(16); // .padStart(2, '0');
if (d.length % 2) {
return '0' + d;
}
return d;
};
Enc.strToHex = function(str) {
return Enc._binToHex(Enc.strToBin(str));
};
Enc._binToHex = function(bin) {
return bin
.split('')
.map(function(ch) {
var h = ch.charCodeAt(0).toString(16);
if (2 !== h.length) {
h = '0' + h;
}
return h;
})
.join('');
};
// From Hex
Enc.hexToBuf = function(hex) {
var arr = [];
hex.match(/.{2}/g).forEach(function(h) {
arr.push(parseInt(h, 16));
});
return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};
Enc.hexToStr = function(hex) {
return Enc.binToStr(_hexToBin(hex));
};
function _hexToBin(hex) {
return hex.replace(/([0-9A-F]{2})/gi, function(_, p1) {
return String.fromCharCode('0x' + p1);
});
}
Enc._hexToBin = _hexToBin;
// to Binary String
Enc.bufToBin = function(buf) {
var bin = '';
// cannot use .map() because Uint8Array would return only 0s
buf.forEach(function(ch) {
bin += String.fromCharCode(ch);
});
return bin;
};
Enc.strToBin = function(str) {
// Note: TextEncoder might be faster (or it might be slower, I don't know),
// but it doesn't solve the double-utf8 problem and MS Edge still has users without it
var escstr = encodeURIComponent(str);
// replaces any uri escape sequence, such as %0A,
// with binary escape, such as 0x0A
var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(_, p1) {
return String.fromCharCode('0x' + p1);
});
return binstr;
};
// to Buffer
Enc.binToBuf = function(bin) {
var arr = bin.split('').map(function(ch) {
return ch.charCodeAt(0);
});
return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};
Enc.strToBuf = function(str) {
return Enc.binToBuf(Enc.strToBin(str));
};
// to Unicode String
Enc.binToStr = function(binstr) {
var escstr = binstr.replace(/(.)/g, function(m, p) {
var code = p
.charCodeAt(0)
.toString(16)
.toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
});
return decodeURIComponent(escstr);
};
Enc.bufToStr = function(buf) {
return Enc.binToStr(Enc.bufToBin(buf));
};
// Base64 + Hex
Enc.base64ToHex = function(b64) {
return Enc.bufToHex(Enc.base64ToBuf(b64));
};
Enc.hexToBase64 = function(hex) {
return btoa(Enc._hexToBin(hex));
};
}());

1
dist/encoding.all.min.js vendored Normal file
View File

@ -0,0 +1 @@
(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i<len;i+=1){h=u8[i].toString(16);if(2!==h.length){h="0"+h}hex.push(h)}return hex.join("").toLowerCase()};Enc.numToHex=function(d){d=d.toString(16);if(d.length%2){return"0"+d}return d};Enc.strToHex=function(str){return Enc._binToHex(Enc.strToBin(str))};Enc._binToHex=function(bin){return bin.split("").map(function(ch){var h=ch.charCodeAt(0).toString(16);if(2!==h.length){h="0"+h}return h}).join("")};Enc.hexToBuf=function(hex){var arr=[];hex.match(/.{2}/g).forEach(function(h){arr.push(parseInt(h,16))});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.hexToStr=function(hex){return Enc.binToStr(_hexToBin(hex))};function _hexToBin(hex){return hex.replace(/([0-9A-F]{2})/gi,function(_,p1){return String.fromCharCode("0x"+p1)})}Enc._hexToBin=_hexToBin;Enc.bufToBin=function(buf){var bin="";buf.forEach(function(ch){bin+=String.fromCharCode(ch)});return bin};Enc.strToBin=function(str){var escstr=encodeURIComponent(str);var binstr=escstr.replace(/%([0-9A-F]{2})/g,function(_,p1){return String.fromCharCode("0x"+p1)});return binstr};Enc.binToBuf=function(bin){var arr=bin.split("").map(function(ch){return ch.charCodeAt(0)});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.strToBuf=function(str){return Enc.binToBuf(Enc.strToBin(str))};Enc.binToStr=function(binstr){var escstr=binstr.replace(/(.)/g,function(m,p){var code=p.charCodeAt(0).toString(16).toUpperCase();if(code.length<2){code="0"+code}return"%"+code});return decodeURIComponent(escstr)};Enc.bufToStr=function(buf){return Enc.binToStr(Enc.bufToBin(buf))};Enc.base64ToHex=function(b64){return Enc.bufToHex(Enc.base64ToBuf(b64))};Enc.hexToBase64=function(hex){return btoa(Enc._hexToBin(hex))}})();

5
encoding.js Normal file
View File

@ -0,0 +1,5 @@
'use strict';
require('./base64.js');
require('./hex.js');
module.exports = require('./bytes.js');

40
hex.js Normal file
View File

@ -0,0 +1,40 @@
'use strict';
var Enc = require('./bytes.js');
// to Hex
function bufToHex(buf) {
// in case it's a Uint8Array
return Buffer.from(buf).toString('hex');
}
Enc.bufToHex = bufToHex;
Enc.strToHex = function(str) {
return Buffer.from(str).toString('hex');
};
// from Hex
function hexToBuf(hex) {
return Buffer.from(hex, 'hex');
}
Enc.hexToBuf = hexToBuf;
Enc.hexToStr = function(hex) {
return hexToBuf(hex).toString('utf8');
};
// to/from num
Enc.numToHex = function(d) {
d = d.toString(16);
if (d.length % 2) {
return '0' + d;
}
return d;
};
module.exports = Enc;

5
package-lock.json generated Normal file
View File

@ -0,0 +1,5 @@
{
"name": "@root/encoding",
"version": "1.0.0",
"lockfileVersion": 1
}

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "@root/encoding",
"version": "1.0.0",
"description": "Leightweight, Zero-dependency, translation between Unicode, Buffers, Base64, Hex, Binary Strings, UCS-2, UTF-8, etc.",
"main": "./encoding.js",
"browser": {
"./base64.js": "./browser/base64.js",
"./bytes.js": "./browser/bytes.js",
"./hex.js": "./browser/hex.js"
},
"scripts": {
"test": "node tests/"
},
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/encoding.js.git"
},
"keywords": [
"Unicode",
"UTF-8",
"Buffer",
"ArrayBuffer",
"Binary",
"Strings",
"TypedArray",
"utf8",
"unibabel"
],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0"
}

5
tests/index.html Normal file
View File

@ -0,0 +1,5 @@
<html>
<meta charset="UTF-8" />
<script src="../dist/encoding.all.js"></script>
<script src="./index.js"></script>
</html>

216
tests/index.js Normal file
View File

@ -0,0 +1,216 @@
'use strict';
var Enc =
('undefined' === typeof window ? {} : window).Encoding || require('../');
//UTF-8
var pass = 0;
var references = {
string: 'I ½ ♥ 𩶘',
array: [73, 32, 194, 189, 32, 226, 153, 165, 32, 240, 169, 182, 152],
hex: [
'49',
'20',
'c2',
'bd',
'20',
'e2',
'99',
'a5',
'20',
'f0',
'a9',
'b6',
'98'
].join(''),
base64: 'SSDCvSDimaUg8Km2mA==',
urlBase64: 'SSDCvSDimaUg8Km2mA',
base32: 'JEQMFPJA4KM2KIHQVG3JQ==='
};
references.bin = references.array
.map(function(n) {
return String.fromCharCode(n);
})
.join('');
references.buffer = Uint8Array.from(references.array);
var binrefs = {
// Note that the binary string "ÿâó<86>Î<93>k" can't be serialized to text
array: [255, 226, 26, 243, 134, 206, 147, 107],
hex: 'ffe21af386ce936b',
base64: '/+Ia84bOk2s=',
urlBase64: '_-Ia84bOk2s'
};
binrefs.buffer = new Uint8Array(binrefs.array);
var str = references.string;
var buf = Enc.strToBuf(references.string);
var base64 = Enc.bufToBase64(references.buffer);
var hex = Enc.bufToHex(references.buffer);
//var b32 = Enc.bufToBase32(references.buffer);
function buffersAreEqual(buf1, buf2) {
if (buf1.length !== buf2.length) {
return false;
}
return Array.prototype.every.call(buf1, function(byte, i) {
if (byte === buf2[i]) {
return true;
}
});
}
// To Binary String
if (Enc.strToBin(references.string) === references.bin) {
pass += 1;
} else {
console.error('[FAIL] str -> bin');
}
if (Enc.bufToBin(references.array) !== references.bin) {
console.error('[FAIL] buf -> bin');
} else {
pass += 1;
}
// To Byte Array
if (!buffersAreEqual(Enc.strToBuf(references.string), references.array)) {
console.error('[FAIL] utf8 -> buf');
} else {
pass += 1;
}
if (!buffersAreEqual(Enc.base64ToBuf(references.base64), references.array)) {
console.error('[FAIL] base64 -> buf');
} else {
pass += 1;
}
if (!buffersAreEqual(Enc.hexToBuf(references.hex), references.array)) {
console.error('[FAIL] hex -> buf');
} else {
pass += 1;
}
if (!buffersAreEqual(Enc.binToBuf(references.bin), references.array)) {
console.error('[FAIL] bin -> buf');
} else {
pass += 1;
}
// To Unicode String
if (Enc.bufToStr(references.array) !== references.string) {
console.error('[FAIL] buf -> str');
} else {
pass += 1;
}
if (Enc.base64ToStr(references.base64) !== references.string) {
console.error('[FAIL] base64 -> str');
} else {
pass += 1;
}
if (Enc.base64ToStr(references.urlBase64) !== references.string) {
console.error('[FAIL] url base64 -> str');
} else {
pass += 1;
}
if (Enc.hexToStr(references.hex) !== references.string) {
console.error('[FAIL] hex -> str');
} else {
pass += 1;
}
if (Enc.binToStr(references.bin) !== references.string) {
console.error('[FAIL] bin -> str');
} else {
pass += 1;
}
// To Base64
if (Enc.bufToBase64(references.array) !== references.base64) {
console.error('[FAIL] buf -> base64');
} else {
pass += 1;
}
if (Enc.bufToUrlBase64(references.array) !== references.urlBase64) {
console.error('[FAIL] buf -> url base64');
} else {
pass += 1;
}
if (Enc.strToBase64(references.string) !== references.base64) {
console.error('[FAIL] str -> base64');
} else {
pass += 1;
}
if (Enc.strToUrlBase64(references.string) !== references.urlBase64) {
console.error('[FAIL] str -> url base64');
} else {
pass += 1;
}
if (Enc.hexToBase64(references.hex) !== references.base64) {
console.error('[FAIL] hex -> base64');
} else {
pass += 1;
}
// To Hex
if (Enc.bufToHex(references.array) !== references.hex) {
console.error('[FAIL] buf -> hex');
} else {
pass += 1;
}
if (Enc.strToHex(references.string) !== references.hex) {
console.error('[FAIL] str -> hex', Enc.strToHex(references.string));
} else {
pass += 1;
}
if (Enc.base64ToHex(references.base64) !== references.hex) {
console.error('[FAIL] base64 -> hex');
} else {
pass += 1;
}
// Raw Binary
if (Enc.bufToUrlBase64(binrefs.array) !== binrefs.urlBase64) {
console.error('[FAIL] buf -> url base64');
} else {
pass += 1;
}
var bytes = binrefs.array;
buf = new Uint8Array(bytes);
str = Enc.bufToBin(buf);
base64 = Enc.bufToBase64(buf);
hex = Enc.bufToHex(buf);
// This can't be properly tested because binary strings can't be parsed
// if (str !== "ÿâó†Î“k") {
// pass += 1;
// console.log('[FAIL] binary -> str', str);
// }
if (binrefs.base64 !== base64) {
console.error('[FAIL] binary -> base64', base64);
} else {
pass += 1;
}
if (binrefs.hex !== hex) {
console.error('[FAIL] binary -> hex', hex);
} else {
pass += 1;
}
//
// Base32
//
/*
b32 = Enc.bufToBase32(references.buffer);
if (references.base32 !== b32) {
pass += 1;
console.error('[FAIL] binary -> base32', references.base32, '!==', b32);
}
buf = Enc.base32ToBuffer(references.base32);
if (!buffersAreEqual(buf, references.buffer)) {
pass += 1
console.error('[FAIL] base32 -> binary', references.buffer, '!==', buf);
}
*/
if (22 === pass) {
console.info('[PASS] ' + pass + ' tests passed');
} else {
console.error('[FAIL] ' + (22 - pass) + ' of 22 tests failed');
}