Skip to content

Commit e403860

Browse files
committed
js add storage module
(without virtual mount api calls) by Willy-JL
1 parent cc7fc09 commit e403860

File tree

3 files changed

+321
-0
lines changed

3 files changed

+321
-0
lines changed

applications/system/js_app/application.fam

+8
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ App(
4747
sources=["modules/js_serial.c"],
4848
)
4949

50+
App(
51+
appid="js_storage",
52+
apptype=FlipperAppType.PLUGIN,
53+
entry_point="js_storage_ep",
54+
requires=["js_app"],
55+
sources=["modules/js_storage.c"],
56+
)
57+
5058
App(
5159
appid="js_usbdisk",
5260
apptype=FlipperAppType.PLUGIN,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
let storage = require("storage");
2+
let path = "/ext/storage.test";
3+
4+
function arraybuf_to_string(arraybuf) {
5+
let string = "";
6+
let data_view = Uint8Array(arraybuf);
7+
for (let i = 0; i < data_view.length; i++) {
8+
string += chr(data_view[i]);
9+
}
10+
return string;
11+
}
12+
13+
print("File exists:", storage.exists(path));
14+
15+
print("Writing...");
16+
// write(path, data, offset)
17+
// If offset is specified, the file is not cleared, content is kept and data is written at specified offset
18+
// Takes both strings and array buffers
19+
storage.write(path, "Hello ");
20+
21+
print("File exists:", storage.exists(path));
22+
23+
// Append will create the file even if it doesnt exist!
24+
// Takes both strings and array buffers
25+
storage.append(path, "World!");
26+
27+
print("Reading...");
28+
// read(path, size, offset)
29+
// If no size specified, total filesize is used
30+
// If offset is specified, size is capped at (filesize - offset)
31+
let data = storage.read(path);
32+
// read returns an array buffer, to allow proper usage of raw binary data
33+
print(arraybuf_to_string(data));
34+
35+
print("Removing...")
36+
storage.remove(path);
37+
38+
print("Done")
39+
40+
// There's also:
41+
// storage.copy(old_path, new_path);
42+
// storage.move(old_path, new_path);
43+
// storage.mkdir(path);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
#include "../js_modules.h"
2+
#include <storage/storage.h>
3+
4+
typedef struct {
5+
Storage* api;
6+
} JsStorageInst;
7+
8+
static JsStorageInst* get_this_ctx(struct mjs* mjs) {
9+
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
10+
JsStorageInst* storage = mjs_get_ptr(mjs, obj_inst);
11+
furi_assert(storage);
12+
return storage;
13+
}
14+
15+
static void ret_bad_args(struct mjs* mjs, const char* error) {
16+
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error);
17+
mjs_return(mjs, MJS_UNDEFINED);
18+
}
19+
20+
static void ret_int_err(struct mjs* mjs, const char* error) {
21+
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "%s", error);
22+
mjs_return(mjs, MJS_UNDEFINED);
23+
}
24+
25+
static bool check_arg_count(struct mjs* mjs, size_t count) {
26+
size_t num_args = mjs_nargs(mjs);
27+
if(num_args != count) {
28+
ret_bad_args(mjs, "Wrong argument count");
29+
return false;
30+
}
31+
return true;
32+
}
33+
34+
static bool get_path_arg(struct mjs* mjs, const char** path, size_t index) {
35+
mjs_val_t path_obj = mjs_arg(mjs, index);
36+
if(!mjs_is_string(path_obj)) {
37+
ret_bad_args(mjs, "Path must be a string");
38+
return false;
39+
}
40+
size_t path_len = 0;
41+
*path = mjs_get_string(mjs, &path_obj, &path_len);
42+
if((path_len == 0) || (*path == NULL)) {
43+
ret_bad_args(mjs, "Bad path argument");
44+
return false;
45+
}
46+
return true;
47+
}
48+
49+
static void js_storage_read(struct mjs* mjs) {
50+
JsStorageInst* storage = get_this_ctx(mjs);
51+
52+
const char* path;
53+
if(!get_path_arg(mjs, &path, 0)) return;
54+
55+
File* file = storage_file_alloc(storage->api);
56+
do {
57+
if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
58+
ret_int_err(mjs, storage_file_get_error_desc(file));
59+
break;
60+
}
61+
62+
uint64_t size = storage_file_size(file);
63+
mjs_val_t size_arg = mjs_arg(mjs, 1);
64+
if(mjs_is_number(size_arg)) {
65+
size = mjs_get_int32(mjs, size_arg);
66+
}
67+
68+
mjs_val_t seek_arg = mjs_arg(mjs, 2);
69+
if(mjs_is_number(seek_arg)) {
70+
storage_file_seek(file, mjs_get_int32(mjs, seek_arg), true);
71+
size = MIN(size, storage_file_size(file) - storage_file_tell(file));
72+
}
73+
74+
if(size > memmgr_heap_get_max_free_block()) {
75+
ret_int_err(mjs, "Read size too large");
76+
break;
77+
}
78+
79+
uint8_t* data = malloc(size);
80+
size_t read = storage_file_read(file, data, size);
81+
if(read == size) {
82+
mjs_return(mjs, mjs_mk_array_buf(mjs, (char*)data, size));
83+
} else {
84+
ret_int_err(mjs, "File read failed");
85+
}
86+
free(data);
87+
} while(0);
88+
storage_file_free(file);
89+
}
90+
91+
static void js_storage_write(struct mjs* mjs) {
92+
JsStorageInst* storage = get_this_ctx(mjs);
93+
94+
const char* path;
95+
if(!get_path_arg(mjs, &path, 0)) return;
96+
97+
mjs_val_t data_arg = mjs_arg(mjs, 1);
98+
if(!mjs_is_typed_array(data_arg) && !mjs_is_string(data_arg)) {
99+
ret_bad_args(mjs, "Data must be string, arraybuf or dataview");
100+
return;
101+
}
102+
if(mjs_is_data_view(data_arg)) {
103+
data_arg = mjs_dataview_get_buf(mjs, data_arg);
104+
}
105+
size_t data_len = 0;
106+
const char* data = NULL;
107+
if(mjs_is_string(data_arg)) {
108+
data = mjs_get_string(mjs, &data_arg, &data_len);
109+
} else if(mjs_is_typed_array(data_arg)) {
110+
data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len);
111+
}
112+
113+
mjs_val_t seek_arg = mjs_arg(mjs, 2);
114+
115+
File* file = storage_file_alloc(storage->api);
116+
if(!storage_file_open(
117+
file,
118+
path,
119+
FSAM_WRITE,
120+
mjs_is_number(seek_arg) ? FSOM_OPEN_ALWAYS : FSOM_CREATE_ALWAYS)) {
121+
ret_int_err(mjs, storage_file_get_error_desc(file));
122+
123+
} else {
124+
if(mjs_is_number(seek_arg)) {
125+
storage_file_seek(file, mjs_get_int32(mjs, seek_arg), true);
126+
}
127+
128+
size_t write = storage_file_write(file, data, data_len);
129+
mjs_return(mjs, mjs_mk_boolean(mjs, write == data_len));
130+
}
131+
storage_file_free(file);
132+
}
133+
134+
static void js_storage_append(struct mjs* mjs) {
135+
JsStorageInst* storage = get_this_ctx(mjs);
136+
if(!check_arg_count(mjs, 2)) return;
137+
138+
const char* path;
139+
if(!get_path_arg(mjs, &path, 0)) return;
140+
141+
mjs_val_t data_arg = mjs_arg(mjs, 1);
142+
if(!mjs_is_typed_array(data_arg) && !mjs_is_string(data_arg)) {
143+
ret_bad_args(mjs, "Data must be string, arraybuf or dataview");
144+
return;
145+
}
146+
if(mjs_is_data_view(data_arg)) {
147+
data_arg = mjs_dataview_get_buf(mjs, data_arg);
148+
}
149+
size_t data_len = 0;
150+
const char* data = NULL;
151+
if(mjs_is_string(data_arg)) {
152+
data = mjs_get_string(mjs, &data_arg, &data_len);
153+
} else if(mjs_is_typed_array(data_arg)) {
154+
data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len);
155+
}
156+
157+
File* file = storage_file_alloc(storage->api);
158+
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
159+
ret_int_err(mjs, storage_file_get_error_desc(file));
160+
} else {
161+
size_t write = storage_file_write(file, data, data_len);
162+
mjs_return(mjs, mjs_mk_boolean(mjs, write == data_len));
163+
}
164+
storage_file_free(file);
165+
}
166+
167+
static void js_storage_exists(struct mjs* mjs) {
168+
JsStorageInst* storage = get_this_ctx(mjs);
169+
if(!check_arg_count(mjs, 1)) return;
170+
171+
const char* path;
172+
if(!get_path_arg(mjs, &path, 0)) return;
173+
174+
mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_exists(storage->api, path)));
175+
}
176+
177+
static void js_storage_remove(struct mjs* mjs) {
178+
JsStorageInst* storage = get_this_ctx(mjs);
179+
if(!check_arg_count(mjs, 1)) return;
180+
181+
const char* path;
182+
if(!get_path_arg(mjs, &path, 0)) return;
183+
184+
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove(storage->api, path)));
185+
}
186+
187+
static void js_storage_copy(struct mjs* mjs) {
188+
JsStorageInst* storage = get_this_ctx(mjs);
189+
if(!check_arg_count(mjs, 2)) return;
190+
191+
const char* old_path;
192+
if(!get_path_arg(mjs, &old_path, 0)) return;
193+
194+
const char* new_path;
195+
if(!get_path_arg(mjs, &new_path, 1)) return;
196+
197+
FS_Error error = storage_common_copy(storage->api, old_path, new_path);
198+
if(error == FSE_OK) {
199+
mjs_return(mjs, MJS_UNDEFINED);
200+
} else {
201+
ret_int_err(mjs, storage_error_get_desc(error));
202+
}
203+
}
204+
205+
static void js_storage_move(struct mjs* mjs) {
206+
JsStorageInst* storage = get_this_ctx(mjs);
207+
if(!check_arg_count(mjs, 2)) return;
208+
209+
const char* old_path;
210+
if(!get_path_arg(mjs, &old_path, 0)) return;
211+
212+
const char* new_path;
213+
if(!get_path_arg(mjs, &new_path, 1)) return;
214+
215+
FS_Error error = storage_common_rename(storage->api, old_path, new_path);
216+
if(error == FSE_OK) {
217+
mjs_return(mjs, MJS_UNDEFINED);
218+
} else {
219+
ret_int_err(mjs, storage_error_get_desc(error));
220+
}
221+
}
222+
223+
static void js_storage_mkdir(struct mjs* mjs) {
224+
JsStorageInst* storage = get_this_ctx(mjs);
225+
if(!check_arg_count(mjs, 1)) return;
226+
227+
const char* path;
228+
if(!get_path_arg(mjs, &path, 0)) return;
229+
230+
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_mkdir(storage->api, path)));
231+
}
232+
233+
static void* js_storage_create(struct mjs* mjs, mjs_val_t* object) {
234+
JsStorageInst* storage = malloc(sizeof(JsStorageInst));
235+
mjs_val_t storage_obj = mjs_mk_object(mjs);
236+
mjs_set(mjs, storage_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, storage));
237+
mjs_set(mjs, storage_obj, "read", ~0, MJS_MK_FN(js_storage_read));
238+
mjs_set(mjs, storage_obj, "write", ~0, MJS_MK_FN(js_storage_write));
239+
mjs_set(mjs, storage_obj, "append", ~0, MJS_MK_FN(js_storage_append));
240+
mjs_set(mjs, storage_obj, "exists", ~0, MJS_MK_FN(js_storage_exists));
241+
mjs_set(mjs, storage_obj, "remove", ~0, MJS_MK_FN(js_storage_remove));
242+
mjs_set(mjs, storage_obj, "copy", ~0, MJS_MK_FN(js_storage_copy));
243+
mjs_set(mjs, storage_obj, "move", ~0, MJS_MK_FN(js_storage_move));
244+
mjs_set(mjs, storage_obj, "mkdir", ~0, MJS_MK_FN(js_storage_mkdir));
245+
storage->api = furi_record_open(RECORD_STORAGE);
246+
*object = storage_obj;
247+
return storage;
248+
}
249+
250+
static void js_storage_destroy(void* inst) {
251+
JsStorageInst* storage = inst;
252+
furi_record_close(RECORD_STORAGE);
253+
free(storage);
254+
}
255+
256+
static const JsModuleDescriptor js_storage_desc = {
257+
"storage",
258+
js_storage_create,
259+
js_storage_destroy,
260+
};
261+
262+
static const FlipperAppPluginDescriptor plugin_descriptor = {
263+
.appid = PLUGIN_APP_ID,
264+
.ep_api_version = PLUGIN_API_VERSION,
265+
.entry_point = &js_storage_desc,
266+
};
267+
268+
const FlipperAppPluginDescriptor* js_storage_ep(void) {
269+
return &plugin_descriptor;
270+
}

0 commit comments

Comments
 (0)