More file IO!
Can now create, delete, and write to files.
This commit is contained in:
+52
-7
@@ -124,7 +124,6 @@ static void directoryListCallback(uv_fs_t* request)
|
||||
void directoryList(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
|
||||
// TODO: Check return.
|
||||
@@ -151,6 +150,21 @@ void fileFinalize(void* data)
|
||||
uv_fs_req_cleanup(&request);
|
||||
}
|
||||
|
||||
static void fileDeleteCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
schedulerResume(freeRequest(request), false);
|
||||
}
|
||||
|
||||
void fileDelete(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
|
||||
// TODO: Check return.
|
||||
uv_fs_unlink(getLoop(), request, path, fileDeleteCallback);
|
||||
}
|
||||
|
||||
static void fileOpenCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
@@ -164,10 +178,12 @@ static void fileOpenCallback(uv_fs_t* request)
|
||||
void fileOpen(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
int flags = (int)wrenGetSlotDouble(vm, 2);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3));
|
||||
|
||||
// TODO: Allow controlling flags and modes.
|
||||
uv_fs_open(getLoop(), request, path, O_RDONLY, 0, fileOpenCallback);
|
||||
// TODO: Allow controlling access.
|
||||
uv_fs_open(getLoop(), request, path, flags, S_IRUSR | S_IWUSR,
|
||||
fileOpenCallback);
|
||||
}
|
||||
|
||||
// Called by libuv when the stat call for size completes.
|
||||
@@ -261,14 +277,43 @@ void fileReadBytes(WrenVM* vm)
|
||||
|
||||
void fileSize(WrenVM* vm)
|
||||
{
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 1));
|
||||
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
// TODO: Assert fd != -1.
|
||||
|
||||
uv_fs_fstat(getLoop(), request, fd, fileSizeCallback);
|
||||
}
|
||||
|
||||
static void fileWriteBytesCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
free(data->buffer.base);
|
||||
|
||||
schedulerResume(freeRequest(request), false);
|
||||
}
|
||||
|
||||
void fileWriteBytes(WrenVM* vm)
|
||||
{
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
int length;
|
||||
const char* bytes = wrenGetSlotBytes(vm, 1, &length);
|
||||
size_t offset = (size_t)wrenGetSlotDouble(vm, 2);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3));
|
||||
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
|
||||
data->buffer.len = length;
|
||||
// TODO: Instead of copying, just create a WrenValue for the byte string and
|
||||
// hold on to it in the request until the write is done.
|
||||
// TODO: Handle allocation failure.
|
||||
data->buffer.base = (char*)malloc(length);
|
||||
memcpy(data->buffer.base, bytes, length);
|
||||
|
||||
uv_fs_write(getLoop(), request, fd, &data->buffer, 1, offset,
|
||||
fileWriteBytesCallback);
|
||||
}
|
||||
|
||||
// Called by libuv when the stat call completes.
|
||||
static void statPathCallback(uv_fs_t* request)
|
||||
{
|
||||
|
||||
+80
-20
@@ -1,9 +1,13 @@
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
class Directory {
|
||||
static list(path) {
|
||||
// TODO: Copied from File. Figure out good way to share this.
|
||||
static ensurePath_(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
}
|
||||
|
||||
static list(path) {
|
||||
ensurePath_(path)
|
||||
list_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
@@ -12,16 +16,42 @@ class Directory {
|
||||
}
|
||||
|
||||
foreign class File {
|
||||
static open(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
static create(path) {
|
||||
return openWithFlags(path,
|
||||
FileFlags.writeOnly |
|
||||
FileFlags.create |
|
||||
FileFlags.truncate)
|
||||
}
|
||||
|
||||
open_(path, Fiber.current)
|
||||
static create(path, fn) {
|
||||
return openWithFlags(path,
|
||||
FileFlags.writeOnly |
|
||||
FileFlags.create |
|
||||
FileFlags.truncate, fn)
|
||||
}
|
||||
|
||||
static delete(path) {
|
||||
File.ensurePath_(path)
|
||||
delete_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
static open(path) { openWithFlags(path, FileFlags.readOnly) }
|
||||
|
||||
static open(path, fn) { openWithFlags(path, FileFlags.readOnly, fn) }
|
||||
|
||||
// TODO: Add named parameters and then call this "open(_,flags:_)"?
|
||||
// TODO: Test.
|
||||
static openWithFlags(path, flags) {
|
||||
File.ensurePath_(path)
|
||||
File.ensureInt_(flags, "Flags")
|
||||
open_(path, flags, Fiber.current)
|
||||
var fd = Scheduler.runNextScheduled_()
|
||||
return new_(fd)
|
||||
}
|
||||
|
||||
static open(path, fn) {
|
||||
var file = open(path)
|
||||
static openWithFlags(path, flags, fn) {
|
||||
var file = openWithFlags(path, flags)
|
||||
var fiber = Fiber.new { fn.call(file) }
|
||||
|
||||
// Poor man's finally. Can we make this more elegant?
|
||||
@@ -38,8 +68,7 @@ foreign class File {
|
||||
}
|
||||
|
||||
static size(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
File.ensurePath_(path)
|
||||
sizePath_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
@@ -56,8 +85,7 @@ foreign class File {
|
||||
isOpen { descriptor != -1 }
|
||||
|
||||
size {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
|
||||
ensureOpen_()
|
||||
size_(Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
@@ -65,25 +93,57 @@ foreign class File {
|
||||
readBytes(count) { readBytes(count, 0) }
|
||||
|
||||
readBytes(count, offset) {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
if (!(count is Num)) Fiber.abort("Count must be an integer.")
|
||||
if (!count.isInteger) Fiber.abort("Count must be an integer.")
|
||||
if (count < 0) Fiber.abort("Count cannot be negative.")
|
||||
|
||||
if (!(offset is Num)) Fiber.abort("Offset must be an integer.")
|
||||
if (!offset.isInteger) Fiber.abort("Offset must be an integer.")
|
||||
if (offset < 0) Fiber.abort("Offset cannot be negative.")
|
||||
ensureOpen_()
|
||||
File.ensureInt_(count, "Count")
|
||||
File.ensureInt_(offset, "Offset")
|
||||
|
||||
readBytes_(count, offset, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign static open_(path, fiber)
|
||||
writeBytes(bytes) { writeBytes(bytes, size) }
|
||||
|
||||
writeBytes(bytes, offset) {
|
||||
ensureOpen_()
|
||||
if (!(bytes is String)) Fiber.abort("Bytes must be a string.")
|
||||
File.ensureInt_(offset, "Offset")
|
||||
|
||||
writeBytes_(bytes, offset, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
ensureOpen_() {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
}
|
||||
|
||||
static ensurePath_(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
}
|
||||
|
||||
static ensureInt_(value, name) {
|
||||
if (!(value is Num)) Fiber.abort("%(name) must be an integer.")
|
||||
if (!value.isInteger) Fiber.abort("%(name) must be an integer.")
|
||||
if (value < 0) Fiber.abort("%(name) cannot be negative.")
|
||||
}
|
||||
|
||||
foreign static delete_(path, fiber)
|
||||
foreign static open_(path, flags, fiber)
|
||||
foreign static sizePath_(path, fiber)
|
||||
|
||||
foreign close_(fiber)
|
||||
foreign readBytes_(count, start, fiber)
|
||||
foreign readBytes_(count, offset, fiber)
|
||||
foreign size_(fiber)
|
||||
foreign writeBytes_(bytes, offset, fiber)
|
||||
}
|
||||
|
||||
class FileFlags {
|
||||
static readOnly { 0x0000 }
|
||||
static writeOnly { 0x0001 }
|
||||
static readWrite { 0x0002 }
|
||||
static sync { 0x0080 }
|
||||
static create { 0x0200 }
|
||||
static truncate { 0x0400 }
|
||||
static exclusive { 0x0400 }
|
||||
}
|
||||
|
||||
class Stat {
|
||||
|
||||
+80
-20
@@ -3,9 +3,13 @@ static const char* ioModuleSource =
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"class Directory {\n"
|
||||
" static list(path) {\n"
|
||||
" // TODO: Copied from File. Figure out good way to share this.\n"
|
||||
" static ensurePath_(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static list(path) {\n"
|
||||
" ensurePath_(path)\n"
|
||||
" list_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
@@ -14,16 +18,42 @@ static const char* ioModuleSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class File {\n"
|
||||
" static open(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
" static create(path) {\n"
|
||||
" return openWithFlags(path,\n"
|
||||
" FileFlags.writeOnly |\n"
|
||||
" FileFlags.create |\n"
|
||||
" FileFlags.truncate)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" open_(path, Fiber.current)\n"
|
||||
" static create(path, fn) {\n"
|
||||
" return openWithFlags(path,\n"
|
||||
" FileFlags.writeOnly |\n"
|
||||
" FileFlags.create |\n"
|
||||
" FileFlags.truncate, fn)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static delete(path) {\n"
|
||||
" File.ensurePath_(path)\n"
|
||||
" delete_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static open(path) { openWithFlags(path, FileFlags.readOnly) }\n"
|
||||
"\n"
|
||||
" static open(path, fn) { openWithFlags(path, FileFlags.readOnly, fn) }\n"
|
||||
"\n"
|
||||
" // TODO: Add named parameters and then call this \"open(_,flags:_)\"?\n"
|
||||
" // TODO: Test.\n"
|
||||
" static openWithFlags(path, flags) {\n"
|
||||
" File.ensurePath_(path)\n"
|
||||
" File.ensureInt_(flags, \"Flags\")\n"
|
||||
" open_(path, flags, Fiber.current)\n"
|
||||
" var fd = Scheduler.runNextScheduled_()\n"
|
||||
" return new_(fd)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static open(path, fn) {\n"
|
||||
" var file = open(path)\n"
|
||||
" static openWithFlags(path, flags, fn) {\n"
|
||||
" var file = openWithFlags(path, flags)\n"
|
||||
" var fiber = Fiber.new { fn.call(file) }\n"
|
||||
"\n"
|
||||
" // Poor man's finally. Can we make this more elegant?\n"
|
||||
@@ -40,8 +70,7 @@ static const char* ioModuleSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" static size(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" File.ensurePath_(path)\n"
|
||||
" sizePath_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
@@ -58,8 +87,7 @@ static const char* ioModuleSource =
|
||||
" isOpen { descriptor != -1 }\n"
|
||||
"\n"
|
||||
" size {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
"\n"
|
||||
" ensureOpen_()\n"
|
||||
" size_(Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
@@ -67,25 +95,57 @@ static const char* ioModuleSource =
|
||||
" readBytes(count) { readBytes(count, 0) }\n"
|
||||
"\n"
|
||||
" readBytes(count, offset) {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
" if (!(count is Num)) Fiber.abort(\"Count must be an integer.\")\n"
|
||||
" if (!count.isInteger) Fiber.abort(\"Count must be an integer.\")\n"
|
||||
" if (count < 0) Fiber.abort(\"Count cannot be negative.\")\n"
|
||||
"\n"
|
||||
" if (!(offset is Num)) Fiber.abort(\"Offset must be an integer.\")\n"
|
||||
" if (!offset.isInteger) Fiber.abort(\"Offset must be an integer.\")\n"
|
||||
" if (offset < 0) Fiber.abort(\"Offset cannot be negative.\")\n"
|
||||
" ensureOpen_()\n"
|
||||
" File.ensureInt_(count, \"Count\")\n"
|
||||
" File.ensureInt_(offset, \"Offset\")\n"
|
||||
"\n"
|
||||
" readBytes_(count, offset, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static open_(path, fiber)\n"
|
||||
" writeBytes(bytes) { writeBytes(bytes, size) }\n"
|
||||
"\n"
|
||||
" writeBytes(bytes, offset) {\n"
|
||||
" ensureOpen_()\n"
|
||||
" if (!(bytes is String)) Fiber.abort(\"Bytes must be a string.\")\n"
|
||||
" File.ensureInt_(offset, \"Offset\")\n"
|
||||
"\n"
|
||||
" writeBytes_(bytes, offset, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" ensureOpen_() {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static ensurePath_(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static ensureInt_(value, name) {\n"
|
||||
" if (!(value is Num)) Fiber.abort(\"%(name) must be an integer.\")\n"
|
||||
" if (!value.isInteger) Fiber.abort(\"%(name) must be an integer.\")\n"
|
||||
" if (value < 0) Fiber.abort(\"%(name) cannot be negative.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static delete_(path, fiber)\n"
|
||||
" foreign static open_(path, flags, fiber)\n"
|
||||
" foreign static sizePath_(path, fiber)\n"
|
||||
"\n"
|
||||
" foreign close_(fiber)\n"
|
||||
" foreign readBytes_(count, start, fiber)\n"
|
||||
" foreign readBytes_(count, offset, fiber)\n"
|
||||
" foreign size_(fiber)\n"
|
||||
" foreign writeBytes_(bytes, offset, fiber)\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class FileFlags {\n"
|
||||
" static readOnly { 0x0000 }\n"
|
||||
" static writeOnly { 0x0001 }\n"
|
||||
" static readWrite { 0x0002 }\n"
|
||||
" static sync { 0x0080 }\n"
|
||||
" static create { 0x0200 }\n"
|
||||
" static truncate { 0x0400 }\n"
|
||||
" static exclusive { 0x0400 }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Stat {\n"
|
||||
|
||||
Reference in New Issue
Block a user