v1.0.0: common encodings for browsers and node.js
This commit is contained in:
parent
e0302b9b3f
commit
96fcbdbcad
368
README.md
368
README.md
|
@ -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.
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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));
|
||||
};
|
|
@ -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;
|
|
@ -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
|
|
@ -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');
|
||||
};
|
|
@ -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));
|
||||
};
|
||||
|
||||
}());
|
|
@ -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))}})();
|
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
require('./base64.js');
|
||||
require('./hex.js');
|
||||
module.exports = require('./bytes.js');
|
|
@ -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;
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "@root/encoding",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="../dist/encoding.all.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
</html>
|
|
@ -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');
|
||||
}
|
Loading…
Reference in New Issue