Skip to content

Instantly share code, notes, and snippets.

@palaniraja
Created July 7, 2024 23:36
Show Gist options
  • Save palaniraja/15f013e720c709c452a623e328934dcc to your computer and use it in GitHub Desktop.
Save palaniraja/15f013e720c709c452a623e328934dcc to your computer and use it in GitHub Desktop.
wasm file merger
cmake_minimum_required(VERSION 3.12)
project(concat_files)
set(CMAKE_CXX_STANDARD 14)
# set(CMAKE_CXX_COMPILER em++)
add_executable(concat_files main.cpp)
set_target_properties(concat_files PROPERTIES LINK_FLAGS "\
-s WASM=1 \
-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','FS','stringToUTF8','setValue'] \
-s EXPORTED_FUNCTIONS=['_malloc','_free','_concat_files'] \
-s FORCE_FILESYSTEM=1 \
-lidbfs.js \
")
<!DOCTYPE html>
<html>
<head>
<title>File Dropzone</title>
<link rel="icon" href="data:;base64,=">
<script src="build/concat_files.js"></script>
<style>
ul {
list-style-type: none;
}
li {
margin: 0.5em 0.5em;
}
#dropzone {
width: 300px;
height: 300px;
border: 1px solid black;
background-color: lightgray;
}
</style>
</head>
<body>
<ul>
<li> <div id="dropzone" style="">Drop files here</div>
<li> <button onclick="listFiles()">List Files</button>
<li> <textarea id="fileContent"></textarea>
<li> <button onclick="writeTextareaContent()">write to userinput.txt</button>
<li><button onclick="processFiles()">Process Files with wasm</button>
</ul>
<script>
var fileNameArray = []
window.addEventListener('load', function () {
console.log("window loaded")
let droppedFiles = [];
let dropzone = document.getElementById('dropzone');
dropzone.ondragover = function (e) {
e.preventDefault();
this.className = 'dropzone dragover';
return false;
};
dropzone.ondragleave = function (e) {
e.preventDefault();
this.className = 'dropzone';
return false;
};
dropzone.ondrop = function (e) {
e.preventDefault();
this.className = 'dropzone';
droppedFiles = e.dataTransfer.files;
console.log("dropped files: ", droppedFiles.length)
// Write each file to the Emscripten file system
let promises = Array.from(droppedFiles).map(file => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = function () {
let data = new Uint8Array(reader.result);
var fname = '/testdir/' + file.name
fileNameArray.push(fname)
FS.writeFile(fname, data);
resolve();
};
reader.onerror = function () {
reject(reader.error);
};
reader.readAsArrayBuffer(file);
});
});
// Wait for all files to be written
Promise.all(promises)
.then(() => console.log('All files written successfully'))
.catch(error => console.error('Error writing files:', error));
};
});
var fileName = '/testdir/userinput.txt'
// Assuming Module is the Emscripten module
Module['onRuntimeInitialized'] = function () {
console.log('onRuntimeInitialized')
FS.mkdir('/testdir');
// Module['FS'].mount(IDBFS, {root: '.'}, '/testdir')
// document.querySelector('button').addEventListener('click', processFiles);
}
function listFiles() {
var list = FS.readdir('/testdir');
console.log(list);
}
function writeTextareaContent() {
fileContent = document.querySelector("#fileContent").value;
let data = new TextEncoder().encode(fileContent);
FS.writeFile(fileName, data, { encoding: 'utf8' });
fileNameArray.push(fileName)
setTimeout(function () {
var contents = FS.readFile(fileName, { encoding: 'binary' });
var stringContents = new TextDecoder().decode(contents);
console.log(`content of ${fileName}`)
console.log(stringContents);
// Synchronize the filesystem
FS.syncfs(true, function (err) {
if (err) {
console.error('Failed to sync the filesystem:', err);
} else {
console.log('Filesystem synced successfully');
}
});
}, 300)
}
function processFiles() {
// var fileNameArray = [fileName]; // This should be an array of file names
var fileNamePtrs = new Array(fileNameArray.length);
for (var i = 0; i < fileNameArray.length; i++) {
var fileNamePtr = Module._malloc(fileNameArray[i].length + 1);
Module.stringToUTF8(fileNameArray[i], fileNamePtr, fileNameArray[i].length + 1);
fileNamePtrs[i] = fileNamePtr;
}
var fileNameArrayPtr = Module._malloc(fileNamePtrs.length * 4); // assumes 4 bytes per pointer
Module.HEAP32.set(fileNamePtrs, fileNameArrayPtr / 4); // Copy the array of pointers to the WebAssembly memory
var result = Module.ccall('concat_files', 'string', ['number', 'number'], [fileNameArray.length, fileNameArrayPtr]);
console.log(result);
// Free the memory allocated for the file names and the array of pointers
fileNamePtrs.forEach(p => Module._free(p));
Module._free(fileNameArrayPtr);
}
</script>
</html>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstring>
#include <emscripten.h>
extern "C" {
char* concat_files(int numFiles, char* filenames[]) {
std::stringstream buffer;
// buffer << "numFiles: " << numFiles << std::endl;
for (int i = 0; i < numFiles; i++) {
if (filenames[i] == nullptr || strlen(filenames[i]) == 0) {
buffer << " file: (empty)";
} else {
// buffer << " file: " << filenames[i] << std::endl;
std::ifstream file(filenames[i]);
if (!file.is_open()) {
buffer << " could not open file: " << filenames[i] << std::endl;
continue; // Skip to the next filename
}
buffer << file.rdbuf();
}
}
std::string str = buffer.str();
char* cstr = new char[str.length() + 1];
std::strcpy(cstr, str.c_str());
return cstr;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment