diff --git a/src/file.js.c b/src/file.js.c index 984859a1..ff77c0e3 100644 --- a/src/file.js.c +++ b/src/file.js.c @@ -21,6 +21,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar static JSValue _file_read_file_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_stat_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); +static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static double _time_spec_to_double(const uv_timespec_t* time_spec); static void _file_on_stat_complete(uv_fs_t* request); @@ -47,6 +48,7 @@ void tf_file_register(JSContext* context) const char* zip = tf_task_get_zip_path(task); JS_SetPropertyStr(context, global, "File", file); JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, zip ? _file_read_file_zip : _file_read_file, "readFile", 1)); + JS_SetPropertyStr(context, file, "writeFile", JS_NewCFunction(context, _file_write_file, "writeFile", 2)); JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, zip ? _file_stat_zip : _file_stat, "stat", 1)); JS_FreeValue(context, global); } @@ -59,6 +61,103 @@ static void _file_async_close_callback(uv_fs_t* req) tf_free(req); } +static void _file_write_write_callback(uv_fs_t* req) +{ + uv_fs_req_cleanup(req); + fs_req_t* fsreq = (fs_req_t*)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) + { + tf_task_resolve_promise(task, promise, JS_NewInt64(context, req->result)); + } + else + { + tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "%s", uv_strerror(req->result))); + } + int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); + if (result < 0) + { + uv_fs_req_cleanup(req); + tf_free(fsreq); + } +} + +static void _file_write_open_callback(uv_fs_t* req) +{ + fs_req_t* fsreq = (fs_req_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) + { + uv_buf_t buf = { .base = fsreq->buffer, .len = fsreq->size }; + fsreq->file = req->result; + int result = uv_fs_write(req->loop, req, fsreq->file, &buf, 1, 0, _file_write_write_callback); + if (result < 0) + { + tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "%s", uv_strerror(result))); + result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); + if (result < 0) + { + uv_fs_req_cleanup(req); + tf_free(fsreq); + } + } + } + else + { + tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "%s", uv_strerror(req->result))); + uv_fs_req_cleanup(req); + tf_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_util_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 = -1; + JSValue promise_value = tf_task_allocate_promise(task, &promise); + fs_req_t* req = tf_malloc(sizeof(fs_req_t) + size); + *req = (fs_req_t) + { + .fs = + { + .data = (void*)(intptr_t)promise, + }, + .size = size, + }; + memcpy(req->buffer, buffer, size); + if (!is_array_buffer) + { + JS_FreeCString(context, (const char*)buffer); + } + + int result = uv_fs_open(tf_task_get_loop(task), &req->fs, file_name, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0644, _file_write_open_callback); + if (result < 0) + { + tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "%s", uv_strerror(result))); + } + JS_FreeCString(context, file_name); + return promise_value; +} + static void _file_read_read_callback(uv_fs_t* req) { uv_fs_req_cleanup(req);