aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md13
-rwxr-xr-xbuild34
-rw-r--r--web/adapter.cpp91
-rw-r--r--web/callback.js32
-rw-r--r--web/examples/solve.mjs12
-rw-r--r--web/http/index.html20
-rw-r--r--web/http/mime1
-rw-r--r--web/storage.cpp67
-rw-r--r--web/storage.h7
10 files changed, 247 insertions, 32 deletions
diff --git a/.gitignore b/.gitignore
index 810efe2..8b1a5d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,8 @@ runcpp
22runtest.js 22runtest.js
23runtest.wasm 23runtest.wasm
24web/*.wasm 24web/*.wasm
25web/http/*.wasm
26web/http/*.mjs
25web/nissy_web_module.* 27web/nissy_web_module.*
26runtool 28runtool
27tools/.DS_Store 29tools/.DS_Store
diff --git a/README.md b/README.md
index 4dd55b8..3f64e29 100644
--- a/README.md
+++ b/README.md
@@ -251,6 +251,19 @@ using [nodejs](https://nodejs.org):
251$ node web/examples/[filename] 251$ node web/examples/[filename]
252``` 252```
253 253
254An example web app running nissy can be found in the `web/http` folder.
255You can run a web server in that folder to check it out, but you need
256to set some extra headers to make it work. For example, if you are using
257[darkhttpd](https://github.com/emikulic/darkhttpd) you can start the server
258with the following command:
259
260```
261$ darkhttpd web/http/ \
262 --header 'Cross-Origin-Opener-Policy: same-origin' \
263 --header 'Cross-Origin-Embedder-Policy: require-corp' \
264 --mimetypes web/http/mime
265```
266
254## Cube format 267## Cube format
255 268
256This format is a "base 32" encoding of the cube. It is not meant to be 269This format is a "base 32" encoding of the cube. It is not meant to be
diff --git a/build b/build
index 0376656..b3f0800 100755
--- a/build
+++ b/build
@@ -129,9 +129,12 @@ CPPFLAGS="-std=c++20 -pthread"
129# Build flags for emscripten (WASM target) 129# Build flags for emscripten (WASM target)
130WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread" 130WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread"
131WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD" 131WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD"
132#WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \
133# -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \
134# -sNODERAWFS -sASYNCIFY -sLINKABLE -sEXPORT_ALL"
132WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \ 135WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \
133 -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \ 136 -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \
134 -sLINKABLE -sEXPORT_ALL" 137 -sASYNCIFY -sLINKABLE -sEXPORT_ALL"
135 138
136if (command -v "python3-config" >/dev/null 2>&1) ; then 139if (command -v "python3-config" >/dev/null 2>&1) ; then
137 PYTHON3_INCLUDES="$(python3-config --includes)" 140 PYTHON3_INCLUDES="$(python3-config --includes)"
@@ -207,7 +210,7 @@ odflags() {
207 210
208build_clean() { 211build_clean() {
209 run rm -rf -- *.o *.so *.a run runtest runtool runcpp \ 212 run rm -rf -- *.o *.so *.a run runtest runtool runcpp \
210 web/nissy_web_module.* 213 web/nissy_web_module.* web/*.wasm web/http/*.mjs web/http/*.wasm
211} 214}
212 215
213build_nissy() { 216build_nissy() {
@@ -233,7 +236,7 @@ build_sharedlib() {
233} 236}
234 237
235build_shell() { 238build_shell() {
236 build_nissy 239 build_nissy || exit 1
237 run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c 240 run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c
238} 241}
239 242
@@ -243,7 +246,7 @@ build_python() {
243 echo "Cannot build python module" 246 echo "Cannot build python module"
244 exit 1 247 exit 1
245 fi 248 fi
246 build_nissy 249 build_nissy || exit 1
247 run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \ 250 run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \
248 -o nissy_python_module.so nissy.o python/nissy_module.c 251 -o nissy_python_module.so nissy.o python/nissy_module.c
249} 252}
@@ -255,7 +258,7 @@ build_cpp() {
255 echo "usage: ./build cpp FILES" 258 echo "usage: ./build cpp FILES"
256 fi 259 fi
257 260
258 build_nissy 261 build_nissy || exit 1
259 run $CXX -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@ 262 run $CXX -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@
260 run ./runcpp 263 run ./runcpp
261} 264}
@@ -265,13 +268,20 @@ build_web() {
265 validate_threads "$THREADS" 268 validate_threads "$THREADS"
266 validate_arch "$ARCH" 269 validate_arch "$ARCH"
267 270
271 obj="nissy_web_module"
272
268 run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \ 273 run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \
269 -o nissy.o src/nissy.c 274 -o nissy.o src/nissy.c || exit 1
270 run $EMCC -lembind -lidbfs.js -lnodefs.js \ 275 #run $EMCC -lembind -lidbfs.js -lnodefs.js -lnoderawfs.js \
276 # $CPPFLAGS $(odflags) $WASMLINKFLAGS \
277 # --js-library web/callback.js -o web/"$obj".mjs \
278 # cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o
279 run $EMCC -lembind -lidbfs.js \
271 $CPPFLAGS $(odflags) $WASMLINKFLAGS \ 280 $CPPFLAGS $(odflags) $WASMLINKFLAGS \
272 --js-library web/callback.js \ 281 --js-library web/callback.js -o web/"$obj".mjs \
273 -o web/nissy_web_module.mjs \ 282 cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o || exit 1
274 cpp/nissy.cpp web/adapter.cpp nissy.o 283 cp web/"$obj".mjs web/http
284 cp web/"$obj".wasm web/http
275} 285}
276 286
277dotest() { 287dotest() {
@@ -310,7 +320,7 @@ build_test_generic() {
310 shift 320 shift
311 fi 321 fi
312 debug="yes" 322 debug="yes"
313 build_$obj 323 build_$obj || exit 1
314 for t in test/*; do 324 for t in test/*; do
315 dotest || exit 1 325 dotest || exit 1
316 done 326 done
@@ -323,7 +333,7 @@ build_test() {
323 testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj" 333 testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj"
324 testrun="./$testobj" 334 testrun="./$testobj"
325 build_test_generic $@ 335 build_test_generic $@
326 rm runtest 336 rm -f runtest
327} 337}
328 338
329build_webtest() { 339build_webtest() {
diff --git a/web/adapter.cpp b/web/adapter.cpp
index 1774187..3613730 100644
--- a/web/adapter.cpp
+++ b/web/adapter.cpp
@@ -1,11 +1,15 @@
1#include "../cpp/nissy.h" 1#include "../cpp/nissy.h"
2#include "storage.h"
2 3
4#include <emscripten.h>
3#include <emscripten/bind.h> 5#include <emscripten/bind.h>
4#include <map> 6#include <map>
5#include <set> 7#include <set>
6#include <string> 8#include <string>
7#include <vector> 9#include <vector>
8 10
11EM_ASYNC_JS(void, fake_async, (), {});
12
9extern "C" { 13extern "C" {
10 extern int addCallbackFunction(/* args intentionally unspecified */); 14 extern int addCallbackFunction(/* args intentionally unspecified */);
11 extern void callFunction(int, const char *); 15 extern void callFunction(int, const char *);
@@ -47,26 +51,43 @@ const std::set<std::string> available_solvers
47std::map<std::string, nissy::solver> loaded_solvers; 51std::map<std::string, nissy::solver> loaded_solvers;
48 52
49// TODO: this should ask the user if they want to download or generate. 53// TODO: this should ask the user if they want to download or generate.
50// TODO: this should also save the data to a file (IDBFS / NODEFS)
51bool init_solver(const std::string& name) 54bool init_solver(const std::string& name)
52{ 55{
53 auto se = nissy::solver::get(name); 56 auto se = nissy::solver::get(name);
54 nissy::solver solver = std::get<nissy::solver>(se); 57 nissy::solver solver = std::get<nissy::solver>(se);
55 log("Generating data for solver " + solver.name + "\n"); 58
56 auto err = solver.generate_data(); 59 solver.data.resize(solver.size);
57 if (!err.ok()) { 60 if (storage::read(solver.id, solver.size,
58 log("Error generating the data!\n"); 61 reinterpret_cast<char *>(solver.data.data()))) {
59 return false; 62 log("Data for solver " + solver.name + " read from storage\n");
63 } else {
64 log("Could not read data for solver " + solver.name +
65 " from storage, generating it\n");
66 auto err = solver.generate_data();
67
68 if (!err.ok()) {
69 log("Error generating the data!\n");
70 return false;
71 }
60 } 72 }
73
61 log("Checking data integrity " 74 log("Checking data integrity "
62 "(this is done only once per session per solver)...\n"); 75 "(this is done only once per session per solver)...\n");
63 if (!solver.check_data().ok()) { 76 if (!solver.check_data().ok()) {
64 log("Error generating the data!\n"); 77 log("Error! Data is corrupted!\n");
65 return false; 78 return false;
66 } 79 }
67 loaded_solvers.insert({name, solver}); 80 loaded_solvers.insert({name, solver});
68 log("Data generated successfully, but not saved " 81
69 "(feature not yet available)\n"); 82 if (storage::write(solver.id, solver.size,
83 reinterpret_cast<const char *>(solver.data.data()))) {
84 log("Data for solver " + solver.name + " stored\n");
85 } else {
86 log("Error storing the data (the solver is usable, "
87 "but the data will have to be re-generated next "
88 "time you want to use it)");
89 }
90
70 return true; 91 return true;
71} 92}
72 93
@@ -87,17 +108,31 @@ bool solver_valid(const std::string& name)
87 108
88int poll_status(void *arg) 109int poll_status(void *arg)
89{ 110{
111 return nissy::status::RUN.value;
112/*
113TODO: reintroduce poll status
90 int id = *(int *)arg; 114 int id = *(int *)arg;
91 if (id == -1) 115 if (id == -1)
92 return nissy::status::RUN.value; 116 return nissy::status::RUN.value;
93 return callFunctionInt(id); 117 return callFunctionInt(id);
118*/
94} 119}
95 120
121#if 0
122
96nissy::solver::solve_result solve(std::string name, 123nissy::solver::solve_result solve(std::string name,
97 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves, 124 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves,
98 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads, 125 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads,
99 int poll_status_id) 126 int poll_status_id)
100{ 127{
128 // Here we use a dirty trick to make this function always return the
129 // same kind of JavaScript object. If we did not do this, the returned
130 // object would be a Promise on the first run of the solver for each
131 // session (because when loading the table some async JS code is
132 // called), and a regular object otherwise.
133 // TODO figure out if there is a better way to do this.
134 fake_async();
135
101 if (!solver_valid(name)) 136 if (!solver_valid(name))
102 return nissy::solver::solve_result 137 return nissy::solver::solve_result
103 {.err = nissy::error::INVALID_SOLVER}; 138 {.err = nissy::error::INVALID_SOLVER};
@@ -106,6 +141,35 @@ nissy::solver::solve_result solve(std::string name,
106 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id); 141 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id);
107} 142}
108 143
144#else
145
146std::string solve(std::string name,
147 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves,
148 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads,
149 int poll_status_id)
150{
151 // Here we use a dirty trick to make this function always return the
152 // same kind of JavaScript object. If we did not do this, the returned
153 // object would be a Promise on the first run of the solver for each
154 // session (because when loading the table some async JS code is
155 // called), and a regular object otherwise.
156 // TODO figure out if there is a better way to do this.
157 fake_async();
158
159 if (!solver_valid(name))
160 return "";
161/*
162 return nissy::solver::solve_result
163 {.err = nissy::error::INVALID_SOLVER};
164*/
165
166 return loaded_solvers.at(name).solve(cube, nissflag, minmoves,
167 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id)
168 .solutions[0];
169}
170
171#endif
172
109EMSCRIPTEN_BINDINGS(Nissy) 173EMSCRIPTEN_BINDINGS(Nissy)
110{ 174{
111 emscripten::class_<nissy::nissflag>("NissFlag") 175 emscripten::class_<nissy::nissflag>("NissFlag")
@@ -134,13 +198,11 @@ EMSCRIPTEN_BINDINGS(Nissy)
134 emscripten::constant("statusRUN", nissy::status::RUN.value); 198 emscripten::constant("statusRUN", nissy::status::RUN.value);
135 emscripten::constant("statusSTOP", nissy::status::STOP.value); 199 emscripten::constant("statusSTOP", nissy::status::STOP.value);
136 emscripten::constant("statusPAUSE", nissy::status::PAUSE.value); 200 emscripten::constant("statusPAUSE", nissy::status::PAUSE.value);
137 /*
138 emscripten::class_<nissy::status>("Status") 201 emscripten::class_<nissy::status>("Status")
139 .class_property("run", &nissy::status::RUN) 202 .class_property("run", &nissy::status::RUN)
140 .class_property("stop", &nissy::status::STOP) 203 .class_property("stop", &nissy::status::STOP)
141 .class_property("pause", &nissy::status::PAUSE) 204 .class_property("pause", &nissy::status::PAUSE)
142 ; 205 ;
143 */
144 206
145 emscripten::class_<nissy::cube>("Cube") 207 emscripten::class_<nissy::cube>("Cube")
146 .constructor<>() 208 .constructor<>()
@@ -150,10 +212,17 @@ EMSCRIPTEN_BINDINGS(Nissy)
150 .function("toString", &nissy::cube::to_string) 212 .function("toString", &nissy::cube::to_string)
151 ; 213 ;
152 214
215 emscripten::register_vector<std::string>("StringVector");
216 emscripten::value_array<nissy::solver::solve_result>("SolveResult")
217 .element(&nissy::solver::solve_result::err)
218 .element(&nissy::solver::solve_result::solutions)
219 ;
220/*
153 emscripten::class_<nissy::solver::solve_result>("SolveResult") 221 emscripten::class_<nissy::solver::solve_result>("SolveResult")
154 .property("err", &nissy::solver::solve_result::err) 222 .property("err", &nissy::solver::solve_result::err)
155 .property("solutions", &nissy::solver::solve_result::solutions) 223 .property("solutions", &nissy::solver::solve_result::solutions)
156 ; 224 ;
225*/
157 226
158 emscripten::function("countMoves", &nissy::count_moves); 227 emscripten::function("countMoves", &nissy::count_moves);
159 emscripten::function("solve", &solve, 228 emscripten::function("solve", &solve,
diff --git a/web/callback.js b/web/callback.js
index 1b3028e..178b8e9 100644
--- a/web/callback.js
+++ b/web/callback.js
@@ -2,20 +2,44 @@ addToLibrary({
2 2
3cbfl: [], 3cbfl: [],
4 4
5validateCallbackId__deps: [ 'cbfl' ],
6validateCallbackId: function(i) {
7 if (i < 0) {
8 console.log("--- WARNING ---");
9 console.log("Trying to access callback function of invalid id " + i);
10 console.log("--- WARNING ---");
11 return false;
12 }
13
14 if (i >= _cbfl.length) {
15 console.log("--- WARNING ---");
16 console.log("Trying to access callback function " + i + ", but only "
17 + _cbfl.length + " have been registered. This may be caused by a "
18 + "call outside of the main thread.");
19 console.log("--- WARNING ---");
20 return false;
21 }
22
23 return true;
24},
25
5addCallbackFunction__deps: [ 'cbfl' ], 26addCallbackFunction__deps: [ 'cbfl' ],
6addCallbackFunction: function(f) { 27addCallbackFunction: function(f) {
7 _cbfl.push(f) 28 _cbfl.push(f)
8 return _cbfl.length - 1 29 return _cbfl.length - 1
9}, 30},
10 31
11callFunction__deps: [ 'cbfl' ], 32callFunction__deps: [ 'cbfl', 'validateCallbackId' ],
12callFunction: function(id, arg) { 33callFunction: function(id, arg) {
13 _cbfl[id](UTF8ToString(arg)) 34 if (_validateCallbackId(id))
35 _cbfl[id](UTF8ToString(arg));
14}, 36},
15 37
16callFunctionInt__deps: [ 'cbfl' ], 38callFunctionInt__deps: [ 'cbfl', 'validateCallbackId' ],
17callFunctionInt: function(id) { 39callFunctionInt: function(id) {
18 return _cbfl[id]() 40 if (_validateCallbackId(id))
41 return _cbfl[id]();
42 return 0;
19}, 43},
20 44
21}); 45});
diff --git a/web/examples/solve.mjs b/web/examples/solve.mjs
index c7bce71..8ff59c0 100644
--- a/web/examples/solve.mjs
+++ b/web/examples/solve.mjs
@@ -1,10 +1,12 @@
1import Nissy from '../nissy_web_module.mjs' 1import Nissy from '../nissy_web_module.mjs'
2 2
3const nissy = await Nissy() 3const nissy = await Nissy();
4 4
5nissy.setLogger(nissy._addCallbackFunction(console.log)) 5var log = process.stdout.write.bind(process.stdout);
6//var log = console.log
7nissy.setLogger(nissy._addCallbackFunction(log))
6 8
7var cube = new nissy.Cube() 9var cube = new nissy.Cube();
8cube.move('R\' U\' F') 10cube.move('R\' U\' F');
9 11
10nissy.solve('h48h0k4', cube, nissy.NissFlag.normal, 0, 8, 2, 99, 4, -1) 12nissy.solve('h48h0k4', cube, nissy.NissFlag.normal, 0, 8, 2, 99, 4, -1);
diff --git a/web/http/index.html b/web/http/index.html
new file mode 100644
index 0000000..245205d
--- /dev/null
+++ b/web/http/index.html
@@ -0,0 +1,20 @@
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>Nissy - H48 solver POC</title>
7 <script type="module" src="./solve.mjs"></script>
8 </head>
9 <body>
10 <input id="scrambleText" placeholder="Type the scramble here...">
11 <select id="solverSelector">
12 <option value="h48h0k4" selected="selected">h48 h=0 k=4 (59Mb)</option>
13 <option value="h48h3k2">h48 h=3 k=2 (283Mb)</option>
14 <option value="h48h7k2">h48 h=7 k=2 (3.6Gb)</option>
15 </select>
16 <button id="solveButton">Solve!</button>
17 <p>Solution:</p>
18 <p id="solution"></p>
19 </body>
20</html>
diff --git a/web/http/mime b/web/http/mime
new file mode 100644
index 0000000..8c956e1
--- /dev/null
+++ b/web/http/mime
@@ -0,0 +1 @@
text/javascript mjs
diff --git a/web/storage.cpp b/web/storage.cpp
new file mode 100644
index 0000000..4be3066
--- /dev/null
+++ b/web/storage.cpp
@@ -0,0 +1,67 @@
1#include "storage.h"
2
3#include "emscripten.h"
4#include <filesystem>
5#include <fstream>
6
7EM_JS(int, inbrowser, (), { return typeof window !== 'undefined'; });
8EM_JS(int, inworker, (), { return typeof WorkerGlobalScope !== 'undefined' &&
9 self instanceof WorkerGlobalScope; });
10
11std::string getprefix() {
12 return inbrowser() || inworker() ? "/tables/" : "./tables/";
13}
14
15EM_ASYNC_JS(int, loadfs, (), {
16 const dir = '/tables';
17 const inBrowser = typeof window !== 'undefined';
18 const inWorker = typeof WorkerGlobalScope !== 'undefined' &&
19 self instanceof WorkerGlobalScope;
20
21 if (!(inBrowser || inWorker)) return;
22
23 if (!FS.analyzePath(dir).exists)
24 FS.mkdir(dir);
25
26 if (FS.analyzePath(dir).object.mount.mountpoint != dir) {
27 FS.mount(IDBFS, { autoPersist: true }, dir);
28
29 await new Promise((resolve, reject) => {
30 FS.syncfs(true, function (err) {
31 if (err) {
32 reject(err);
33 } else {
34 resolve(true);
35 }
36 });
37 });
38 }
39});
40
41bool storage::read(std::string key, size_t data_size, char *data)
42{
43 loadfs();
44
45 std::filesystem::path path(getprefix() + key);
46 if (!std::filesystem::exists(path))
47 return false;
48
49 std::ifstream ifs(path, std::ios::binary);
50 ifs.read(data, data_size);
51 ifs.close();
52
53 return !ifs.fail();
54}
55
56bool storage::write(std::string key, size_t data_size, const char *data)
57{
58 loadfs();
59
60 std::filesystem::path path(getprefix() + key);
61
62 std::ofstream ofs(path, std::ios::binary);
63 ofs.write(data, data_size);
64 ofs.close();
65
66 return !ofs.fail();
67}
diff --git a/web/storage.h b/web/storage.h
new file mode 100644
index 0000000..fa6b748
--- /dev/null
+++ b/web/storage.h
@@ -0,0 +1,7 @@
1#include <string>
2#include <fstream>
3
4namespace storage {
5 bool read(std::string, size_t, char *);
6 bool write(std::string, size_t, const char *);
7}

Generated with cgit - Back to sebastiano.tronto.net