Decompressing byte array compressed with LZ4 in JavaScript - javascript
I'm trying to decompress a byte array in JavaScript with lz4. I tried to implement the example in the npmjs page. I already have the data in compressed format, thus compressing it again leads to unwanted results.
I have tried:
var input = Buffer.from(this.bytes);
var output = Buffer.alloc( lz4.encodeBound(input.length) );
var uncompressed = Buffer.alloc(input.length);
var uncompressedSize = lz4.decodeBlock(output, uncompressed);
uncompressed = uncompressed.slice(0, uncompressedSize);
console.log( "uncompressed data", uncompressed);
Which leads to an array of 0's. I have confirmed that the data I pass (this.bytes) is a valid array of bytes. Ex: [f1,09,01,54,00,00,00,00,46,00,00,00,53,07,00,00,00,6f,66,66,73,65,74,58,4e,00,01,00,f1,35,2a,c0,53,08,00,00,00,69,74,65,6d,49,63,6f,6e,53,1f,00,00,00,57,65,61,70,6f,6e,45,6e,63,68,61,6e,74,6d,65,6e,74,5f,53,70,65,61,72,30,32,5f,4c,61,72,67,65,53,0b,00,00,00,53,63,72,65,65,6e,57,69,64,74,68,4e,49,00,55,00,9e,40,53,06,5f,00,01,73,00,f1,04,02,00,00,00,53,01,00]
I'd appreciate any help or suggestions.
Edit:
I have tried to directly decode the byte array:
console.log(LZ4.decode(this.bytes.join("")));
This leads to this error:
Uncaught (in promise) Error: Invalid magic number: 39303166 #0
emit_Error http://localhost:8080/javascripts/lz4.min.js:8
read_MagicNumber http://localhost:8080/javascripts/lz4.min.js:8
_main http://localhost:8080/javascripts/lz4.min.js:8
_transform http://localhost:8080/javascripts/lz4.min.js:8
_read http://localhost:8080/javascripts/lz4.min.js:8
_write http://localhost:8080/javascripts/lz4.min.js:8
b http://localhost:8080/javascripts/lz4.min.js:8
s http://localhost:8080/javascripts/lz4.min.js:8
write http://localhost:8080/javascripts/lz4.min.js:8
end http://localhost:8080/javascripts/lz4.min.js:8
LZ4_uncompress http://localhost:8080/javascripts/lz4.min.js:8
construct http://localhost:8080/javascripts/readSaveFile.js:194
Upon researching this issue, I came across this issue. I could not find the function lz4.createDecoderStream.prototype.uncompressBlock
I then tried an example from the node-lz4 github repo:
var compressed = Buffer.from(this.lua_state);
var uncompressedBlock = Buffer.alloc(compressed.length*10);
var n = LZ4.decodeBlock(compressed, uncompressedBlock);
uncompressedBlock = uncompressedBlock.slice(0,n);
console.log(uncompressedBlock);
Which returns, you guessed it, an array of 0's. I'm completely stuck right now and again appreciate any help.
The problem was that my input array was not an array of integers, so I just converted it into an UInt8Array with this function:
function convert_byte_array_to_int_array(array) {
let output_array = [];
array.forEach(byte => {
output_array.push(parseInt(byte, 16));
});
return output_array;
}
And used it in the package's example like this:
var input = convert_byte_array_to_int_array(this.bytes);
console.log(input);
var uncompressed = Buffer.alloc(input.length);
var uncompressedSize = LZ4.decodeBlock(input, uncompressed);
uncompressed = uncompressed.slice(0, uncompressedSize);
console.log( "uncompressed data", uncompressed );
Uncompressed data that was output to the console was the data I was looking for. Thank you for your help.
Related
Use Module.HEAP8.set Get messed data
I use emscripten compile the c code into asm.js my c code like this: int _call_main(char* p, int number)//p is the start arrary, number is the lengh I want to send a bytearray data to the c code, My Js code like this: var decode_encode_audio = Module.cwrap('_call_main', 'number', ['array','number']); ........ var byteArray = new Uint8Array(message.data);//audio data var offset = Module._malloc(70000); Module.HEAP8.set(byteArray, offset); decode_encode_audio(offset , byteArray.length); In the C code, I print the point p array, I got a messed data, the start part and the end part of p is correct(I save the file), However, In the middle part is messed.I replace the function(Module.HEAP8.set) with [Module.HEAPU8.set],also got error. I don't know why? I change the Js code : var decode_encode_audio = Module.cwrap('_call_main', 'number', ['number','number']);//not array ........ var byteArray = new Uint8Array(message.data);//audio data //var offset = Module._malloc(70000); //Module.HEAP8.set(byteArray, offset); decode_encode_audio(byteArray, byteArray.length); I can get correct data. Can anyone tell me what's wrong with "Module.HEAP8.set" method.
You are writing unsigned data from your Uint8Array into the signed view HEAP8. You should use the HEAPU8, which is used for unsigned data.
php base64_encode result not similar to javascript encode result
Code snippets: php: $shaVal = '59bc125840733ea828f42e276661b01e177f1414'; $enc = base64_encode(pack('H*', $shaVal)); echo $enc; //prints => WbwSWEBzPqgo9C4nZmGwHhd/FBQ= and in Javascipt I used buffer npm module let Buffer = require('buffer').Buffer; let shaVal = '59bc125840733ea828f42e276661b01e177f1414'; //function similar to php's pack() and returns binary data let bData = Buffer.from(shaVal, 'hex').toString(); console.log('bData ', bData) //encode with base64 let val64 = Buffer.from(bData, 'binary').toString('base64'); console.log('base 64 encode ', val64) //prints => Wf0SWEBzPv0o/S4nZmH9Hhd/FBQ= How can I get the exact same output printed by php? Note: Both options showing binary data as Y�X#s>�(�.'fa�
It's because PHP pack returns string, where as javascript buffer returns Array. This answer might help : https://stackoverflow.com/a/41962257/3086531
JSON.parse() - SyntaxError: Expected end of stream at char 2
In my project, I have written google spreadsheet script to decipher encrypted cell content using sjcl. But I failed. function encryptCell() { var masterKey = Browser.inputBox('Enter masterKey'); var spreadSheet = SpreadsheetApp.getActiveSpreadsheet(); var cell = spreadSheet.getActiveSheet().getActiveCell(); var input = cell.getValue(); var encJson = sjcl.encrypt(masterKey, input); /* {"iv":"4psT+LTIh/aT7WWv7Ye7qw==","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"PjSOiia9TCM=","ct":"3hwmBbwQ7y/fsjk="} */ var encStr = JSON.stringify(encJson); /* "{\"iv\":\"4psT+LTIh/aT7WWv7Ye7qw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"PjSOiia9TCM=\",\"ct\":\"3hwmBbwQ7y/fsjk=\"}" */ var encB64 = Utilities.base64Encode(encStr); Browser.msgBox(encB64); var rencStr = Utilities.base64Decode(encB64); /* 34,123,92,34,105,118,92,34,58,92,34,52,112,115,84,43,76,84,73,104,47,97,84,55,87,87,118,55,89,101,55,113,119,61,61,92,34,44,92,34,118,92,34,58,49,44,92,34,105,116,101,114,92,34,58,49,48,48,48,44,92,34,107,115,92,34,58,49,50,56,44,92,34,116,115,92,34,58,54,52,44,92,34,109,111,100,101,92,34,58,92,34,99,99,109,92,34,44,92,34,97,100,97,116,97,92,34,58,92,34,92,34,44,92,34,99,105,112,104,101,114,92,34,58,92,34,97,101,115,92,34,44,92,34,115,97,108,116,92,34,58,92,34,80,106,83,79,105,105,97,57,84,67,77,61,92,34,44,92,34,99,116,92,34,58,92,34,51,104,119,109,66,98,119,81,55,121,47,102,115,106,107,61,92,34,125,34 */ var rencJson = JSON.parse(rencStr); var rinput = sjcl.decrypt(masterKey, rencJson); Browser.msgBox(rinput); } Encryption is fine, which I can decrypt using this tool. Something is wrong with JSON.parse().
var rencStr = Utilities.base64Decode(encB64); /* 34,123,92,34,105,118,92,34,58,92,34,52,112,115,84,43,76,84,73,104,47,97,84,55,87,87,118,55,89,101,55,113,119,61,61,92,34,44,92,34,118,92,34,58,49,44,92,34,105,116,101,114,92,34,58,49,48,48,48,44,92,34,107,115,92,34,58,49,50,56,44,92,34,116,115,92,34,58,54,52,44,92,34,109,111,100,101,92,34,58,92,34,99,99,109,92,34,44,92,34,97,100,97,116,97,92,34,58,92,34,92,34,44,92,34,99,105,112,104,101,114,92,34,58,92,34,97,101,115,92,34,44,92,34,115,97,108,116,92,34,58,92,34,80,106,83,79,105,105,97,57,84,67,77,61,92,34,44,92,34,99,116,92,34,58,92,34,51,104,119,109,66,98,119,81,55,121,47,102,115,106,107,61,92,34,125,34 */ If you take a look, that's not JSON. did you possibly miss the JSON.stringify(); step for that one? You can't parse it if it's not JSON.
When decoding with Utilities.base64Decode() you have one extra step to follow if you want to get a string back. If you look at the Google Scripts reference it says that base64Decode returns a byte array and not a string. Looking at your code, your decode returns var rencStr = Utilities.base64Decode(encB64); /*34,123,92,34,105,118,92,34,58,92,34,52,112,115,84,43,76,84,73,104,47,97,84...*/ which is a numeric representation of your string in Unicode. If you run your return value through Utilities.newBlob(rencStr).getDataAsString() as the reference recommends, you will have your JSON that you can parse back into your encrypted string.
JavaScript: reading 3 bytes Buffer as an integer
Let's say I have a hex data stream, which I want to divide into 3-bytes blocks which I need to read as an integer. For example: given a hex string 01be638119704d4b9a I need to read the first three bytes 01be63 and read it as integer 114275. This is what I got: var sample = '01be638119704d4b9a'; var buffer = new Buffer(sample, 'hex'); var bufferChunk = buffer.slice(0, 3); var decimal = bufferChunk.readUInt32BE(0); The readUInt32BE works perfectly for 4-bytes data, but here I obviously get: RangeError: index out of range at checkOffset (buffer.js:494:11) at Buffer.readUInt32BE (buffer.js:568:5) How do I read 3-bytes as integer correctly?
If you are using node.js v0.12+ or io.js, there is buffer.readUIntBE() which allows a variable number of bytes: var decimal = buffer.readUIntBE(0, 3); (Note that it's readUIntBE for Big Endian and readUIntLE for Little Endian). Otherwise if you're on an older version of node, you will have to do it manually (check bounds first of course): var decimal = (buffer[0] << 16) + (buffer[1] << 8) + buffer[2];
I'm using this, if someone knows something wrong with it, please advise; const integer = parseInt(buffer.toString("hex"), 16)
you should convert three byte to four byte. function three(var sample){ var buffer = new Buffer(sample, 'hex'); var buf = new Buffer(1); buf[0] = 0x0; return Buffer.concat([buf, buffer.slice(0, 3)]).readUInt32BE(); } You can try this function.
Decompress gzip and zlib string in javascript
I want to get compress layer data from tmx file . Who knows libraries for decompress gzip and zlib string in javascript ? I try zlib but it doesn't work for me . Ex , layer data in tmx file is : <data encoding="base64" compression="zlib"> eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ== </data> My javascript code is var base64Data = "eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ=="; var compressData = atob(base64Data); var inflate = new Zlib.Inflate(compressData); var output = inflate.decompress(); It runs with displays message error "unsupported compression method" . But I try decompress with online tool as http://i-tools.org/gzip , it returns correct string.
Pako is a full and modern Zlib port. Here is a very simple example and you can work from there. Get pako.js and you can decompress byteArray like so: <html> <head> <title>Gunzipping binary gzipped string</title> <script type="text/javascript" src="pako.js"></script> <script type="text/javascript"> // Get datastream as Array, for example: var charData = [31,139,8,0,0,0,0,0,0,3,5,193,219,13,0,16,16,4,192,86,214,151,102,52,33,110,35,66,108,226,60,218,55,147,164,238,24,173,19,143,241,18,85,27,58,203,57,46,29,25,198,34,163,193,247,106,179,134,15,50,167,173,148,48,0,0,0]; // Turn number array into byte-array var binData = new Uint8Array(charData); // Pako magic var data = pako.inflate(binData); // Convert gunzipped byteArray back to ascii string: var strData = String.fromCharCode.apply(null, new Uint16Array(data)); // Output to console console.log(strData); </script> </head> <body> Open up the developer console. </body> </html> Running example: http://jsfiddle.net/9yH7M/ Alternatively you can base64 encode the array before you send it over as the Array takes up a lot of overhead when sending as JSON or XML. Decode likewise: // Get some base64 encoded binary data from the server. Imagine we got this: var b64Data = 'H4sIAAAAAAAAAwXB2w0AEBAEwFbWl2Y0IW4jQmziPNo3k6TuGK0Tj/ESVRs6yzkuHRnGIqPB92qzhg8yp62UMAAAAA=='; // Decode base64 (convert ascii to binary) var strData = atob(b64Data); // Convert binary string to character-number array var charData = strData.split('').map(function(x){return x.charCodeAt(0);}); // Turn number array into byte-array var binData = new Uint8Array(charData); // Pako magic var data = pako.inflate(binData); // Convert gunzipped byteArray back to ascii string: var strData = String.fromCharCode.apply(null, new Uint16Array(data)); // Output to console console.log(strData); Running example: http://jsfiddle.net/9yH7M/1/ To go more advanced, here is the pako API documentation.
I can solve my problem by zlib . I fix my code as below var base64Data = "eJztwTEBAAAAwqD1T20JT6AAAHgaCWAAAQ=="; var compressData = atob(base64Data); var compressData = compressData.split('').map(function(e) { return e.charCodeAt(0); }); var inflate = new Zlib.Inflate(compressData); var output = inflate.decompress();
For anyone using Ruby on Rails, who wants to send compressed encoded data to the browser, then uncompress it via Javascript on the browser, I've combined both excellent answers above into the following solution. Here's the Rails server code in my application controller which compresses and encodes a string before sending it the browser via a #variable to a .html.erb file: require 'zlib' require 'base64' def compressor (some_string) Base64.encode64(Zlib::Deflate.deflate(some_string)) end Here's the Javascript function, which uses pako.min.js: function uncompress(input_field){ base64data = document.getElementById(input_field).innerText; compressData = atob(base64data); compressData = compressData.split('').map(function(e) { return e.charCodeAt(0); }); binData = new Uint8Array(compressData); data = pako.inflate(binData); return String.fromCharCode.apply(null, new Uint16Array(data)); } Here's a javascript call to that uncompress function, which wants to unencode and uncompress data stored inside a hidden HTML field: my_answer = uncompress('my_hidden_field'); Here's the entry in the Rails application.js file to call pako.min.js, which is in the /vendor/assets/javascripts directory: //= require pako.min And I got the pako.min.js file from here: https://github.com/nodeca/pako/tree/master/dist All works at my end, anyway! :-)
I was sending data from a Python script and trying to decode it in JS. Here's what I had to do: Python import base64 import json import urllib.parse import zlib ... data_object = { '_id': '_id', ... } compressed_details = base64.b64encode(zlib.compress(bytes(json.dumps(data_object), 'utf-8'))).decode("ascii") urlsafe_object = urllib.parse.quote(str(compressed_details))#.replace('%', '\%') # you likely don't need this last part final_URL = f'https://my.domain.com?data_object={urlsafe_object}' ... JS // npm install this import pako from 'pako'; ... const urlParams = new URLSearchParams(window.location.search); const data_object = urlParams.get('data_object'); if (data_object) { const compressedData = Uint8Array.from(window.atob(data_object), (c) => c.charCodeAt(0)); originalObject = JSON.parse(pako.inflate(compressedData, { to: 'string' })); }; ...