-
Notifications
You must be signed in to change notification settings - Fork 303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
zstd-codec support for decompressing files #382
base: master
Are you sure you want to change the base?
Changes from 1 commit
787880f
08b5fd6
a4c7c4e
65755ae
34a07ae
9816f3f
e311d62
b4c8df1
8ab6186
fffc0d4
d8a3095
9fd7296
5a5926e
33d942b
9bd2e54
8f7ecce
880ead2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,5 @@ CMakeCache.txt | |
*.clst | ||
*.snap | ||
node_modules | ||
build | ||
build | ||
*-lock.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
window.ZstdCodec = require('zstd-codec').ZstdCodec; | ||
require("zstd-codec/lib/module.js").run((binding) => {console.log("running", binding); window.binding = binding} ) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,26 +3,24 @@ | |
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
"-": "^0.0.1", | ||
"@testing-library/jest-dom": "^5.16.5", | ||
"@testing-library/react": "^13.4.0", | ||
"@testing-library/user-event": "^13.5.0", | ||
"fs": "^0.0.1-security", | ||
"g": "^2.0.1", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. really need "fs" ? |
||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-scripts": "^5.0.1", | ||
"web-vitals": "^2.1.4" | ||
"web-vitals": "^2.1.4", | ||
"zstd-codec": "^0.1.4" | ||
}, | ||
"scripts": { | ||
"start": "react-scripts start", | ||
"build": "react-scripts build", | ||
"build": "react-scripts build; browserify index.js -o build/bundle.js -t babelify --presets es2015; ", | ||
"test": "react-scripts test", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. supposed, need run browserify before react-script |
||
"eject": "react-scripts eject" | ||
}, | ||
"eslintConfig": { | ||
"extends": [ | ||
"react-app", | ||
"react-app/jest" | ||
] | ||
}, | ||
"browserslist": { | ||
"production": [ | ||
">0.2%", | ||
|
@@ -35,5 +33,8 @@ | |
"last 1 safari version" | ||
] | ||
}, | ||
"proxy": "http://localhost:5000" | ||
"devDependencies": { | ||
"@babel/core": "^7.22.9", | ||
"babelify": "^10.0.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'; | |
import './index.css'; | ||
import App from './App'; | ||
import reportWebVitals from './reportWebVitals'; | ||
// import * as Z from 'zstd-codec' | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed commented code |
||
const root = ReactDOM.createRoot(document.getElementById('root')); | ||
root.render( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
|
||
import {useEffect, useState} from 'react'; | ||
import {str_to_arr, ab_to_str} from '../helper/functions.js' | ||
import {CHUNK_SZ, BLK_SZ, CHUNK_TYPE_RAW, CHUNK_TYPE_DONT_CARE, build_sparse_header, build_chunk_header} from '../helper/sparse.js' | ||
|
||
const USBrequestParams = { filters: [{ vendorId: 8137, productId: 0x0152 }] }; | ||
|
||
const PACKET_SZ = 0x10000; | ||
const DATA_SZ = CHUNK_SZ * BLK_SZ; // in bytes | ||
|
||
const useDecompress = (flashFile) => { | ||
const [USBDevice, setUSBDevice] = useState(); | ||
const [flashProgress, setFlashProgress] = useState(); | ||
const [flashTotal, setFlashTotal] = useState(); | ||
|
||
useEffect (()=> { | ||
if (USBDevice) { | ||
USBDevice.open().then(()=>{ | ||
console.log(`Opened USB Device: ${USBDevice.productName}`); | ||
}, (err)=> { | ||
console.log(err) | ||
}) | ||
.then(()=> { | ||
if (flashFile) | ||
decompressFileToTransform(); | ||
}) | ||
} | ||
|
||
}, [USBDevice]) | ||
|
||
const requestUSBDevice = () => { // first thing called with button | ||
navigator.usb | ||
.requestDevice(USBrequestParams) | ||
.then((device) => { | ||
setUSBDevice(device); | ||
}) | ||
.catch((e) => { | ||
console.error(`There is no device. ${e}`); | ||
}); | ||
} | ||
|
||
async function preBoot () { | ||
await USBDevice.claimInterface(0); | ||
|
||
await send_data(await str_to_arr("UCmd:setenv fastboot_dev mmc"), "OKAY"); | ||
await send_data(await str_to_arr("UCmd:setenv mmcdev ${emmc_dev}"), "OKAY"); | ||
await send_data(await str_to_arr("UCmd:mmc dev ${emmc_dev}"), "OKAY"); | ||
|
||
console.log("preboot complete") | ||
} | ||
|
||
const processChunk = async(data, curr_len, i) => { | ||
console.log(data, curr_len, i); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. avoid log data, it is too big |
||
// ignore, fill with zeroes | ||
if (curr_len != data.length) { | ||
let fill_len = (Math.ceil(curr_len/BLK_SZ)*BLK_SZ - curr_len); | ||
// let fill = new Uint8Array(fill_len); | ||
data.set(curr_len, new Uint8Array(fill_len)); | ||
data = data.slice(0, curr_len+fill_len); | ||
} | ||
|
||
let hex_len = (data.length + 52).toString(16); // 52 comes from headers | ||
await send_data(await str_to_arr(`download:${hex_len}`), `DATA${hex_len}`); | ||
await send_headers(data.length, i); | ||
|
||
let offset=0; | ||
while (offset < data.length) { | ||
let packet_len = Math.min(PACKET_SZ, DATA_SZ - offset); | ||
await USBDevice.transferOut(1, data.slice(offset, offset + packet_len)); | ||
console.log("transferout", data.slice(offset, offset + packet_len)); | ||
offset += packet_len; | ||
} | ||
|
||
let result = await USBDevice.transferIn(1, 1048); | ||
if ("OKAY" !== await ab_to_str(result.data.buffer)) { | ||
throw new Error ("failed to send data:", await ab_to_str(result.data.buffer)) | ||
} | ||
|
||
await flash_all(); | ||
console.log("FLASH SUCCESS") | ||
} | ||
|
||
async function send_packet(raw_data) { | ||
await USBDevice.transferOut(1, raw_data); | ||
console.log("transferout", raw_data); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. avoid log data |
||
|
||
async function send_chunk_headers (chunk_len, i) { | ||
let hex_len = (chunk_len + 52).toString(16); // 52 comes from headers | ||
await send_data(await str_to_arr(`download:${hex_len}`), `DATA${hex_len}`); | ||
await send_headers(chunk_len, i); | ||
} | ||
|
||
async function send_headers(raw_data_bytelength, i) { | ||
let sparse = await build_sparse_header(raw_data_bytelength, i); | ||
let dont_care = await build_chunk_header(CHUNK_TYPE_DONT_CARE, raw_data_bytelength, i); | ||
let raw = await build_chunk_header(CHUNK_TYPE_RAW, raw_data_bytelength, i); | ||
|
||
let headers = new Uint8Array(52); | ||
headers.set(sparse, 0); | ||
headers.set(dont_care, 28); | ||
headers.set(raw, 40); | ||
|
||
await USBDevice.transferOut(1, headers); | ||
} | ||
|
||
async function send_flash () { | ||
let result = await USBDevice.transferIn(1, 1048); | ||
if ("OKAY" !== await ab_to_str(result.data.buffer)) { | ||
throw new Error ("failed to send data:", await ab_to_str(result.data.buffer)) | ||
} | ||
|
||
await flash_all(); | ||
} | ||
|
||
/* | ||
* data: string or arraybuffer | ||
* success_str: checks response of usb | ||
* Throws error if USB input does not match success_str | ||
*/ | ||
async function send_data(data, success_str) { | ||
await USBDevice.transferOut(1, data); | ||
let result = await USBDevice.transferIn(1, 1048); | ||
|
||
if (success_str !== await ab_to_str(result.data.buffer)) { | ||
throw new Error ("failed to send data:",await ab_to_str(result.data.buffer)) | ||
} | ||
} | ||
|
||
async function flash_all () { | ||
await send_data(await str_to_arr("flash:all"), "OKAY"); | ||
console.log("flash"); | ||
} | ||
|
||
const decompressFileToTransform = async(e) => { | ||
console.log(USBDevice); | ||
|
||
if (!flashFile) return; | ||
|
||
const file = flashFile; | ||
let buff = await file.arrayBuffer() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missed ";" |
||
let data = new Uint8Array(buff); | ||
let totalBytes = 0; | ||
|
||
console.log(data) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. avoid log data, which just for debug |
||
if (!window.binding) { | ||
console.log("no binding"); | ||
return; | ||
} | ||
|
||
const stream = new binding.ZstdDecompressStreamBinding(); | ||
const transform = new TransformStream(); | ||
const writer = transform.writable.getWriter(); | ||
|
||
const callback = (decompressed) => { | ||
totalBytes += decompressed.length; | ||
writer.write(decompressed); | ||
} | ||
|
||
if (!stream.begin()) { | ||
console.log("stream.begin() error"); | ||
return null; | ||
} | ||
|
||
console.log("start unzipping"); | ||
|
||
let i = 0; | ||
const size = 1024*1024*2; | ||
while (size*i < data.length) { | ||
let end = Math.min(size*(i+1), data.length); | ||
let slice = data.slice(size*i, end); | ||
|
||
if (!stream.transform(slice, callback)) { | ||
console.log(`stream.transform() error on slice ${size*i} to ${end}`); | ||
return null; | ||
} | ||
|
||
i++; | ||
} | ||
|
||
if (!stream.end(callback)) { | ||
console.log("stream.end() error"); | ||
return null; | ||
} | ||
|
||
console.log("finishing unzipping"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am little confused here, after you decompress whole data, then trigger read. did you check memory usage here. You can stop here to check how many memory used by browser. |
||
|
||
const readable = transform.readable; | ||
doFlash(readable, PACKET_SZ, totalBytes); | ||
} | ||
|
||
const doFlash = async(readable, size, totalSize) => { | ||
await preBoot(); | ||
|
||
const reader = readable.getReader(); | ||
|
||
let offset = 0; | ||
let sofar = new Uint8Array(size); | ||
let i=0; | ||
let totalBytes = 0; | ||
|
||
let i_last = Math.floor(totalSize/DATA_SZ); | ||
let last_len = totalSize - Math.floor(totalSize/DATA_SZ)*DATA_SZ; | ||
|
||
console.log(i_last, last_len); | ||
|
||
let isFirst = true; | ||
let bytesChunk = 0; | ||
|
||
reader.read().then(async function processText ({ done, value }) { | ||
// Result objects contain two properties: | ||
// done - true if the stream has already given you all its data. | ||
// value - some data. Always undefined when done is true. | ||
|
||
totalBytes += value.length; | ||
console.log(totalBytes); | ||
|
||
// Send chunk headers | ||
if (isFirst) { | ||
let send_len; | ||
if (i==i_last) { | ||
send_len = Math.ceil(last_len/BLK_SZ)*BLK_SZ | ||
} | ||
else { | ||
send_len = DATA_SZ; | ||
} | ||
await send_chunk_headers(send_len, i); | ||
console.log("Sent headers", send_len, i) | ||
|
||
isFirst = false; | ||
} | ||
|
||
while (offset + value.length >= size) { | ||
sofar.set(value.slice(0, size-offset), offset); // Whole packet is ready to send | ||
bytesChunk += size-offset; | ||
await send_packet(sofar); | ||
|
||
console.log("bytes", bytesChunk, i); | ||
|
||
if (bytesChunk == DATA_SZ) { | ||
await send_flash(); | ||
bytesChunk = 0; | ||
i++; | ||
isFirst = true; | ||
} | ||
value = value.slice(size-offset, value.length); | ||
offset = 0; | ||
} | ||
|
||
sofar.set(value, offset); | ||
offset += value.length; | ||
bytesChunk += value.length | ||
|
||
if (i==i_last && bytesChunk==last_len) { | ||
console.log("reached last send", i, bytesChunk); | ||
|
||
// Pad last packet with zeros | ||
let fill_len = Math.ceil(last_len/BLK_SZ)*BLK_SZ - last_len; | ||
sofar.set(new Uint8Array(fill_len), offset); | ||
sofar = sofar.slice(0, offset+fill_len); | ||
|
||
console.log(sofar); | ||
|
||
await send_packet(sofar); | ||
await send_flash(); | ||
} | ||
|
||
if (done) { | ||
console.log("stream done") | ||
return; | ||
} | ||
// Read some more, and call this function again | ||
return reader.read().then(processText); | ||
}); | ||
} | ||
|
||
return [{ | ||
requestUSBDevice, | ||
USBDevice, | ||
flashProgress, | ||
flashTotal, | ||
preBoot, | ||
processChunk, | ||
send_chunk_headers, | ||
send_packet, | ||
send_flash, | ||
flash_all, | ||
}] | ||
} | ||
|
||
export default useDecompress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's "-" means here?