Add an optional offset to File.readBytes().

This commit is contained in:
Bob Nystrom
2015-12-30 08:13:19 -08:00
parent 6e2ec92e0d
commit b054526df8
16 changed files with 104 additions and 18 deletions

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)
}

View File

@ -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"

View File

@ -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()

View File

@ -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()

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.