More file IO!

Can now create, delete, and write to files.
This commit is contained in:
Bob Nystrom
2016-02-20 09:23:42 -08:00
parent bec5181bdb
commit 8e90e3577b
21 changed files with 463 additions and 52 deletions
+52 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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"