commit 2a8e7aeeef99161b6f0378ac14e297c23ca6227b Author: Sebastiano Tronto <sebastiano@tronto.net> Date: Wed, 4 Jun 2025 11:55:34 +0200 First 4 examples Diffstat:
29 files changed, 288 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +*/build diff --git a/00_hello_world/build.sh b/00_hello_world/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir -p build +emcc -o build/index.html hello.c diff --git a/00_hello_world/hello.c b/00_hello_world/hello.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +int main() { + printf("Hello, web!\n"); +} diff --git a/00_hello_world/run-node.sh b/00_hello_world/run-node.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node build/index.js diff --git a/00_hello_world/run-server.sh b/00_hello_world/run-server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +darkhttpd build diff --git a/01_library/build.sh b/01_library/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir -p build +emcc -sEXPORTED_FUNCTIONS=_multiply -o build/library.js library.c diff --git a/01_library/index.html b/01_library/index.html @@ -0,0 +1,17 @@ +<!doctype html> +<html lang="en-US"> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + <title>Multiply two numbers</title> + <script src="./build/library.js" defer></script> + <script src="./script.js" defer></script> +</head> + +<body> + <input id="aInput" /> x <br /> + <input id="bInput" /> <button id="goButton">=</button> <br /> + <p id="resultText"></p> +</body> + +</html> diff --git a/01_library/library.c b/01_library/library.c @@ -0,0 +1,3 @@ +int multiply(int a, int b) { + return a * b; +} diff --git a/01_library/program.js b/01_library/program.js @@ -0,0 +1,6 @@ +var library = require("./build/library.js"); + +library.onRuntimeInitialized = () => { + const result = library._multiply(6, 7); + console.log("The answer is " + result); +}; diff --git a/01_library/run-node.sh b/01_library/run-node.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node program.js diff --git a/01_library/run-server.sh b/01_library/run-server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +darkhttpd . diff --git a/01_library/script.js b/01_library/script.js @@ -0,0 +1,10 @@ +var aInput = document.getElementById("aInput"); +var bInput = document.getElementById("bInput"); +var button = document.getElementById("goButton"); +var resultText = document.getElementById("resultText"); + +button.addEventListener("click", () => { + var a = Number(aInput.value); + var b = Number(bInput.value); + resultText.innerText = Module._multiply(a, b); +}); diff --git a/02_library_modularized/build.sh b/02_library_modularized/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +mkdir -p build +emcc -sEXPORTED_FUNCTIONS=_multiply -sMODULARIZE -sEXPORT_NAME=MyLibrary \ + -o build/library.mjs library.c diff --git a/02_library_modularized/index.html b/02_library_modularized/index.html @@ -0,0 +1,16 @@ +<!doctype html> +<html lang="en-US"> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + <title>Multiply two numbers</title> + <script src="./script.mjs" type="module" defer></script> +</head> + +<body> + <input id="aInput" /> x <br /> + <input id="bInput" /> <button id="goButton">=</button> <br /> + <p id="resultText"></p> +</body> + +</html> diff --git a/02_library_modularized/library.c b/02_library_modularized/library.c @@ -0,0 +1,3 @@ +int multiply(int a, int b) { + return a * b; +} diff --git a/02_library_modularized/mime.txt b/02_library_modularized/mime.txt @@ -0,0 +1 @@ +text/javascript mjs diff --git a/02_library_modularized/program.mjs b/02_library_modularized/program.mjs @@ -0,0 +1,6 @@ +import MyLibrary from "./build/library.mjs"; + +var myLibraryInstance = await MyLibrary(); + +const result = myLibraryInstance._multiply(6, 7); +console.log("The answer is " + result); diff --git a/02_library_modularized/run-node.sh b/02_library_modularized/run-node.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node program.mjs diff --git a/02_library_modularized/run-server.sh b/02_library_modularized/run-server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +darkhttpd . --mimetypes mime.txt diff --git a/02_library_modularized/script.mjs b/02_library_modularized/script.mjs @@ -0,0 +1,14 @@ +import MyLibrary from "./build/library.mjs"; + +var myLibraryInstance = await MyLibrary(); + +var aInput = document.getElementById("aInput"); +var bInput = document.getElementById("bInput"); +var button = document.getElementById("goButton"); +var resultText = document.getElementById("resultText"); + +button.addEventListener("click", () => { + var a = Number(aInput.value); + var b = Number(bInput.value); + resultText.innerText = myLibraryInstance._multiply(a, b); +}); diff --git a/03_threads/build.sh b/03_threads/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +mkdir -p build +emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \ + -pthread -sPTHREAD_POOL_SIZE=16 \ + -o build/primes.mjs primes.c diff --git a/03_threads/index.html b/03_threads/index.html @@ -0,0 +1,19 @@ +<!doctype html> +<html lang="en-US"> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + <title>Multiply two numbers</title> + <script src="./script.mjs" type="module" defer></script> +</head> + +<body> + <label for="aInput">Lower bound (included)</label><br /> + <input id="aInput" name="aInput" /> x <br /> + <label for="bInput">Upper bound (excluded)</label><br /> + <input id="bInput" name="bInput" /> <br /> + <button id="goButton">Compute</button> <br /> + <p id="resultText"></p> +</body> + +</html> diff --git a/03_threads/mime.txt b/03_threads/mime.txt @@ -0,0 +1 @@ +text/javascript mjs diff --git a/03_threads/primes.c b/03_threads/primes.c @@ -0,0 +1,53 @@ +#include <stdbool.h> +#include <pthread.h> + +#define NTHREADS 16 + +bool isprime(int); +void *pthread_routine(void *); + +struct interval { int low; int high; int count; }; + +int primes_in_range(int low, int high) { + pthread_t threads[NTHREADS]; + struct interval args[NTHREADS]; + + if (low < 0 || high < low) + return 0; + + int interval_size = (high-low)/NTHREADS + 1; + for (int i = 0; i < NTHREADS; i++) { + args[i].low = low + i*interval_size; + args[i].high = args[i].low + interval_size; + pthread_create(&threads[i], NULL, pthread_routine, &args[i]); + } + + int result = 0; + for (int i = 0; i < NTHREADS; i++) { + pthread_join(threads[i], NULL); + result += args[i].count; + } + + return result; +} + +bool isprime(int n) { + if (n < 2) + return false; + + for (int i = 2; i*i <= n; i++) + if (n % i == 0) + return false; + return true; +} + +void *pthread_routine(void *arg) { + struct interval *interval = arg; + + interval->count = 0; + for (int i = interval->low; i < interval->high; i++) + if (isprime(i)) + interval->count++; + + return NULL; +} diff --git a/03_threads/program.mjs b/03_threads/program.mjs @@ -0,0 +1,6 @@ +import Primes from "./build/primes.mjs" + +var primes = await Primes(); + +const count = primes._primes_in_range(1, 100); +console.log("There are " + count + " primes betwees 1 and 100"); diff --git a/03_threads/run-node.sh b/03_threads/run-node.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node program.mjs diff --git a/03_threads/run-server.sh b/03_threads/run-server.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +darkhttpd . \ + --mimetypes mime.txt \ + --header 'Cross-Origin-Opener-Policy: same-origin' \ + --header 'Cross-Origin-Embedder-Policy: require-corp' diff --git a/03_threads/script.mjs b/03_threads/script.mjs @@ -0,0 +1,16 @@ +import Primes from "./build/primes.mjs"; + +var primes = await Primes(); + +var aInput = document.getElementById("aInput"); +var bInput = document.getElementById("bInput"); +var button = document.getElementById("goButton"); +var resultText = document.getElementById("resultText"); + +button.addEventListener("click", () => { + var a = Number(aInput.value); + var b = Number(bInput.value); + const count = primes._primes_in_range(a, b); + resultText.innerText = "There are " + count + " primes between " + + a + " and " + b; +}); diff --git a/README.md b/README.md @@ -0,0 +1,65 @@ +# How to port a complex C codebase to the web + +*This repository is still work in progress* + +This repository contains the source code for the examples discussed in +a post walkthrough that is going to appear shortly on my website. +It will be linked here once published. + +Each sub-folder is a self-contained example of a C program (or library) +that can be compiled to [WebAssembly](https://webassembly.org/) using +[Emscripten](https://emscripten.org), and some JavaScript and HTML code +that can be used to run the C or C++ code in a web page or in a JavaScript +runtime such as [Node.js](https://nodejs.org). Following these steps in +order will walk you through the process of deploying a complex C or C++ +program (including multithreading, persistent storage, callback functions +and so on) as a web app. + +Besides the source files, each folder contains a few one-line shell +scripts, for convenience: + +* `build.sh`: to build the C / C++ code with Emscripten. +* `run-node.sh`: to run an example program in Node.js. +* `run-server.sh`: to start a web server on + [localhost:8080](http://localhost:8080) running an example web page + (require darkhttpd, see below). + +The examples have been tested only on Linux, but should work on any +UNIX system, and should be easy to adapt to Windows or other OSes. +Pull requests are welcome. + +## Prerequisites + +In order to follow this tutorial, you are going to need the following: + +1. [Emscripten](https://emscripten.org) +2. A web server to run locally, such as + [darkhttpd](https://github.com/emikulic/darkhttpd) +3. [Node.js](https://nodejs.org) (this is optional, since a version + of Node.js is distributed together with emscripten). + +## 0. Hello world + +The folder `00_hello_world` contains the simplest possible example. + +## 1. Building a library + +A common use case for building C or C++ code to WebAssembly is using some +high-performance library in a web app. In this situation, the code you +want to build does not have a `main()` entry point, but instead its public +functions are called from JavaScript. Motivated by this, in the folder +`01_library` you'll find an extremely simple C library (with only one +one-line function), and some JavaScript code to run it from a web page. + +## 2. Making it a module + +In `02_library_modularized` we review the previous example and we build it +into a +[module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). The result is the same, but more convenient to use. + +## 3. Multi-threading + +In `03_threads` we build a more complicated example based on +[pthreads](https://en.wikipedia.org/wiki/Pthreads). To run this +example, the web server has to be configured to provide the correct +`Cross-Origin-*` headers, see `03_threads/run-server.sh` for details.