aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-06-06 09:13:54 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-06-06 09:13:54 +0200
commitab0db69a7b11ba1a9dc52d50c94e9e321b70ebf0 (patch)
tree76be6a8b1596ea1e74dbf7085b85214c2c83e69a
parent961c3470d7e9bdf5efbe0509f586be75d7052d8b (diff)
downloademscripten-tutorial-ab0db69a7b11ba1a9dc52d50c94e9e321b70ebf0.tar.gz
emscripten-tutorial-ab0db69a7b11ba1a9dc52d50c94e9e321b70ebf0.zip
Added example 6
Diffstat (limited to '')
-rwxr-xr-x05_callback/build.sh2
-rwxr-xr-x06_storage/build.sh9
-rw-r--r--06_storage/index.html19
-rw-r--r--06_storage/init_idbfs.js16
-rw-r--r--06_storage/mime.txt1
-rw-r--r--06_storage/primes.c94
-rw-r--r--06_storage/primes.h12
-rwxr-xr-x06_storage/run-server.sh6
-rw-r--r--06_storage/script.mjs20
-rw-r--r--06_storage/storage.c28
-rw-r--r--06_storage/storage.h7
-rw-r--r--06_storage/worker.mjs22
-rw-r--r--README.md13
13 files changed, 248 insertions, 1 deletions
diff --git a/05_callback/build.sh b/05_callback/build.sh
index 998ebd1..76bf5ad 100755
--- a/05_callback/build.sh
+++ b/05_callback/build.sh
@@ -2,7 +2,7 @@
2 2
3mkdir -p build 3mkdir -p build
4emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \ 4emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \
5 -pthread -sPTHREAD_POOL_SIZE=16 \
5 -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString \ 6 -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString \
6 -sALLOW_TABLE_GROWTH \ 7 -sALLOW_TABLE_GROWTH \
7 -pthread -sPTHREAD_POOL_SIZE=16 \
8 -o build/primes.mjs primes.c 8 -o build/primes.mjs primes.c
diff --git a/06_storage/build.sh b/06_storage/build.sh
new file mode 100755
index 0000000..a477cc5
--- /dev/null
+++ b/06_storage/build.sh
@@ -0,0 +1,9 @@
1#!/bin/sh
2
3mkdir -p build
4emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \
5 -pthread -sPTHREAD_POOL_SIZE=16 \
6 -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString \
7 -sALLOW_TABLE_GROWTH \
8 -lidbfs.js --pre-js init_idbfs.js -sINITIAL_MEMORY=272629760 \
9 -o build/primes.mjs primes.c storage.c
diff --git a/06_storage/index.html b/06_storage/index.html
new file mode 100644
index 0000000..a4ba674
--- /dev/null
+++ b/06_storage/index.html
@@ -0,0 +1,19 @@
1<!doctype html>
2<html lang="en-US">
3<head>
4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width" />
6 <title>Primes in a range</title>
7 <script src="./script.mjs" type="module" defer></script>
8</head>
9
10<body>
11 <label for="aInput">Lower bound (included)</label><br />
12 <input id="aInput" name="aInput" /> x <br />
13 <label for="bInput">Upper bound (excluded)</label><br />
14 <input id="bInput" name="bInput" /> <br />
15 <button id="goButton" disabled>Loading, please wait...</button> <br />
16 <p id="resultText"></p>
17</body>
18
19</html>
diff --git a/06_storage/init_idbfs.js b/06_storage/init_idbfs.js
new file mode 100644
index 0000000..82483f4
--- /dev/null
+++ b/06_storage/init_idbfs.js
@@ -0,0 +1,16 @@
1Module['preRun'] = [
2 async () => {
3 const dir = "/assets";
4
5 FS.mkdir(dir);
6 FS.mount(IDBFS, { autoPersist: true }, dir);
7
8 Module.fileSystemLoaded = new Promise((resolve, reject) => {
9 FS.syncfs(true, (err) => {
10 if (err) reject(err);
11 else resolve(true);
12 });
13 });
14
15 }
16];
diff --git a/06_storage/mime.txt b/06_storage/mime.txt
new file mode 100644
index 0000000..6a9a425
--- /dev/null
+++ b/06_storage/mime.txt
@@ -0,0 +1 @@
text/javascript mjs
diff --git a/06_storage/primes.c b/06_storage/primes.c
new file mode 100644
index 0000000..4a17e8d
--- /dev/null
+++ b/06_storage/primes.c
@@ -0,0 +1,94 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdbool.h>
4#include <pthread.h>
5
6#include "primes.h"
7#include "storage.h"
8
9#define NTHREADS 16
10#define INDEX(i) ((i) >> 3)
11#define MASK(i) (unsigned char)(1 << ((i) % 8))
12
13unsigned char primes_table[TABLESIZE];
14struct interval { int low; int high; int count; };
15
16void *pthread_routine(void *);
17int primes_in_range(int, int, void (*)(const char *));
18void set_nonprime(unsigned char *, int);
19bool isprime(const unsigned char *, int);
20
21int primes_in_range(int low, int high, void (*log)(const char *)) {
22 static bool table_is_loaded = false;
23
24 pthread_t threads[NTHREADS];
25 struct interval args[NTHREADS];
26
27 if (low < 0 || high < low)
28 return 0;
29
30 if (!table_is_loaded) {
31 if (!read_table(primes_table)) {
32 generate_primes(primes_table, log);
33 if (!store_table(primes_table))
34 log("Error storing table to indexed DB");
35 }
36
37 table_is_loaded = true;
38 }
39
40 int interval_size = (high-low)/NTHREADS + 1;
41 for (int i = 0; i < NTHREADS; i++) {
42 args[i].low = low + i*interval_size;
43 args[i].high = args[i].low + interval_size;
44 pthread_create(&threads[i], NULL, pthread_routine, &args[i]);
45 }
46
47 log("All threads have started, computing...");
48
49 int result = 0;
50 for (int i = 0; i < NTHREADS; i++) {
51 pthread_join(threads[i], NULL);
52 result += args[i].count;
53 }
54
55 return result;
56}
57
58void set_nonprime(unsigned char *table, int n) {
59 table[INDEX(n)] &= ~MASK(n);
60}
61
62bool isprime(const unsigned char *table, int n) {
63 return table[INDEX(n)] & MASK(n);
64}
65
66void generate_primes(unsigned char *table, void (*log)(const char *)) {
67 memset(table, 0xFF, TABLESIZE);
68 set_nonprime(table, 0);
69 set_nonprime(table, 1);
70 for (long long i = 0; i < MAX_PRIME; i++) {
71 if (i % 100000000 == 0 && log != NULL) {
72 char msg[200];
73 sprintf(msg,
74 "Could not read table of primes, generating it\n"
75 "Done %llu / %d", i, MAX_PRIME);
76 log(msg);
77 }
78 if (!isprime(table, i))
79 continue;
80 for (long long int j = 2*i; j < MAX_PRIME; j += i)
81 set_nonprime(table, j);
82 }
83}
84
85void *pthread_routine(void *arg) {
86 struct interval *interval = arg;
87
88 interval->count = 0;
89 for (int i = interval->low; i < interval->high; i++)
90 if (isprime(primes_table, i))
91 interval->count++;
92
93 return NULL;
94}
diff --git a/06_storage/primes.h b/06_storage/primes.h
new file mode 100644
index 0000000..d9006f7
--- /dev/null
+++ b/06_storage/primes.h
@@ -0,0 +1,12 @@
1#ifndef PRIMES_H
2#define PRIMES_H
3
4#include <limits.h>
5
6#define MAX_PRIME INT_MAX
7#define TABLESIZE (1 << 28)
8
9int primes_in_range(int, int, void (*)(const char *));
10void generate_primes(unsigned char *, void (*)(const char *));
11
12#endif
diff --git a/06_storage/run-server.sh b/06_storage/run-server.sh
new file mode 100755
index 0000000..3a526f3
--- /dev/null
+++ b/06_storage/run-server.sh
@@ -0,0 +1,6 @@
1#!/bin/sh
2
3darkhttpd . \
4 --mimetypes mime.txt \
5 --header 'Cross-Origin-Opener-Policy: same-origin' \
6 --header 'Cross-Origin-Embedder-Policy: require-corp'
diff --git a/06_storage/script.mjs b/06_storage/script.mjs
new file mode 100644
index 0000000..1238371
--- /dev/null
+++ b/06_storage/script.mjs
@@ -0,0 +1,20 @@
1var aInput = document.getElementById("aInput");
2var bInput = document.getElementById("bInput");
3var button = document.getElementById("goButton");
4var resultText = document.getElementById("resultText");
5
6var worker = new Worker("./worker.mjs", { type: "module" });
7
8button.addEventListener("click", () => worker.postMessage({
9 a: Number(aInput.value),
10 b: Number(bInput.value)
11}));
12
13worker.onmessage = (e) => {
14 if (e.data.type == "response")
15 resultText.innerText = e.data.message;
16 else if (e.data.type == "readySignal") {
17 button.disabled = false;
18 button.innerText = "Compute"
19 }
20};
diff --git a/06_storage/storage.c b/06_storage/storage.c
new file mode 100644
index 0000000..7542022
--- /dev/null
+++ b/06_storage/storage.c
@@ -0,0 +1,28 @@
1#include <stdio.h>
2#include <stdbool.h>
3
4#include "primes.h"
5
6#define FILENAME "/assets/primes_table"
7
8bool read_table(unsigned char *table) {
9 FILE *f = fopen(FILENAME, "rb");
10 if (f == NULL)
11 return false;
12
13 int b = fread(table, TABLESIZE, 1, f);
14 fclose(f);
15
16 return b == 1;
17}
18
19bool store_table(const unsigned char *table) {
20 FILE *f = fopen(FILENAME, "wb");
21 if (f == NULL)
22 return false;
23
24 int b = fwrite(table, TABLESIZE, 1, f);
25 fclose(f);
26
27 return b == 1;
28}
diff --git a/06_storage/storage.h b/06_storage/storage.h
new file mode 100644
index 0000000..ff0afae
--- /dev/null
+++ b/06_storage/storage.h
@@ -0,0 +1,7 @@
1#ifndef STORAGE_H
2#define STORAGE_H
3
4bool read_table(unsigned char *);
5bool store_table(const unsigned char *);
6
7#endif
diff --git a/06_storage/worker.mjs b/06_storage/worker.mjs
new file mode 100644
index 0000000..929fabc
--- /dev/null
+++ b/06_storage/worker.mjs
@@ -0,0 +1,22 @@
1import Primes from "./build/primes.mjs";
2
3var primes = await Primes();
4
5const logPtr = primes.addFunction((cstr) => {
6 const str = primes.UTF8ToString(cstr);
7 console.log(str);
8 postMessage({ message: str });
9}, "vp");
10
11onmessage = (e) => {
12 const count = primes._primes_in_range(e.data.a, e.data.b, logPtr);
13 postMessage({
14 type: "response",
15 message: "There are " + count + " primes between " +
16 e.data.a + " and " + e.data.b
17 });
18};
19
20primes.fileSystemLoaded.then(() => {
21 postMessage({ type: "readySignal" });
22});
diff --git a/README.md b/README.md
index 582cd4d..84b0066 100644
--- a/README.md
+++ b/README.md
@@ -75,3 +75,16 @@ thread by using a web worker.
75## 5. Callback functions 75## 5. Callback functions
76 76
77In `05_callback` the example is extended to include a callback function. 77In `05_callback` the example is extended to include a callback function.
78
79## 6. Storage
80
81In `06_storage` we show how to use Emscripten's
82[file system API](https://emscripten.org/docs/api_reference/Filesystem-API.html)
83using as backend the
84[indexed DB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API).
85This example only works in the browser, as other runtimes (such as
86Node.js) require different backends.
87
88In this example we make use for the first time of the `--pre-js` option of
89the compiler to include custom JavaScript code that is run when our module
90loads. Moreover, the C code is split into multiple files for convenience.

Generated with cgit - Back to sebastiano.tronto.net