aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/adapter.cpp177
l---------web/http/tables1
-rw-r--r--web/http/worker.mjs30
-rw-r--r--web/logging.h17
-rw-r--r--web/storage.cpp35
-rw-r--r--web/storage.h6
6 files changed, 173 insertions, 93 deletions
diff --git a/web/adapter.cpp b/web/adapter.cpp
index 71a34ab..676390c 100644
--- a/web/adapter.cpp
+++ b/web/adapter.cpp
@@ -1,5 +1,12 @@
1extern "C" {
2 extern int addCallbackFunction(/* args intentionally unspecified */);
3 extern void callFunction(int, const char *);
4 extern int callFunctionInt(int);
5}
6
1#include "../cpp/nissy.h" 7#include "../cpp/nissy.h"
2#include "storage.h" 8#include "storage.h"
9#include "logging.h"
3 10
4#include <emscripten.h> 11#include <emscripten.h>
5#include <emscripten/bind.h> 12#include <emscripten/bind.h>
@@ -10,78 +17,130 @@
10 17
11EM_ASYNC_JS(void, fake_async, (), {}); 18EM_ASYNC_JS(void, fake_async, (), {});
12 19
13extern "C" { 20const std::set<std::string> available_solvers
14 extern int addCallbackFunction(/* args intentionally unspecified */); 21{
15 extern void callFunction(int, const char *); 22 "h48h0k4",
16 extern int callFunctionInt(int); 23 "h48h1k2",
24 "h48h2k2",
25 "h48h3k2",
26 "h48h7k2",
27};
28
29std::map<std::string, nissy::solver> loaded_solvers;
30
31bool is_solver_available(const std::string& name)
32{
33 return available_solvers.contains(name);
17} 34}
18 35
19static int logger_id = -1; 36bool is_solver_loaded(const std::string& name)
37{
38 return loaded_solvers.contains(name);
39}
20 40
21void log(std::string s) 41bool check_data(nissy::solver& solver)
22{ 42{
23 if (logger_id == -1) 43 log("Checking data integrity "
24 return; 44 "(this is done only once per session per solver)...\n");
25 45
26 callFunction(logger_id, s.c_str()); 46 if (!solver.check_data().ok()) {
47 log("Error! Data is corrupted!\n");
48 return false;
49 }
50
51 return true;
27} 52}
28 53
29void log_wrapper(const char *cstr, void *data) 54bool read_solver_data(nissy::solver& solver)
30{ 55{
31 log(cstr); 56 solver.data.resize(solver.size);
57
58 bool success = storage::read(solver.id, solver.size,
59 reinterpret_cast<char *>(solver.data.data()));
60
61 if (!success) {
62 log("Could not read data for solver " +
63 solver.name + " from storage\n");
64 return false;
65 }
66
67 if (!check_data(solver)) {
68 log("Data for solver " + solver.name + " is corrupt!\n");
69 return false;
70 }
71
72 log("Data for solver " + solver.name + " read from storage\n");
73 loaded_solvers.insert({solver.name, solver});
74
75 return true;
32} 76}
33 77
34void set_logger(int id) 78bool is_solver_valid(const std::string& name,
79 std::variant<nissy::solver, nissy::error>& se)
35{ 80{
36 logger_id = id; 81 if (std::holds_alternative<nissy::error>(se)) {
37 nissy::set_logger(log_wrapper, NULL); 82 log("Invalid solver " + name + "\n");
83 return false;
84 }
85
86 if (!is_solver_available(name)) {
87 log("Solver " + name + " is not available in this version\n");
88 return false;
89 }
90
91 return true;
38} 92}
39 93
40// Some of the solvers are not available to the JS interface because of 94bool init_solver_from_storage(const std::string& name)
41// memory limitations.
42const std::set<std::string> available_solvers
43{ 95{
44 "h48h0k4", 96 if (is_solver_loaded(name))
45 "h48h1k2", 97 return true;
46 "h48h2k2", 98 auto se = nissy::solver::get(name);
47 "h48h3k2", 99 if (!is_solver_valid(name, se))
48 "h48h7k2", 100 return false;
49}; 101 nissy::solver solver = std::get<nissy::solver>(se);
50 102
51std::map<std::string, nissy::solver> loaded_solvers; 103 return read_solver_data(solver);
104}
52 105
53// TODO: this should ask the user if they want to download or generate. 106bool init_solver_download(const std::string& name, const std::string& urlbase)
54bool init_solver(const std::string& name)
55{ 107{
108 if (is_solver_loaded(name))
109 return true;
56 auto se = nissy::solver::get(name); 110 auto se = nissy::solver::get(name);
111 if (!is_solver_valid(name, se))
112 return false;
57 nissy::solver solver = std::get<nissy::solver>(se); 113 nissy::solver solver = std::get<nissy::solver>(se);
58 114
59 solver.data.resize(solver.size); 115 if (storage::download(solver.id, urlbase + "/" + solver.id)) {
60 if (storage::read(solver.id, solver.size, 116 return read_solver_data(solver);
61 reinterpret_cast<char *>(solver.data.data()))) {
62 log("Data for solver " + solver.name + " read from storage\n");
63 } else { 117 } else {
64 log("Could not read data for solver " + solver.name + 118 return false;
65 " from storage, generating it\n"); 119 }
66 auto err = solver.generate_data(); 120}
67 121
68 if (!err.ok()) { 122bool init_solver_generate(const std::string& name)
69 log("Error generating the data!\n"); 123{
70 return false; 124 if (is_solver_loaded(name))
71 } 125 return true;
126 auto se = nissy::solver::get(name);
127 if (!is_solver_valid(name, se))
128 return false;
129 nissy::solver solver = std::get<nissy::solver>(se);
130
131 if (!solver.generate_data().ok()) {
132 log("Error generating data for solver " + name + "!\n");
133 return false;
72 } 134 }
73 135
74 log("Checking data integrity " 136 if (!check_data(solver)) {
75 "(this is done only once per session per solver)...\n"); 137 log("Data for solver " + name + " generated incorrectly!\n");
76 if (!solver.check_data().ok()) {
77 log("Error! Data is corrupted!\n");
78 return false; 138 return false;
79 } 139 }
80 loaded_solvers.insert({name, solver});
81 140
82 if (storage::write(solver.id, solver.size, 141 if (storage::write(solver.id, solver.size,
83 reinterpret_cast<const char *>(solver.data.data()))) { 142 reinterpret_cast<const char *>(solver.data.data()))) {
84 log("Data for solver " + solver.name + " stored\n"); 143 log("Data for solver " + name + " stored\n");
85 } else { 144 } else {
86 log("Error storing the data (the solver is usable, " 145 log("Error storing the data (the solver is usable, "
87 "but the data will have to be re-generated next " 146 "but the data will have to be re-generated next "
@@ -91,21 +150,6 @@ bool init_solver(const std::string& name)
91 return true; 150 return true;
92} 151}
93 152
94bool solver_valid(const std::string& name)
95{
96 if (loaded_solvers.contains(name) ||
97 (available_solvers.contains(name) && init_solver(name)))
98 return true;
99
100 auto se = nissy::solver::get(name);
101 if (std::holds_alternative<nissy::solver>(se))
102 log("The solver " + name + " is not available in "
103 "the web version of Nissy. Use a native version.\n");
104 else
105 log("Invalid solver " + name + "\n");
106 return false;
107}
108
109int poll_status(void *arg) 153int poll_status(void *arg)
110{ 154{
111 if (arg == NULL || *(int *)arg == -1) 155 if (arg == NULL || *(int *)arg == -1)
@@ -127,9 +171,11 @@ nissy::solver::solve_result solve(std::string name,
127 // TODO figure out if there is a better way to do this. 171 // TODO figure out if there is a better way to do this.
128 fake_async(); 172 fake_async();
129 173
130 if (!solver_valid(name)) 174 if (!is_solver_loaded(name)) {
175 log("Solver " + name + " is invalid or has not been loaded\n");
131 return nissy::solver::solve_result 176 return nissy::solver::solve_result
132 {.err = nissy::error::INVALID_SOLVER}; 177 {.err = nissy::error::INVALID_SOLVER};
178 }
133 179
134 return loaded_solvers.at(name).solve(cube, nissflag, minmoves, 180 return loaded_solvers.at(name).solve(cube, nissflag, minmoves,
135 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id); 181 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id);
@@ -147,8 +193,10 @@ EMSCRIPTEN_BINDINGS(Nissy)
147 193
148 emscripten::class_<nissy::error>("Error") 194 emscripten::class_<nissy::error>("Error")
149 .function("ok", &nissy::error::ok) 195 .function("ok", &nissy::error::ok)
150 .class_property("unsolvableWarning", &nissy::error::UNSOLVABLE_WARNING) 196 .class_property("unsolvableWarning",
151 .class_property("unsolvableError", &nissy::error::UNSOLVABLE_ERROR) 197 &nissy::error::UNSOLVABLE_WARNING)
198 .class_property("unsolvableError",
199 &nissy::error::UNSOLVABLE_ERROR)
152 .class_property("invalidCube", &nissy::error::INVALID_CUBE) 200 .class_property("invalidCube", &nissy::error::INVALID_CUBE)
153 .class_property("invalidMoves", &nissy::error::INVALID_MOVES) 201 .class_property("invalidMoves", &nissy::error::INVALID_MOVES)
154 .class_property("invalidTrans", &nissy::error::INVALID_TRANS) 202 .class_property("invalidTrans", &nissy::error::INVALID_TRANS)
@@ -182,6 +230,13 @@ EMSCRIPTEN_BINDINGS(Nissy)
182 .property("solutions", &nissy::solver::solve_result::solutions) 230 .property("solutions", &nissy::solver::solve_result::solutions)
183 ; 231 ;
184 232
233 emscripten::function("isSolverAvailable", &is_solver_available);
234 emscripten::function("isSolverLoaded", &is_solver_loaded);
235 emscripten::function("initSolverFromStorage",
236 &init_solver_from_storage);
237 emscripten::function("initSolverDownload", &init_solver_download);
238 emscripten::function("initSolverGenerate", &init_solver_generate);
239
185 emscripten::function("countMoves", &nissy::count_moves); 240 emscripten::function("countMoves", &nissy::count_moves);
186 emscripten::function("solve", &solve, 241 emscripten::function("solve", &solve,
187 emscripten::return_value_policy::take_ownership()); 242 emscripten::return_value_policy::take_ownership());
diff --git a/web/http/tables b/web/http/tables
new file mode 120000
index 0000000..b3afcac
--- /dev/null
+++ b/web/http/tables
@@ -0,0 +1 @@
../../tables/ \ No newline at end of file
diff --git a/web/http/worker.mjs b/web/http/worker.mjs
index 831ef09..7ce1070 100644
--- a/web/http/worker.mjs
+++ b/web/http/worker.mjs
@@ -4,14 +4,24 @@ const nissy = await Nissy();
4nissy.setLogger(nissy._addCallbackFunction(console.log)); 4nissy.setLogger(nissy._addCallbackFunction(console.log));
5 5
6onmessage = (e) => { 6onmessage = (e) => {
7 var cube = new nissy.Cube(); 7//TODO: try to load from storage first, then download
8 cube.move(e.data.scramble); 8 nissy.initSolverDownload(e.data.solver, "/tables").then(
9 nissy.solve(e.data.solver, cube, nissy.NissFlag.normal, 0, 17, 1, 99, 4, -1) 9 (download_result) => {
10 .then((solve_result) => { 10 if (!download_result) {
11 if (!solve_result.err.ok()) 11 postMessage("Error retrieving the solver data");
12 posMessage("Error while solving (solve returned " + 12 } else {
13 solve_result.err.value + ")"); 13 var cube = new nissy.Cube();
14 else 14 cube.move(e.data.scramble);
15 postMessage(solve_result.solutions) 15
16 }); 16 nissy.solve(e.data.solver, cube, nissy.NissFlag.normal,
17 0, 17, 1, 99, 4, -1).then(
18 (solve_result) => {
19 if (!solve_result.err.ok())
20 postMessage("Error while solving (solve returned " +
21 solve_result.err.value + ")");
22 else
23 postMessage(solve_result.solutions)
24 });
25 }
26 });
17}; 27};
diff --git a/web/logging.h b/web/logging.h
index b20a595..9ed4835 100644
--- a/web/logging.h
+++ b/web/logging.h
@@ -1,18 +1,3 @@
1#ifndef LOGGING_H
2#define LOGGING_H
3
4#include "../cpp/nissy.h"
5#include <emscripten/bind.h>
6#include <string>
7
8#define LOGGING_EMBIND \
9 emscripten::function("addCallbackFunction", &addCallbackFunction);
10
11extern "C" {
12 extern int addCallbackFunction();
13 extern void callFunction(int, const char *);
14}
15
16static int logger_id = -1; 1static int logger_id = -1;
17 2
18void log(std::string s) 3void log(std::string s)
@@ -33,5 +18,3 @@ void set_logger(int id)
33 logger_id = id; 18 logger_id = id;
34 nissy::set_logger(log_wrapper, NULL); 19 nissy::set_logger(log_wrapper, NULL);
35} 20}
36
37#endif
diff --git a/web/storage.cpp b/web/storage.cpp
index 4d0959f..cbf9e9c 100644
--- a/web/storage.cpp
+++ b/web/storage.cpp
@@ -13,13 +13,12 @@ std::string getprefix() {
13} 13}
14 14
15EM_ASYNC_JS(int, loadfs, (), { 15EM_ASYNC_JS(int, loadfs, (), {
16 const dir = '/tables';
17 const inBrowser = typeof window !== 'undefined'; 16 const inBrowser = typeof window !== 'undefined';
18 const inWorker = typeof WorkerGlobalScope !== 'undefined' && 17 const inWorker = typeof WorkerGlobalScope !== 'undefined' &&
19 self instanceof WorkerGlobalScope; 18 self instanceof WorkerGlobalScope;
19 console.assert(inBrowser || inWorker, "Non-browsers not supported");
20 20
21 if (!(inBrowser || inWorker)) 21 const dir = '/tables';
22 return;
23 22
24 if (!FS.analyzePath(dir).exists) 23 if (!FS.analyzePath(dir).exists)
25 FS.mkdir(dir); 24 FS.mkdir(dir);
@@ -38,6 +37,30 @@ EM_ASYNC_JS(int, loadfs, (), {
38 } 37 }
39}); 38});
40 39
40EM_ASYNC_JS(int, download_and_store, (const char *key, const char *url), {
41 const inBrowser = typeof window !== 'undefined';
42 const inWorker = typeof WorkerGlobalScope !== 'undefined' &&
43 self instanceof WorkerGlobalScope;
44 console.assert(inBrowser || inWorker, "Non-browsers not supported");
45
46 url = UTF8ToString(url);
47 key = UTF8ToString(key);
48 let response = await fetch(url);
49 if (!response.ok) {
50 console.log("Error downloading data for " + key);
51 console.log("" + response.status + ": " + response.statusText);
52 return 0;
53 }
54
55 console.log("Data for " + key + " downloaded, writing to storage...");
56 let data = await response.bytes();
57 var stream = FS.open("/tables/" + key, "w+");
58 FS.write(stream, data, 0, data.length, 0);
59 FS.close(stream);
60 console.log("Data for " + key + " stored (" + data.length + " bytes)");
61 return 1;
62});
63
41bool storage::read(std::string key, size_t data_size, char *data) 64bool storage::read(std::string key, size_t data_size, char *data)
42{ 65{
43 loadfs(); 66 loadfs();
@@ -65,3 +88,9 @@ bool storage::write(std::string key, size_t data_size, const char *data)
65 88
66 return !ofs.fail(); 89 return !ofs.fail();
67} 90}
91
92int storage::download(std::string key, std::string url)
93{
94 loadfs();
95 return download_and_store(key.c_str(), url.c_str());
96}
diff --git a/web/storage.h b/web/storage.h
index fa6b748..b491742 100644
--- a/web/storage.h
+++ b/web/storage.h
@@ -1,7 +1,9 @@
1#include <string> 1#include <string>
2#include <fstream> 2#include <fstream>
3#include <emscripten/fetch.h>
3 4
4namespace storage { 5namespace storage {
5 bool read(std::string, size_t, char *); 6 bool read(std::string key, size_t data_size, char *data);
6 bool write(std::string, size_t, const char *); 7 bool write(std::string key, size_t data_size, const char *data);
8 int download(std::string key, std::string url);
7} 9}

Generated with cgit - Back to sebastiano.tronto.net