From b054526df818b84e8d6abcb337155295a84eaea0 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 30 Dec 2015 08:13:19 -0800 Subject: [PATCH] Add an optional offset to File.readBytes(). --- doc/site/modules/io/file.markdown | 27 ++++++++++++++----- src/cli/modules.c | 2 +- src/module/io.c | 10 ++++--- src/module/io.wren | 12 ++++++--- src/module/io.wren.inc | 15 ++++++++--- test/io/file/read_bytes.wren | 6 +++++ ...ve.wren => read_bytes_count_negative.wren} | 0 ...wren => read_bytes_count_not_integer.wren} | 0 test/io/file/read_bytes_from.wren | 20 ++++++++++++++ test/io/file/read_bytes_from_after_close.wren | 6 +++++ .../file/read_bytes_from_count_negative.wren | 4 +++ .../read_bytes_from_count_not_integer.wren | 4 +++ .../file/read_bytes_from_count_not_num.wren | 4 +++ .../file/read_bytes_from_offset_negative.wren | 4 +++ .../read_bytes_from_offset_not_integer.wren | 4 +++ .../file/read_bytes_from_offset_not_num.wren | 4 +++ 16 files changed, 104 insertions(+), 18 deletions(-) rename test/io/file/{read_bytes_negative.wren => read_bytes_count_negative.wren} (100%) rename test/io/file/{read_bytes_not_integer.wren => read_bytes_count_not_integer.wren} (100%) create mode 100644 test/io/file/read_bytes_from.wren create mode 100644 test/io/file/read_bytes_from_after_close.wren create mode 100644 test/io/file/read_bytes_from_count_negative.wren create mode 100644 test/io/file/read_bytes_from_count_not_integer.wren create mode 100644 test/io/file/read_bytes_from_count_not_num.wren create mode 100644 test/io/file/read_bytes_from_offset_negative.wren create mode 100644 test/io/file/read_bytes_from_offset_not_integer.wren create mode 100644 test/io/file/read_bytes_from_offset_not_num.wren diff --git a/doc/site/modules/io/file.markdown b/doc/site/modules/io/file.markdown index 60a23f63..9cd717fb 100644 --- a/doc/site/modules/io/file.markdown +++ b/doc/site/modules/io/file.markdown @@ -12,7 +12,7 @@ a file descriptor. ### File.**open**(path, fn) -Opens the file at [path] and passes it to [fn]. After the function returns, the +Opens the file at `path` and passes it to `fn`. After the function returns, the file is automatically closed. :::wren @@ -22,7 +22,7 @@ file is automatically closed. ### File.**read**(path) -Reads the entire contents of the file at [path] and returns it as a string. +Reads the entire contents of the file at `path` and returns it as a string. :::wren File.read("words.txt") @@ -33,13 +33,13 @@ whatever encoding the file uses. ### File.**size**(path) -Returns the size in bytes of the contents of the file at [path]. +Returns the size in bytes of the contents of the file at `path`. ## Constructors ### File.**open**(path) -Opens the file at [path] for reading. +Opens the file at `path` for reading. ## Methods @@ -61,6 +61,21 @@ Closes the file. After calling this, you can read or write from it. ### **readBytes**(count) -Reads up to [count] bytes starting from the beginning of the file. +Reads up to `count` bytes starting from the beginning of the file. -(Allowing an offset to read elsewhere from the file isn't implemented yet.) + :::wren + // Assume this file contains "I am a file!". + File.open("example.txt") {|file| + System.print(file.readBytes(6)) //> I am a + } + +### **readBytes**(count, offset) + +Reads up to `count` bytes starting at `offset` bytes from the beginning of +the file. + + :::wren + // Assume this file contains "I am a file!". + File.open("example.txt") {|file| + System.print(file.readBytes(6, 2)) //> am a f + } diff --git a/src/cli/modules.c b/src/cli/modules.c index 4e6e8e3f..cf4e8075 100644 --- a/src/cli/modules.c +++ b/src/cli/modules.c @@ -99,7 +99,7 @@ static ModuleRegistry modules[] = STATIC_METHOD("sizePath_(_,_)", fileSizePath) METHOD("close_(_)", fileClose) METHOD("descriptor", fileDescriptor) - METHOD("readBytes_(_,_)", fileReadBytes) + METHOD("readBytes_(_,_,_)", fileReadBytes) METHOD("size_(_)", fileSize) END_CLASS CLASS(Stdin) diff --git a/src/module/io.c b/src/module/io.c index 2355d14c..6b6c7c98 100644 --- a/src/module/io.c +++ b/src/module/io.c @@ -228,12 +228,13 @@ static void fileReadBytesCallback(uv_fs_t* request) FileRequestData* data = (FileRequestData*)request->data; uv_buf_t buffer = data->buffer; + size_t count = request->result; // TODO: Having to copy the bytes here is a drag. It would be good if Wren's // embedding API supported a way to *give* it bytes that were previously // allocated using Wren's own allocator. schedulerResume(freeRequest(request), true); - wrenSetSlotBytes(getVM(), 2, buffer.base, buffer.len); + wrenSetSlotBytes(getVM(), 2, buffer.base, count); schedulerFinishResume(); // TODO: Likewise, freeing this after we resume is lame. @@ -242,19 +243,20 @@ static void fileReadBytesCallback(uv_fs_t* request) void fileReadBytes(WrenVM* vm) { - uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2)); + uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3)); int fd = *(int*)wrenGetSlotForeign(vm, 0); // TODO: Assert fd != -1. FileRequestData* data = (FileRequestData*)request->data; size_t length = (size_t)wrenGetSlotDouble(vm, 1); + size_t offset = (size_t)wrenGetSlotDouble(vm, 2); data->buffer.len = length; data->buffer.base = (char*)malloc(length); - // TODO: Allow passing in offset. - uv_fs_read(getLoop(), request, fd, &data->buffer, 1, 0, fileReadBytesCallback); + uv_fs_read(getLoop(), request, fd, &data->buffer, 1, offset, + fileReadBytesCallback); } void fileSize(WrenVM* vm) diff --git a/src/module/io.wren b/src/module/io.wren index a4135a15..ea707624 100644 --- a/src/module/io.wren +++ b/src/module/io.wren @@ -62,13 +62,19 @@ foreign class File { return Scheduler.runNextScheduled_() } - readBytes(count) { + 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.") - readBytes_(count, Fiber.current) + 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.") + + readBytes_(count, offset, Fiber.current) return Scheduler.runNextScheduled_() } @@ -76,7 +82,7 @@ foreign class File { foreign static sizePath_(path, fiber) foreign close_(fiber) - foreign readBytes_(count, fiber) + foreign readBytes_(count, start, fiber) foreign size_(fiber) } diff --git a/src/module/io.wren.inc b/src/module/io.wren.inc index 770657c9..7ba842d5 100644 --- a/src/module/io.wren.inc +++ b/src/module/io.wren.inc @@ -53,6 +53,8 @@ static const char* ioModuleSource = " Scheduler.runNextScheduled_()\n" " }\n" "\n" +" foreign descriptor\n" +"\n" " isOpen { descriptor != -1 }\n" "\n" " size {\n" @@ -62,13 +64,19 @@ static const char* ioModuleSource = " return Scheduler.runNextScheduled_()\n" " }\n" "\n" -" readBytes(count) {\n" +" 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" -" readBytes_(count, Fiber.current)\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" +"\n" +" readBytes_(count, offset, Fiber.current)\n" " return Scheduler.runNextScheduled_()\n" " }\n" "\n" @@ -76,8 +84,7 @@ static const char* ioModuleSource = " foreign static sizePath_(path, fiber)\n" "\n" " foreign close_(fiber)\n" -" foreign descriptor\n" -" foreign readBytes_(count, fiber)\n" +" foreign readBytes_(count, start, fiber)\n" " foreign size_(fiber)\n" "}\n" "\n" diff --git a/test/io/file/read_bytes.wren b/test/io/file/read_bytes.wren index 44297cb4..a9d56a87 100644 --- a/test/io/file/read_bytes.wren +++ b/test/io/file/read_bytes.wren @@ -10,4 +10,10 @@ System.print(file.readBytes(7)) // expect: this is // Allows zero. System.print(file.readBytes(0).bytes.count) // expect: 0 +// A longer number reads the whole file. +System.print(file.readBytes(100)) // expect: this is a text file + +// Reading past the end truncates the buffer. +System.print(file.readBytes(100).bytes.count) // expect: 19 + file.close() diff --git a/test/io/file/read_bytes_negative.wren b/test/io/file/read_bytes_count_negative.wren similarity index 100% rename from test/io/file/read_bytes_negative.wren rename to test/io/file/read_bytes_count_negative.wren diff --git a/test/io/file/read_bytes_not_integer.wren b/test/io/file/read_bytes_count_not_integer.wren similarity index 100% rename from test/io/file/read_bytes_not_integer.wren rename to test/io/file/read_bytes_count_not_integer.wren diff --git a/test/io/file/read_bytes_from.wren b/test/io/file/read_bytes_from.wren new file mode 100644 index 00000000..a05cbc97 --- /dev/null +++ b/test/io/file/read_bytes_from.wren @@ -0,0 +1,20 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") + +// Zero starts at the beginning. +System.print(file.readBytes(3, 0)) // expect: thi + +// Starts at the offset. +System.print(file.readBytes(8, 3)) // expect: s is a t + +// Allows zero. +System.print(file.readBytes(0, 4).bytes.count) // expect: 0 + +// A longer number length reads until the end. +System.print(file.readBytes(100, 2)) // expect: is is a text file + +// An offset past the end returns an empty string. +System.print(file.readBytes(100, 30).bytes.count) // expect: 0 + +file.close() diff --git a/test/io/file/read_bytes_from_after_close.wren b/test/io/file/read_bytes_from_after_close.wren new file mode 100644 index 00000000..4cc8a753 --- /dev/null +++ b/test/io/file/read_bytes_from_after_close.wren @@ -0,0 +1,6 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.close() + +file.readBytes(3, 0) // expect runtime error: File is not open. diff --git a/test/io/file/read_bytes_from_count_negative.wren b/test/io/file/read_bytes_from_count_negative.wren new file mode 100644 index 00000000..9d251303 --- /dev/null +++ b/test/io/file/read_bytes_from_count_negative.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes(-1, 0) // expect runtime error: Count cannot be negative. diff --git a/test/io/file/read_bytes_from_count_not_integer.wren b/test/io/file/read_bytes_from_count_not_integer.wren new file mode 100644 index 00000000..8a2ba576 --- /dev/null +++ b/test/io/file/read_bytes_from_count_not_integer.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes(1.2, 0) // expect runtime error: Count must be an integer. diff --git a/test/io/file/read_bytes_from_count_not_num.wren b/test/io/file/read_bytes_from_count_not_num.wren new file mode 100644 index 00000000..7783b522 --- /dev/null +++ b/test/io/file/read_bytes_from_count_not_num.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes("not num", 0) // expect runtime error: Count must be an integer. diff --git a/test/io/file/read_bytes_from_offset_negative.wren b/test/io/file/read_bytes_from_offset_negative.wren new file mode 100644 index 00000000..5a303082 --- /dev/null +++ b/test/io/file/read_bytes_from_offset_negative.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes(1, -1) // expect runtime error: Offset cannot be negative. diff --git a/test/io/file/read_bytes_from_offset_not_integer.wren b/test/io/file/read_bytes_from_offset_not_integer.wren new file mode 100644 index 00000000..dc573cea --- /dev/null +++ b/test/io/file/read_bytes_from_offset_not_integer.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes(1, 1.2) // expect runtime error: Offset must be an integer. diff --git a/test/io/file/read_bytes_from_offset_not_num.wren b/test/io/file/read_bytes_from_offset_not_num.wren new file mode 100644 index 00000000..528d0097 --- /dev/null +++ b/test/io/file/read_bytes_from_offset_not_num.wren @@ -0,0 +1,4 @@ +import "io" for File + +var file = File.open("test/io/file/file.txt") +file.readBytes(1, "not num") // expect runtime error: Offset must be an integer.