Skip to content

Commit 37d1189

Browse files
phischub-studios
andauthored
Specialize on most common opening flags (#816)
Should fix #809 Please test on Windows and Mac --------- Co-authored-by: Jonathan Brachthäuser <[email protected]>
1 parent a0021e5 commit 37d1189

File tree

5 files changed

+108
-107
lines changed

5 files changed

+108
-107
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
hello!
2+
hello! world!
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import io
2+
import io/filesystem
3+
import io/error
4+
5+
import bytearray
6+
7+
def writeHello(path: String) = {
8+
val file = openForWriting(path)
9+
val buffer = fromString("hello!")
10+
val n = file.write(buffer, 0, buffer.size, 0)
11+
if (n < buffer.size) {
12+
panic("write failed")
13+
}
14+
file.close()
15+
}
16+
17+
def readContents(path: String) = {
18+
val file = openForReading(path)
19+
val buffer = bytearray::allocate(64)
20+
val n = file.read(buffer, 0, buffer.size, 0)
21+
file.close()
22+
buffer.resize(n).toString
23+
}
24+
25+
def appendWorld(path: String) = {
26+
val file = openForAppending(path)
27+
val buffer = fromString(" world!")
28+
val n = file.write(buffer, 0, buffer.size, -1)
29+
if (n < buffer.size) {
30+
panic("append failed")
31+
}
32+
file.close()
33+
}
34+
35+
def main() = {
36+
with on[IOError].panic;
37+
val filename = "/tmp/some_file_to_write.txt"
38+
writeHello(filename)
39+
println(readContents(filename))
40+
appendWorld(filename)
41+
println(readContents(filename))
42+
}

libraries/common/io/filesystem.effekt

Lines changed: 49 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,13 @@ import io
66
import io/error
77

88

9-
/// Represents the file opening modes with their respective flags
10-
/// Backends rely on the order of these
11-
type Mode {
12-
// Most common modes
13-
ReadOnly() // 'r' exception if does not exist
14-
WriteOnly() // 'w' created if does not exist, truncated if exists
15-
AppendOnly() // 'a' created if does not exist
16-
17-
// Other modes
18-
ReadWrite() // 'w+' created if does not exist, truncated if exists
19-
ReadAppend() // 'a+' created if does not exist
20-
AppendExclusive() // 'ax' created if does not exist, fails if exists
21-
ReadAppendExclusive() // 'ax+' created if does not exist, fails if exists
22-
AppendSync() // 'as' created if does not exist, append in synchronous mode
23-
ReadAppendSync() // 'as+' created if does not exist, append in synchronous mode
24-
ReadSync() // 'rs' exception if does not exist, read in synchronous mode
25-
ReadWriteSync() // 'rs+' exception if does not exist, read/write in synchronous mode
26-
WriteExclusive() // 'wx' created if does not exist, truncated if exists, fails if exists
27-
ReadWriteExclusive() // 'wx+' created if does not exist, truncated if exists, fails if exists
28-
}
29-
30-
319
/// A file descriptor. Should not be inspected.
3210
type File = Int
3311

3412

3513
/// Reads a file at given path as utf8 encoded string.
3614
def readFile(path: String): String / Exception[IOError] = {
37-
val file = open(path, ReadOnly());
15+
val file = openForReading(path);
3816
with on[IOError].finalize { close(file) }
3917

4018
val chunkSize = 1048576 // 1MB
@@ -59,7 +37,7 @@ def readFile(path: String): String / Exception[IOError] = {
5937

6038
/// Writes the (utf8 encoded) string `contents` into the specified file.
6139
def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
62-
val file = open(path, WriteOnly());
40+
val file = openForWriting(path);
6341
with on[IOError].finalize { close(file) }
6442

6543
val chunkSize = 1048576 // 1MB
@@ -75,6 +53,23 @@ def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
7553
go()
7654
}
7755

56+
/// Appends the (utf8 encoded) string `contents` to the specified file.
57+
def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
58+
val file = openForAppending(path);
59+
with on[IOError].finalize { close(file) }
60+
61+
val chunkSize = 1048576 // 1MB
62+
val buffer = contents.fromString
63+
var offset = 0;
64+
65+
def go(): Unit = {
66+
val n = write(file, buffer, offset, min(buffer.size - offset, chunkSize), -1)
67+
offset = offset + n
68+
if (offset < buffer.size) { go() }
69+
}
70+
71+
go()
72+
}
7873

7974
/// An abstract interface applications can program against.
8075
///
@@ -95,8 +90,14 @@ def filesystem[R] { program: => R / Files }: R / Exception[IOError] = // TODO mo
9590
}
9691

9792

98-
def open(path: String, mode: Mode): File / Exception[IOError] =
99-
internal::checkResult(internal::open(path, mode))
93+
def openForReading(path: String): File / Exception[IOError] =
94+
internal::checkResult(internal::openForReading(path))
95+
96+
def openForWriting(path: String): File / Exception[IOError] =
97+
internal::checkResult(internal::openForWriting(path))
98+
99+
def openForAppending(path: String): File / Exception[IOError] =
100+
internal::checkResult(internal::openForAppending(path))
100101

101102
def read(file: File, buffer: ByteArray, offset: Int, size: Int, position: Int): Int / Exception[IOError] =
102103
internal::checkResult(internal::read(file, buffer, offset, size, position))
@@ -111,40 +112,6 @@ def close(file: File): Unit / Exception[IOError] = {
111112
namespace internal {
112113

113114
extern js """
114-
function modeName(mode) {
115-
switch (mode.__tag) {
116-
case 0: // ReadOnly()
117-
return 'r';
118-
case 1: // WriteOnly()
119-
return 'w';
120-
case 2: // AppendOnly()
121-
return 'a';
122-
case 3: // ReadWrite()
123-
return 'w+';
124-
case 4: // ReadAppend()
125-
return 'a+';
126-
case 5: // AppendExclusive()
127-
return 'ax';
128-
case 6: // ReadAppendExclusive()
129-
return 'ax+';
130-
case 7: // AppendSync()
131-
return 'as';
132-
case 8: // ReadAppendSync()
133-
return 'as+';
134-
case 9: // ReadSync()
135-
return 'rs';
136-
case 10: // ReadWriteSync()
137-
return 'rs+';
138-
case 11: // WriteExclusive()
139-
return 'wx';
140-
case 12: // ReadWriteExclusive()
141-
return 'wx+';
142-
default:
143-
// Invalid tag value
144-
return null;
145-
}
146-
}
147-
148115
/**
149116
* Nodejs file operations expect buffers, but we represent buffers as typed arrays.
150117
* This function converts between the two without copying.
@@ -157,8 +124,8 @@ namespace internal {
157124
extern jsNode """
158125
const fs = require("fs");
159126

160-
function open(path, mode, callback) {
161-
fs.open(path, modeName(mode), (err, file) => {
127+
function open(path, flags, callback) {
128+
fs.open(path, flags, (err, file) => {
162129
if (err) { callback(err.errno) } else { callback(file) }
163130
})
164131
}
@@ -185,16 +152,32 @@ namespace internal {
185152
"""
186153

187154
extern llvm """
188-
declare void @c_fs_open(%Pos, %Pos, %Stack)
155+
declare void @c_fs_open_reading(%Pos, %Stack)
156+
declare void @c_fs_open_writing(%Pos, %Stack)
157+
declare void @c_fs_open_appending(%Pos, %Stack)
189158
declare void @c_fs_read(%Int, %Pos, %Int, %Int, %Int, %Stack)
190159
declare void @c_fs_write(%Int, %Pos, %Int, %Int, %Int, %Stack)
191160
declare void @c_fs_close(%Int, %Stack)
192161
"""
193162

194-
extern async def open(path: String, mode: Mode): Int =
195-
jsNode "$effekt.capture(callback => open(${path}, ${mode}, callback))"
163+
extern async def openForReading(path: String): Int =
164+
jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
165+
llvm """
166+
call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
167+
ret void
168+
"""
169+
170+
extern async def openForWriting(path: String): Int =
171+
jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
172+
llvm """
173+
call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
174+
ret void
175+
"""
176+
177+
extern async def openForAppending(path: String): Int =
178+
jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
196179
llvm """
197-
call void @c_fs_open(%Pos ${path}, %Pos ${mode}, %Stack %stack)
180+
call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
198181
ret void
199182
"""
200183

libraries/common/stream.effekt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def zip[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B]
263263

264264
def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
265265

266-
val file = open(path, WriteOnly());
266+
val file = openForWriting(path);
267267
with on[IOError].finalize { close(file) }
268268

269269
val chunkSize = 1048576 // 1MB
@@ -291,7 +291,7 @@ def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[I
291291

292292
def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
293293

294-
val file = open(path, ReadOnly());
294+
val file = openForReading(path);
295295
with on[IOError].finalize { close(file) }
296296

297297
val chunkSize = 1048576 // 1MB

libraries/llvm/io.c

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,50 +62,12 @@ void c_resume_int_fs(uv_fs_t* request) {
6262
resume_Int(stack, result);
6363
}
6464

65-
int modeFlags(struct Pos mode) {
66-
switch (mode.tag) {
67-
case 0: // ReadOnly()
68-
return UV_FS_O_RDONLY;
69-
case 1: // WriteOnly()
70-
return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC;
71-
case 2: // AppendOnly()
72-
return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND;
73-
case 3: // ReadWrite()
74-
return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC;
75-
case 4: // ReadAppend()
76-
return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND;
77-
case 5: // AppendExclusive()
78-
return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_EXCL;
79-
case 6: // ReadAppendExclusive()
80-
return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_EXCL;
81-
case 7: // AppendSync()
82-
return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_SYNC;
83-
case 8: // ReadAppendSync()
84-
return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_SYNC;
85-
case 9: // ReadSync()
86-
return UV_FS_O_RDONLY | UV_FS_O_SYNC;
87-
case 10: // ReadWriteSync()
88-
return UV_FS_O_RDWR | UV_FS_O_SYNC;
89-
case 11: // WriteExclusive()
90-
return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL;
91-
case 12: // ReadWriteExclusive()
92-
return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL;
93-
default:
94-
// Invalid tag value
95-
return -1;
96-
}
97-
}
98-
99-
void c_fs_open(String path, struct Pos mode, Stack stack) {
65+
void c_fs_open(String path, int flags, Stack stack) {
10066

10167
// Convert the Effekt String to a 0-terminated string
10268
char* path_str = c_bytearray_into_nullterminated_string(path);
10369
erasePositive((struct Pos) path);
10470

105-
// Convert the Effekt String representing the opening mode to libuv flags
106-
int flags = modeFlags(mode);
107-
erasePositive((struct Pos) mode);
108-
10971
uv_fs_t* request = malloc(sizeof(uv_fs_t));
11072
request->data = stack;
11173

@@ -123,6 +85,18 @@ void c_fs_open(String path, struct Pos mode, Stack stack) {
12385
return;
12486
}
12587

88+
void c_fs_open_reading(String path, Stack stack) {
89+
c_fs_open(path, UV_FS_O_RDONLY, stack);
90+
}
91+
92+
void c_fs_open_writing(String path, Stack stack) {
93+
c_fs_open(path, UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC, stack);
94+
}
95+
96+
void c_fs_open_appending(String path, Stack stack) {
97+
c_fs_open(path, UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND, stack);
98+
}
99+
126100
void c_fs_read(Int file, struct Pos buffer, Int offset, Int size, Int position, Stack stack) {
127101

128102
uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size);

0 commit comments

Comments
 (0)