I have some UTF-8 encoded data living in a range of Uint8Array elements in Javascript. Is there an efficient way to decode these out to a regular javascript string (I believe Javascript uses 16 bit Unicode)? I dont want to add one character at the time as the string concaternation would become to CPU intensive.
TextEncoder and TextDecoder from the Encoding standard, which is polyfilled by the stringencoding library, converts between strings and ArrayBuffers:
var uint8array = new TextEncoder().encode("someString");
var string = new TextDecoder().decode(uint8array);
This should work:
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
/* utf.js - UTF-8 <=> UTF-16 convertion
*
* Copyright (C) 1999 Masanao Izumo <iz#onicos.co.jp>
* Version: 1.0
* LastModified: Dec 25 1999
* This library is free. You can redistribute it and/or modify it.
*/
function Utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;
out = "";
len = array.length;
i = 0;
while(i < len) {
c = array[i++];
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}
It's somewhat cleaner as the other solutions because it doesn't use any hacks nor depends on Browser JS functions, e.g. works also in other JS environments.
Check out the JSFiddle demo.
Also see the related questions: here and here
Here's what I use:
var str = String.fromCharCode.apply(null, uint8Arr);
In Node "Buffer instances are also Uint8Array instances", so buf.toString() works in this case.
In NodeJS, we have Buffers available, and string conversion with them is really easy. Better, it's easy to convert a Uint8Array to a Buffer. Try this code, it's worked for me in Node for basically any conversion involving Uint8Arrays:
let str = Buffer.from(uint8arr.buffer).toString();
We're just extracting the ArrayBuffer from the Uint8Array and then converting that to a proper NodeJS Buffer. Then we convert the Buffer to a string (you can throw in a hex or base64 encoding if you want).
If we want to convert back to a Uint8Array from a string, then we'd do this:
let uint8arr = new Uint8Array(Buffer.from(str));
Be aware that if you declared an encoding like base64 when converting to a string, then you'd have to use Buffer.from(str, "base64") if you used base64, or whatever other encoding you used.
This will not work in the browser without a module! NodeJS Buffers just don't exist in the browser, so this method won't work unless you add Buffer functionality to the browser. That's actually pretty easy to do though, just use a module like this, which is both small and fast!
Found in one of the Chrome sample applications, although this is meant for larger blocks of data where you're okay with an asynchronous conversion.
/**
* Converts an array buffer to a string
*
* #private
* #param {ArrayBuffer} buf The buffer to convert
* #param {Function} callback The function to call when conversion is complete
*/
function _arrayBufferToString(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
The solution given by Albert works well as long as the provided function is invoked infrequently and is only used for arrays of modest size, otherwise it is egregiously inefficient. Here is an enhanced vanilla JavaScript solution that works for both Node and browsers and has the following advantages:
• Works efficiently for all octet array sizes
• Generates no intermediate throw-away strings
• Supports 4-byte characters on modern JS engines (otherwise "?" is substituted)
var utf8ArrayToStr = (function () {
var charCache = new Array(128); // Preallocate the cache for the common single byte chars
var charFromCodePt = String.fromCodePoint || String.fromCharCode;
var result = [];
return function (array) {
var codePt, byte1;
var buffLen = array.length;
result.length = 0;
for (var i = 0; i < buffLen;) {
byte1 = array[i++];
if (byte1 <= 0x7F) {
codePt = byte1;
} else if (byte1 <= 0xDF) {
codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
} else if (byte1 <= 0xEF) {
codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else if (String.fromCodePoint) {
codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else {
codePt = 63; // Cannot convert four byte code points, so use "?" instead
i += 3;
}
result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
}
return result.join('');
};
})();
Uint8Array to String
let str = Buffer.from(key.secretKey).toString('base64');
String to Uint8Array
let uint8arr = new Uint8Array(Buffer.from(data,'base64'));
I was frustrated to see that people were not showing how to go both ways or showing that things work on none trivial UTF8 strings. I found a post on codereview.stackexchange.com that has some code that works well. I used it to turn ancient runes into bytes, to test some crypo on the bytes, then convert things back into a string. The working code is on github here. I renamed the methods for clarity:
// https://codereview.stackexchange.com/a/3589/75693
function bytesToSring(bytes) {
var chars = [];
for(var i = 0, n = bytes.length; i < n;) {
chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
}
return String.fromCharCode.apply(null, chars);
}
// https://codereview.stackexchange.com/a/3589/75693
function stringToBytes(str) {
var bytes = [];
for(var i = 0, n = str.length; i < n; i++) {
var char = str.charCodeAt(i);
bytes.push(char >>> 8, char & 0xFF);
}
return bytes;
}
The unit test uses this UTF-8 string:
// http://kermitproject.org/utf8.html
// From the Anglo-Saxon Rune Poem (Rune version)
const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`;
Note that the string length is only 117 characters but the byte length, when encoded, is 234.
If I uncomment the console.log lines I can see that the string that is decoded is the same string that was encoded (with the bytes passed through Shamir's secret sharing algorithm!):
Do what #Sudhir said, and then to get a String out of the comma seperated list of numbers use:
for (var i=0; i<unitArr.byteLength; i++) {
myString += String.fromCharCode(unitArr[i])
}
This will give you the string you want,
if it's still relevant
If you can't use the TextDecoder API because it is not supported on IE:
You can use the FastestSmallestTextEncoderDecoder polyfill recommended by the Mozilla Developer Network website;
You can use this function also provided at the MDN website:
function utf8ArrayToString(aBytes) {
var sView = "";
for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
nPart = aBytes[nIdx];
sView += String.fromCharCode(
nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
/* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
(nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
(nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
(nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
(nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
(nPart - 192 << 6) + aBytes[++nIdx] - 128
: /* nPart < 127 ? */ /* one byte */
nPart
);
}
return sView;
}
let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);
// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);
Try these functions,
var JsonToArray = function(json)
{
var str = JSON.stringify(json, null, 0);
var ret = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
ret[i] = str.charCodeAt(i);
}
return ret
};
var binArrayToJson = function(binArray)
{
var str = "";
for (var i = 0; i < binArray.length; i++) {
str += String.fromCharCode(parseInt(binArray[i]));
}
return JSON.parse(str)
}
source: https://gist.github.com/tomfa/706d10fed78c497731ac, kudos to Tomfa
I'm using this function, which works for me:
function uint8ArrayToBase64(data) {
return btoa(Array.from(data).map((c) => String.fromCharCode(c)).join(''));
}
For ES6 and UTF8 string
decodeURIComponent(escape(String.fromCharCode(...uint8arrData)))
By far the easiest way that has worked for me is:
//1. Create or fetch the Uint8Array to use in the example
const bufferArray = new Uint8Array([10, 10, 10])
//2. Turn the Uint8Array into a regular array
const array = Array.from(bufferArray);
//3. Stringify it (option A)
JSON.stringify(array);
//3. Stringify it (option B: uses #serdarsenay code snippet to decode each item in array)
let binArrayToString = function(binArray) {
let str = "";
for (let i = 0; i < binArray.length; i++) {
str += String.fromCharCode(parseInt(binArray[i]));
}
return str;
}
binArrayToString(array);
class UTF8{
static encode(str:string){return new UTF8().encode(str)}
static decode(data:Uint8Array){return new UTF8().decode(data)}
private EOF_byte:number = -1;
private EOF_code_point:number = -1;
private encoderError(code_point) {
console.error("UTF8 encoderError",code_point)
}
private decoderError(fatal, opt_code_point?):number {
if (fatal) console.error("UTF8 decoderError",opt_code_point)
return opt_code_point || 0xFFFD;
}
private inRange(a:number, min:number, max:number) {
return min <= a && a <= max;
}
private div(n:number, d:number) {
return Math.floor(n / d);
}
private stringToCodePoints(string:string) {
/** #type {Array.<number>} */
let cps = [];
// Based on http://www.w3.org/TR/WebIDL/#idl-DOMString
let i = 0, n = string.length;
while (i < string.length) {
let c = string.charCodeAt(i);
if (!this.inRange(c, 0xD800, 0xDFFF)) {
cps.push(c);
} else if (this.inRange(c, 0xDC00, 0xDFFF)) {
cps.push(0xFFFD);
} else { // (inRange(c, 0xD800, 0xDBFF))
if (i == n - 1) {
cps.push(0xFFFD);
} else {
let d = string.charCodeAt(i + 1);
if (this.inRange(d, 0xDC00, 0xDFFF)) {
let a = c & 0x3FF;
let b = d & 0x3FF;
i += 1;
cps.push(0x10000 + (a << 10) + b);
} else {
cps.push(0xFFFD);
}
}
}
i += 1;
}
return cps;
}
private encode(str:string):Uint8Array {
let pos:number = 0;
let codePoints = this.stringToCodePoints(str);
let outputBytes = [];
while (codePoints.length > pos) {
let code_point:number = codePoints[pos++];
if (this.inRange(code_point, 0xD800, 0xDFFF)) {
this.encoderError(code_point);
}
else if (this.inRange(code_point, 0x0000, 0x007f)) {
outputBytes.push(code_point);
} else {
let count = 0, offset = 0;
if (this.inRange(code_point, 0x0080, 0x07FF)) {
count = 1;
offset = 0xC0;
} else if (this.inRange(code_point, 0x0800, 0xFFFF)) {
count = 2;
offset = 0xE0;
} else if (this.inRange(code_point, 0x10000, 0x10FFFF)) {
count = 3;
offset = 0xF0;
}
outputBytes.push(this.div(code_point, Math.pow(64, count)) + offset);
while (count > 0) {
let temp = this.div(code_point, Math.pow(64, count - 1));
outputBytes.push(0x80 + (temp % 64));
count -= 1;
}
}
}
return new Uint8Array(outputBytes);
}
private decode(data:Uint8Array):string {
let fatal:boolean = false;
let pos:number = 0;
let result:string = "";
let code_point:number;
let utf8_code_point = 0;
let utf8_bytes_needed = 0;
let utf8_bytes_seen = 0;
let utf8_lower_boundary = 0;
while (data.length > pos) {
let _byte = data[pos++];
if (_byte == this.EOF_byte) {
if (utf8_bytes_needed != 0) {
code_point = this.decoderError(fatal);
} else {
code_point = this.EOF_code_point;
}
} else {
if (utf8_bytes_needed == 0) {
if (this.inRange(_byte, 0x00, 0x7F)) {
code_point = _byte;
} else {
if (this.inRange(_byte, 0xC2, 0xDF)) {
utf8_bytes_needed = 1;
utf8_lower_boundary = 0x80;
utf8_code_point = _byte - 0xC0;
} else if (this.inRange(_byte, 0xE0, 0xEF)) {
utf8_bytes_needed = 2;
utf8_lower_boundary = 0x800;
utf8_code_point = _byte - 0xE0;
} else if (this.inRange(_byte, 0xF0, 0xF4)) {
utf8_bytes_needed = 3;
utf8_lower_boundary = 0x10000;
utf8_code_point = _byte - 0xF0;
} else {
this.decoderError(fatal);
}
utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
code_point = null;
}
} else if (!this.inRange(_byte, 0x80, 0xBF)) {
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
pos--;
code_point = this.decoderError(fatal, _byte);
} else {
utf8_bytes_seen += 1;
utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
if (utf8_bytes_seen !== utf8_bytes_needed) {
code_point = null;
} else {
let cp = utf8_code_point;
let lower_boundary = utf8_lower_boundary;
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
if (this.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) {
code_point = cp;
} else {
code_point = this.decoderError(fatal, _byte);
}
}
}
}
//Decode string
if (code_point !== null && code_point !== this.EOF_code_point) {
if (code_point <= 0xFFFF) {
if (code_point > 0)result += String.fromCharCode(code_point);
} else {
code_point -= 0x10000;
result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
}
}
}
return result;
}
`
Using base64 as the encoding format works quite well. This is how it was implemented for passing secrets via urls in Firefox Send. You will need the base64-js package. These are the functions from the Send source code:
const b64 = require("base64-js")
function arrayToB64(array) {
return b64.fromByteArray(array).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
}
function b64ToArray(str) {
return b64.toByteArray(str + "===".slice((str.length + 3) % 4))
}
With vanilla, browser side, recording from microphone, base64 functions worked for me (I had to implement an audio sending function to a chat).
const ui8a = new Uint8Array(e.target.result);
const string = btoa(ui8a);
const ui8a_2 = atob(string).split(',');
Full code now. Thanks to Bryan Jennings & breakspirit#py4u.net for the code.
https://medium.com/#bryanjenningz/how-to-record-and-play-audio-in-javascript-faa1b2b3e49b
https://www.py4u.net/discuss/282499
index.html
<html>
<head>
<title>Record Audio Test</title>
<meta name="encoding" charset="utf-8" />
</head>
<body>
<h1>Audio Recording Test</h1>
<script src="index.js"></script>
<button id="action" onclick="start()">Start</button>
<button id="stop" onclick="stop()">Stop</button>
<button id="play" onclick="play()">Listen</button>
</body>
</html>
index.js:
const recordAudio = () =>
new Promise(async resolve => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaRecorder = new MediaRecorder(stream);
const audioChunks = [];
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => mediaRecorder.start();
const stop = () =>
new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks);
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
const play = () => audio.play();
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
resolve({ start, stop });
});
let recorder = null;
let audio = null;
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
const start = async () => {
recorder = await recordAudio();
recorder.start();
}
const stop = async () => {
audio = await recorder.stop();
read(audio.audioUrl);
}
const play = ()=> {
audio.play();
}
const read = (blobUrl)=> {
var xhr = new XMLHttpRequest;
xhr.responseType = 'blob';
xhr.onload = function() {
var recoveredBlob = xhr.response;
const reader = new FileReader();
// This fires after the blob has been read/loaded.
reader.addEventListener('loadend', (e) => {
const ui8a = new Uint8Array(e.target.result);
const string = btoa(ui8a);
const ui8a_2 = atob(string).split(',');
playByteArray(ui8a_2);
});
// Start reading the blob as text.
reader.readAsArrayBuffer(recoveredBlob);
};
// get the blob through blob url
xhr.open('GET', blobUrl);
xhr.send();
}
window.onload = init;
var context; // Audio context
var buf; // Audio buffer
function init() {
if (!window.AudioContext) {
if (!window.webkitAudioContext) {
alert("Your browser does not support any AudioContext and cannot play back this audio.");
return;
}
window.AudioContext = window.webkitAudioContext;
}
context = new AudioContext();
}
function playByteArray(byteArray) {
var arrayBuffer = new ArrayBuffer(byteArray.length);
var bufferView = new Uint8Array(arrayBuffer);
for (i = 0; i < byteArray.length; i++) {
bufferView[i] = byteArray[i];
}
context.decodeAudioData(arrayBuffer, function(buffer) {
buf = buffer;
play2();
});
}
// Play the loaded file
function play2() {
// Create a source node from the buffer
var source = context.createBufferSource();
source.buffer = buf;
// Connect to the final output node (the speakers)
source.connect(context.destination);
// Play immediately
source.start(0);
}
var decodedString = decodeURIComponent(escape(String.fromCharCode(...new Uint8Array(err))));
var obj = JSON.parse(decodedString);
I am using this Typescript snippet:
function UInt8ArrayToString(uInt8Array: Uint8Array): string
{
var s: string = "[";
for(var i: number = 0; i < uInt8Array.byteLength; i++)
{
if( i > 0 )
s += ", ";
s += uInt8Array[i];
}
s += "]";
return s;
}
Remove the type annotations if you need the JavaScript version.
Hope this helps!
I'm looking for a JS script that will convert a base-36 number like:
23SQJ1LNEFSL00H18IVWABMP
to a base-62 number like:
1rZmfPo0xtnf8CLTfWRJh
I'm trying to translate this python code to do that.
converter.py
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(num, alphabet=BASE62):
"""Encode a positive number in Base X
Arguments:
- `num`: The number to encode
- `alphabet`: The alphabet to use for encoding
"""
if num == 0:
return alphabet[0]
arr = []
base = len(alphabet)
while num:
num, rem = divmod(num, base)
arr.append(alphabet[rem])
arr.reverse()
return ''.join(arr)
How can I accomplish this?
Here's an answer I found by #RyanSmith https://stackoverflow.com/a/32480941
function convertBase(value, from_base, to_base) {
var range = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'.split('');
var from_range = range.slice(0, from_base);
var to_range = range.slice(0, to_base);
var dec_value = value.split('').reverse().reduce(function (carry, digit, index) {
if (from_range.indexOf(digit) === -1) throw new Error('Invalid digit `'+digit+'` for base '+from_base+'.');
return carry += from_range.indexOf(digit) * (Math.pow(from_base, index));
}, 0);
var new_value = '';
while (dec_value > 0) {
new_value = to_range[dec_value % to_base] + new_value;
dec_value = (dec_value - (dec_value % to_base)) / to_base;
}
return new_value || '0';
}
This converts base-10 to base-62 https://helloacm.com/base62/
// https://helloacm.com
// https://rot47.net
// base62.js
// provides conversion between base10 and base62
var Base62 = (function(){
var table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
function _to10(num)
{
var limit = num.length;
var res = 0;
for (var i = 0; i < limit; i ++)
{
res = 62 * res + table.indexOf(num.charAt(i));
}
return res;
}
function _toBase(num)
{
var r = num % 62;
var res = table.charAt(r);
var q = Math.floor(num / 62);
while (q)
{
r = q % 62;
q = Math.floor(q / 62);
res = table.charAt(r) + res;
}
return res;
}
return {
FromBase10: function()
{
var r = [];
for (var i = 0; i < arguments.length; i ++)
{
var num = parseInt(arguments[i]);
r.push(_toBase(num));
}
return r;
},
FromBase62: function()
{
var r = [];
for (var i = 0; i < arguments.length; i ++)
{
var num = arguments[i].toString();
if (num.length)
{
r.push(_to10(num));
}
}
return r;
}
}
})();
I am looking for a way to generate the same sequence of pseudo random integer numbers from both python and javascript.
When I seed in python like this I get the below results:
random.seed(3909461935)
random.randint(0, 2147483647) = 162048056
random.randint(0, 2147483647) = 489743869
random.randint(0, 2147483647) = 1561110296
I need the same sequence in javascript.
Note: I used 2147483647 as the range in the randint method because I am assuming javascript can only handle 32 bit INTs.
Are there any libraries on both sides I can use to generate the same set of pseudo random numbers given the same seed?
I have found two implementations of Mersenne Twister that generate the same 32 bit integer values given the same seed.
This way you can generate a server side sequence in Python, and have the browser independently generate the same sequence in javascript.
Python:
from mt_random import *
r = mersenne_rng(seed = 12345)
r.get_random_number() # Prints 3992670690
r.get_random_number() # Prints 3823185381
r.get_random_number() # Prints 1358822685
Javascript:
r = new MersenneTwister();
r.init_genrand(12345);
r = mersenne_rng(seed = 12345)
r.genrand_int32(); # Prints 3992670690
r.genrand_int32(); # Prints 3823185381
r.genrand_int32(); # Prints 1358822685
The JS is here:
/*
* 疑似乱数生成機 移植
*
* Mersenne Twister with improved initialization (2002)
* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html
* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/mt19937ar.html
*/
// = 移植元ラインセンス =======================================================
// ======================================================================
/*
A C-program for MT19937, with initialization improved 2002/2/10.
Coded by Takuji Nishimura and Makoto Matsumoto.
This is a faster version by taking Shawn Cokus's optimization,
Matthe Bellew's simplification, Isaku Wada's real version.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or another materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Any feedback is very welcome.
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat # math.sci.hiroshima-u.ac.jp (remove space)
*/
// ======================================================================
function MersenneTwister() {
// 整数を扱うクラス
function Int32(value) {
var bits = new Array(0, 0, 0, 0);
var i;
var v = value;
if (v != 0) {
for (i = 0; i < 4; ++i) {
bits[i] = v & 0xff;
v = v >> 8;
}
}
this.getValue = function () {
return (bits[0] | (bits[1] << 8) | (bits[2] << 16)) + ((bits[3] << 16) * 0x100);
};
this.getBits = function (i) { return bits[i & 3]; };
this.setBits = function (i, val) { return (bits[i & 3] = val & 0xff); };
this.add = function (another) {
var tmp = new Int32(0);
var i, fl = 0, b;
for (i = 0; i < 4; ++i) {
b = bits[i] + another.getBits(i) + fl;
tmp.setBits(i, b);
fl = b >> 8;
}
return tmp;
};
this.sub = function (another) {
var tmp = new Int32(0);
var bb = new Array(0, 0, 0, 0);
var i;
for (i = 0; i < 4; ++i) {
bb[i] = bits[i] - another.getBits(i);
if ((i > 0) && (bb[i - 1] < 0)) {
--bb[i];
}
}
for (i = 0; i < 4; ++i) {
tmp.setBits(i, bb[i]);
}
return tmp;
};
this.mul = function (another) {
var tmp = new Int32(0);
var bb = new Array(0, 0, 0, 0, 0);
var i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; i + j < 4; ++j) {
bb[i + j] += bits[i] * another.getBits(j);
}
tmp.setBits(i, bb[i]);
bb[i + 1] += bb[i] >> 8;
}
return tmp;
};
this.and = function (another) {
var tmp = new Int32(0);
var i;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, bits[i] & another.getBits(i));
}
return tmp;
};
this.or = function (another) {
var tmp = new Int32(0);
var i;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, bits[i] | another.getBits(i));
}
return tmp;
};
this.xor = function (another) {
var tmp = new Int32(0);
var i;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, bits[i] ^ another.getBits(i));
}
return tmp;
};
this.rshifta = function (s) {
var tmp = new Int32(0);
var bb = new Array(0, 0, 0, 0, 0);
var p = s >> 3;
var i, sg = 0;
if ((bits[3] & 0x80) > 0) {
bb[4] = sg = 0xff;
}
for (i = 0; i + p < 4; ++i) {
bb[i] = bits[i + p];
}
for (; i < 4; ++i) {
bb[i] = sg;
}
p = s & 0x7;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, ((bb[i] | (bb[i + 1] << 8)) >> p) & 0xff);
}
return tmp;
};
this.rshiftl = function (s) {
var tmp = new Int32(0);
var bb = new Array(0, 0, 0, 0, 0);
var p = s >> 3;
var i;
for (i = 0; i + p < 4; ++i) {
bb[i] = bits[i + p];
}
p = s & 0x7;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, ((bb[i] | (bb[i + 1] << 8)) >> p) & 0xff);
}
return tmp;
};
this.lshift = function (s) {
var tmp = new Int32(0);
var bb = new Array(0, 0, 0, 0, 0);
var p = s >> 3;
var i;
for (i = 0; i + p < 4; ++i) {
bb[i + p + 1] = bits[i];
}
p = s & 0x7;
for (i = 0; i < 4; ++i) {
tmp.setBits(i, (((bb[i] | (bb[i + 1] << 8)) << p) >> 8) & 0xff);
}
return tmp;
};
this.equals = function (another) {
var i;
for (i = 0; i < 4; ++i) {
if (bits[i] != another.getBits(i)) {
return false;
}
}
return true;
};
this.compare = function (another) {
var i;
for (i = 3; i >= 0; --i) {
if (bits[i] > another.getBits(i)) {
return 1;
} else if (bits[i] < another.getBits(i)) {
return -1;
}
}
return 0;
};
}
// End of Int32
/* Period parameters */
var N = 624;
var M = 397;
var MATRIX_A = new Int32(0x9908b0df); /* constant vector a */
var UMASK = new Int32(0x80000000); /* most significant w-r bits */
var LMASK = new Int32(0x7fffffff); /* least significant r bits */
var INT32_ZERO = new Int32(0);
var INT32_ONE = new Int32(1);
var MIXBITS = function (u, v) {
return (u.and(UMASK)).or(v.and(LMASK));
};
var TWIST = function (u, v) {
return ((MIXBITS(u, v).rshiftl(1)).xor((v.and(INT32_ONE)).equals(INT32_ZERO) ? INT32_ZERO : MATRIX_A));
};
var state = new Array(); /* the array for the state vector */
var left = 1;
var initf = 0;
var next = 0;
var i;
for (i = 0; i < N; ++i) {
state[i] = INT32_ZERO;
}
/* initializes state[N] with a seed */
var _init_genrand = function (s) {
var lt1812433253 = new Int32(1812433253);
var j;
state[0]= new Int32(s);
for (j = 1; j < N; ++j) {
state[j] = ((lt1812433253.mul(state[j - 1].xor(state[j - 1].rshiftl(30)))).add(new Int32(j)));
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array state[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
//state[j] &= 0xffffffff; /* for >32 bit machines */
}
left = 1; initf = 1;
};
this.init_genrand = _init_genrand;
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
this.init_by_array = function (init_key, key_length) {
var lt1664525 = new Int32(1664525);
var lt1566083941 = new Int32(1566083941);
var i, j, k;
_init_genrand(19650218);
i = 1; j = 0;
k = (N > key_length ? N : key_length);
for (; k; --k) {
state[i] = ((state[i].xor((state[i - 1].xor(state[i - 1].rshiftl(30))).mul(lt1664525))).add(
new Int32(init_key[j]))).add(new Int32(j)); /* non linear */
//state[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
i++; j++;
if (i >= N) {
state[0] = state[N - 1];
i = 1;
}
if (j >= key_length) {
j = 0;
}
}
for (k = N - 1; k; --k) {
state[i] = (state[i].xor((state[i-1].xor(state[i - 1].rshiftl(30))).mul(lt1566083941))).sub(
new Int32(i)); /* non linear */
//state[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
i++;
if (i >= N) {
state[0] = state[N - 1];
i = 1;
}
}
state[0] = new Int32(0x80000000); /* MSB is 1; assuring non-zero initial array */
left = 1; initf = 1;
};
var next_state = function () {
var p = 0;
var j;
/* if init_genrand() has not been called, */
/* a default initial seed is used */
if (initf == 0) {
_init_genrand(5489);
}
left = N;
next = 0;
for (j = N - M + 1; --j; ++p) {
state[p] = state[p + M].xor(TWIST(state[p], state[p + 1]));
}
for (j = M; --j; ++p) {
state[p] = state[p + M - N].xor(TWIST(state[p], state[p + 1]));
}
state[p] = state[p + M - N].xor(TWIST(state[p], state[0]));
};
var lt0x9d2c5680 = new Int32(0x9d2c5680);
var lt0xefc60000 = new Int32(0xefc60000);
/* generates a random number on [0,0xffffffff]-interval */
var _genrand_int32 = function () {
var y;
if (--left == 0) {
next_state();
}
y = state[next];
++next;
/* Tempering */
y = y.xor(y.rshiftl(11));
y = y.xor((y.lshift(7)).and(lt0x9d2c5680));
y = y.xor((y.lshift(15)).and(lt0xefc60000));
y = y.xor(y.rshiftl(18));
return y.getValue();
};
this.genrand_int32 = _genrand_int32;
/* generates a random number on [0,0x7fffffff]-interval */
this.genrand_int31 = function () {
var y;
if (--left == 0) {
next_state();
}
y = state[next];
++next;
/* Tempering */
y = y.xor(y.rshiftl(11));
y = y.xor((y.lshift(7)).and(lt0x9d2c5680));
y = y.xor((y.lshift(15)).and(lt0xefc60000));
y = y.xor(y.rshiftl(18));
return (y.rshiftl(1)).getValue();
};
/* generates a random number on [0,1]-real-interval */
this.genrand_real1 = function () {
var y;
if (--left == 0) {
next_state();
}
y = state[next];
++next;
/* Tempering */
y = y.xor(y.rshiftl(11));
y = y.xor((y.lshift(7)).and(lt0x9d2c5680));
y = y.xor((y.lshift(15)).and(lt0xefc60000));
y = y.xor(y.rshiftl(18));
return y.getValue() * (1.0/4294967295.0);
/* divided by 2^32-1 */
};
/* generates a random number on [0,1)-real-interval */
this.genrand_real2 = function () {
var y;
if (--left == 0) {
next_state();
}
y = state[next];
++next;
/* Tempering */
y = y.xor(y.rshiftl(11));
y = y.xor((y.lshift(7)).and(lt0x9d2c5680));
y = y.xor((y.lshift(15)).and(lt0xefc60000));
y = y.xor(y.rshiftl(18));
return y.getValue() * (1.0 / 4294967296.0);
/* divided by 2^32 */
};
/* generates a random number on (0,1)-real-interval */
this.genrand_real3 = function () {
var y;
if (--left == 0) {
next_state();
}
y = state[next];
++next;
/* Tempering */
y = y.xor(y.rshiftl(11));
y = y.xor((y.lshift(7)).and(lt0x9d2c5680));
y = y.xor((y.lshift(15)).and(lt0xefc60000));
y = y.xor(y.rshiftl(18));
return (y.getValue() + 0.5) * (1.0 / 4294967296.0);
/* divided by 2^32 */
};
/* generates a random number on [0,1) with 53-bit resolution*/
this.genrand_res53 = function () {
var a = ((new Int32(_genrand_int32())).rshiftl(5)).getValue();
var b = ((new Int32(_genrand_int32())).rshiftl(6)).getValue();
return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
};
/* These real versions are due to Isaku Wada, 2002/01/09 added */
}
The corresponding Python implementation is here:
class mersenne_rng(object):
def __init__(self, seed = 5489):
self.state = [0]*624
self.f = 1812433253
self.m = 397
self.u = 11
self.s = 7
self.b = 0x9D2C5680
self.t = 15
self.c = 0xEFC60000
self.l = 18
self.index = 624
self.lower_mask = (1<<31)-1
self.upper_mask = 1<<31
# update state
self.state[0] = seed
for i in range(1,624):
self.state[i] = self.int_32(self.f*(self.state[i-1]^(self.state[i-1]>>30)) + i)
def twist(self):
for i in range(624):
temp = self.int_32((self.state[i]&self.upper_mask)+(self.state[(i+1)%624]&self.lower_mask))
temp_shift = temp>>1
if temp%2 != 0:
temp_shift = temp_shift^0x9908b0df
self.state[i] = self.state[(i+self.m)%624]^temp_shift
self.index = 0
def get_random_number(self):
if self.index >= 624:
self.twist()
y = self.state[self.index]
y = y^(y>>self.u)
y = y^((y<<self.s)&self.b)
y = y^((y<<self.t)&self.c)
y = y^(y>>self.l)
self.index+=1
return self.int_32(y)
def int_32(self, number):
return int(0xFFFFFFFF & number)
if __name__ == "__main__":
rng = mersenne_rng(1131464071)
for i in range(10):
print rng.get_random_number()
Anyone know of a good snippet of JavaScript code to convert HEX encoded strings to base64 encoded strings?
If you're working in Node or using Browserify, you can use
var base64String = Buffer.from(hexString, 'hex').toString('base64')
or
var hexString = Buffer.from(base64String, 'base64').toString('hex')
The excellent comment by #dandavis is modified by StackOverflow, and has some weird hidden characters.
Here it is as one liner :
btoa("a6b580481008e60df9350de170b7e728".match(/\w{2}/g).map(function(a){return String.fromCharCode(parseInt(a, 16));} ).join(""))
or :
function hexToBase64(hexstring) {
return btoa(hexstring.match(/\w{2}/g).map(function(a) {
return String.fromCharCode(parseInt(a, 16));
}).join(""));
}
hexToBase64("a6b580481008e60df9350de170b7e728");
Both return :
"prWASBAI5g35NQ3hcLfnKA=="
Note that the hex string should have an even length :
hexToBase64("00");
// => "AA=="
hexToBase64("000");
// => "AA=="
if (!window.atob) {
var tableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var table = tableStr.split("");
window.atob = function (base64) {
if (/(=[^=]+|={3,})$/.test(base64)) throw new Error("String contains an invalid character");
base64 = base64.replace(/=/g, "");
var n = base64.length & 3;
if (n === 1) throw new Error("String contains an invalid character");
for (var i = 0, j = 0, len = base64.length / 4, bin = []; i < len; ++i) {
var a = tableStr.indexOf(base64[j++] || "A"), b = tableStr.indexOf(base64[j++] || "A");
var c = tableStr.indexOf(base64[j++] || "A"), d = tableStr.indexOf(base64[j++] || "A");
if ((a | b | c | d) < 0) throw new Error("String contains an invalid character");
bin[bin.length] = ((a << 2) | (b >> 4)) & 255;
bin[bin.length] = ((b << 4) | (c >> 2)) & 255;
bin[bin.length] = ((c << 6) | d) & 255;
};
return String.fromCharCode.apply(null, bin).substr(0, bin.length + n - 4);
};
window.btoa = function (bin) {
for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) {
var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++);
if ((a | b | c) > 255) throw new Error("String contains an invalid character");
base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] +
(isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) +
(isNaN(b + c) ? "=" : table[c & 63]);
}
return base64.join("");
};
}
function hexToBase64(str) {
return btoa(String.fromCharCode.apply(null,
str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))
);
}
function base64ToHex(str) {
for (var i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
var tmp = bin.charCodeAt(i).toString(16);
if (tmp.length === 1) tmp = "0" + tmp;
hex[hex.length] = tmp;
}
return hex.join(" ");
}
I liked the approach from #eric-duminil nevertheless the solution below - avoiding regex - is ~2x faster.
Browser:
function hexToBase64(hexStr) {
return btoa([...hexStr].reduce((acc, _, i) =>
acc += !(i - 1 & 1) ? String.fromCharCode(parseInt(hexStr.substring(i - 1, i + 1), 16)) : ""
,""));
}
OR
// even a bit faster
function hexToBase64(hexStr) {
let base64 = "";
for(let i = 0; i < hexStr.length; i++) {
base64 += !(i - 1 & 1) ? String.fromCharCode(parseInt(hexStr.substring(i - 1, i + 1), 16)) : ""
}
return btoa(base64);
}
Node:
const base64 = Buffer.from(hexStr, 'hex').toString('base64');
Large strings, no btoa
Solution below is good for large strings - if you want to get bytes from base64 then look HERE
function bytesArrToBase64(arr) {
const abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // base64 alphabet
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = arr.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(arr[3*i]) + bin(c1? 0:arr[3*i+1]) + bin(c2? 0:arr[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
return result;
}
function hexToBytes(hexString) {
return hexString.match(/.{1,2}/g).map(x=> +('0x'+x));
}
// ---------
// TEST
// ---------
let hexString = "a6b580481008e60df9350de170b7e728";
let bytes = hexToBytes(hexString);
let base64 = bytesArrToBase64(bytes);
console.log(base64);