Async File.writeFile.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3673 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2021-10-27 23:27:21 +00:00
parent 08e32c0de4
commit 07a0828626
3 changed files with 102 additions and 31 deletions

View File

@ -272,11 +272,10 @@ function getGlobalSettings() {
function setGlobalSettings(settings) { function setGlobalSettings(settings) {
makeDirectoryForFile(kGlobalSettingsFile); makeDirectoryForFile(kGlobalSettingsFile);
if (!File.writeFile(kGlobalSettingsFile, JSON.stringify(settings))) {
gGlobalSettings = settings; gGlobalSettings = settings;
} else { return File.writeFile(kGlobalSettingsFile, JSON.stringify(settings)).catch(function(error) {
throw new Error("Unable to save settings."); throw new Error("Unable to save settings: " + error);
} });
} }
var kStaticFiles = [ var kStaticFiles = [

View File

@ -4,6 +4,7 @@
#include <malloc.h> #include <malloc.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include <uv.h> #include <uv.h>
#ifdef _WIN32 #ifdef _WIN32
@ -36,19 +37,21 @@ void tf_file_register(JSContext* context)
JSValue global = JS_GetGlobalObject(context); JSValue global = JS_GetGlobalObject(context);
JSValue file = JS_NewObject(context); JSValue file = JS_NewObject(context);
JS_SetPropertyStr(context, global, "File", file); JS_SetPropertyStr(context, global, "File", file);
JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, _file_read_file, "readFile", 1));
JS_SetPropertyStr(context, file, "writeFile", JS_NewCFunction(context, _file_write_file, "writeFile", 2));
/* TODO: async */
JS_SetPropertyStr(context, file, "makeDirectory", JS_NewCFunction(context, _file_make_directory, "makeDirectory", 1)); JS_SetPropertyStr(context, file, "makeDirectory", JS_NewCFunction(context, _file_make_directory, "makeDirectory", 1));
JS_SetPropertyStr(context, file, "readDirectory", JS_NewCFunction(context, _file_read_directory, "readDirectory", 1)); JS_SetPropertyStr(context, file, "readDirectory", JS_NewCFunction(context, _file_read_directory, "readDirectory", 1));
JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, _file_read_file, "readFile", 1));
JS_SetPropertyStr(context, file, "renameFile", JS_NewCFunction(context, _file_rename_file, "renameFile", 2)); JS_SetPropertyStr(context, file, "renameFile", JS_NewCFunction(context, _file_rename_file, "renameFile", 2));
JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, _file_stat, "stat", 1)); JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, _file_stat, "stat", 1));
JS_SetPropertyStr(context, file, "unlinkFile", JS_NewCFunction(context, _file_unlink_file, "unlinkFile", 1)); JS_SetPropertyStr(context, file, "unlinkFile", JS_NewCFunction(context, _file_unlink_file, "unlinkFile", 1));
JS_SetPropertyStr(context, file, "writeFile", JS_NewCFunction(context, _file_write_file, "writeFile", 2));
JS_FreeValue(context, global); JS_FreeValue(context, global);
} }
static const int k_file_read_max = 4 * 1024 * 1024; static const int k_file_read_max = 4 * 1024 * 1024;
static void _file_read_close_callback(uv_fs_t* req) static void _file_async_close_callback(uv_fs_t* req)
{ {
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
free(req); free(req);
@ -76,7 +79,7 @@ static void _file_read_read_callback(uv_fs_t* req)
{ {
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result))); tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
} }
int result = uv_fs_close(req->loop, req, req->file, _file_read_close_callback); int result = uv_fs_close(req->loop, req, req->file, _file_async_close_callback);
if (result < 0) if (result < 0)
{ {
free(req); free(req);
@ -97,7 +100,7 @@ static void _file_read_open_callback(uv_fs_t* req)
if (result < 0) if (result < 0)
{ {
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result))); tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
result = uv_fs_close(req->loop, req, file, _file_read_close_callback); result = uv_fs_close(req->loop, req, file, _file_async_close_callback);
} }
} }
else else
@ -127,32 +130,87 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
return tf_task_get_promise(task, promise); return tf_task_get_promise(task, promise);
} }
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static void _file_write_write_callback(uv_fs_t* req)
{ {
JSValue result = JS_NULL; uv_fs_req_cleanup(req);
const char* fileName = JS_ToCString(context, argv[0]); tf_task_t* task = req->loop->data;
FILE* file = fopen(fileName, "wb"); JSContext* context = tf_task_get_context(task);
JS_FreeCString(context, fileName); promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
if (file)
{ {
size_t size; tf_task_resolve_promise(task, promise, JS_NewInt64(context, req->result));
uint8_t* buffer = tf_try_get_array_buffer(context, &size, argv[1]);
if (buffer)
{
int written = fwrite((const char*)buffer, 1, size, file);
result = JS_NewInt32(context, (size_t)written == size ? 0 : written);
} }
else else
{ {
const char* data = JS_ToCStringLen(context, &size, argv[1]); tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
int written = fwrite((const char*)data, 1, size, file);
result = JS_NewInt32(context, (size_t)written == size ? 0 : written);
JS_FreeCString(context, data);
} }
fclose(file); uv_fs_close(req->loop, req, req->file, _file_async_close_callback);
}
static void _file_write_open_callback(uv_fs_t* req)
{
uv_fs_req_cleanup(req);
tf_task_t* task = req->loop->data;
JSContext* context = tf_task_get_context(task);
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
{
size_t size = 0;
memcpy(&size, req + 1, sizeof(size));
uv_buf_t buf = { .base = (char*)(req + 1) + sizeof(size), .len = size };
uv_file file = req->result;
int result = uv_fs_write(req->loop, req, file, &buf, 1, 0, _file_write_write_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
result = uv_fs_close(req->loop, req, file, _file_async_close_callback);
} }
return result; }
else
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
free(req);
}
}
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
void* task = JS_GetContextOpaque(context);
const char* file_name = JS_ToCString(context, argv[0]);
size_t size;
uint8_t* buffer = tf_try_get_array_buffer(context, &size, argv[1]);
bool is_array_buffer = false;
if (buffer)
{
is_array_buffer = true;
}
else
{
buffer = (uint8_t*)JS_ToCStringLen(context, &size, argv[1]);
}
promiseid_t promise = tf_task_allocate_promise(task);
JSValue promise_value = tf_task_get_promise(task, promise);
uv_fs_t* req = malloc(sizeof(uv_fs_t) + sizeof(size_t) + size);
*req = (uv_fs_t)
{
.data = (void*)(intptr_t)promise,
};
memcpy(req + 1, &size, sizeof(size_t));
memcpy((char*)(req + 1) + sizeof(size_t), buffer, size);
if (!is_array_buffer)
{
JS_FreeCString(context, (const char*)buffer);
}
int result = uv_fs_open(tf_task_get_loop(task), req, file_name, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0755, _file_write_open_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
}
JS_FreeCString(context, file_name);
return promise_value;
} }
static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)

View File

@ -506,6 +506,20 @@ static void _test_file(const tf_test_options_t* options)
" exit(1);\n" " exit(1);\n"
"}).catch(function(error) {\n" "}).catch(function(error) {\n"
" print('expected error', error);\n" " print('expected error', error);\n"
"});\n"
"File.writeFile('out/new.txt', 'hello').then(function(result) {\n"
" File.readFile('out/new.txt').then(function(data) {\n"
" print('READ', utf8Decode(data));\n"
" if (utf8Decode(data) != 'hello') {\n"
" exit(1);\n"
" }\n"
" }).catch(function(error) {\n"
" print('unexpected read error', error);\n"
" exit(1);\n"
" });\n"
"}).catch(function(error) {\n"
" print('unexpected write error', error);\n"
" exit(1);\n"
"});\n"); "});\n");
fclose(file); fclose(file);