Skip to content

Commit 1a746a1

Browse files
committed
[mycpp] POSIX I/O experiment
Signed-off-by: Andrii Sultanov <[email protected]>
1 parent 252d42b commit 1a746a1

File tree

4 files changed

+54
-52
lines changed

4 files changed

+54
-52
lines changed

cpp/stdlib.cc

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,13 @@ void putenv(BigStr* name, BigStr* value) {
7070

7171
mylib::File* fdopen(int fd, BigStr* c_mode) {
7272
// CPython checks if it's a directory first
73+
7374
struct stat buf;
7475
if (fstat(fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
7576
throw Alloc<OSError>(EISDIR);
7677
}
7778

78-
// CPython does some fcntl() stuff with mode == 'a', which we don't support
79-
DCHECK(c_mode->data_[0] != 'a');
80-
81-
FILE* f = ::fdopen(fd, c_mode->data_);
82-
if (f == nullptr) {
83-
throw Alloc<OSError>(errno);
84-
}
85-
86-
return Alloc<mylib::CFile>(f);
79+
return Alloc<mylib::CFile>(fd);
8780
}
8881

8982
void execve(BigStr* argv0, List<BigStr*>* argv,

mycpp/gc_mylib.cc

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "mycpp/gc_mylib.h"
22

33
#include <errno.h>
4+
#include <fcntl.h>
45
#include <math.h>
56
#include <stdio.h>
67
#include <unistd.h> // isatty
@@ -104,51 +105,58 @@ Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim) {
104105
LineReader* gStdin;
105106

106107
LineReader* open(BigStr* path) {
107-
// TODO: Don't use C I/O; use POSIX I/O!
108-
FILE* f = fopen(path->data_, "r");
109-
if (f == nullptr) {
108+
int fd = ::open(path->data_, O_RDONLY);
109+
110+
if (fd < 0) {
110111
throw Alloc<IOError>(errno);
111112
}
112113

113-
return reinterpret_cast<LineReader*>(Alloc<CFile>(f));
114+
return reinterpret_cast<LineReader*>(Alloc<CFile>(fd));
114115
}
115116

116117
BigStr* CFile::readline() {
117-
char* line = nullptr;
118-
size_t allocated_size = 0; // unused
119-
120118
// Reset errno because we turn the EOF error into empty string (like Python).
121119
errno = 0;
122-
ssize_t len = getline(&line, &allocated_size, f_);
123-
// log("getline = %d", len);
124-
if (len < 0) {
125-
// Reset EOF flag so the next readline() will get a line.
126-
clearerr(f_);
127-
128-
// man page says the buffer should be freed even if getline fails
129-
free(line);
130-
131-
// Raise KeyboardInterrupt like mylib.Stdin().readline() does in Python!
132-
// This affects _PlainPromptInput() in frontend/reader.py.
133-
if (errno == EINTR && iolib::gSignalSafe->PollUntrappedSigInt()) {
134-
throw Alloc<KeyboardInterrupt>();
135-
}
136120

137-
if (errno != 0) { // Unexpected error
138-
// log("getline() error: %s", strerror(errno));
139-
throw Alloc<IOError>(errno);
140-
}
141-
return kEmptyString; // Indicate EOF with empty string, like Python
121+
size_t i = 0;
122+
bool null_terminated = false;
123+
for (; i < 131071; i++) {
124+
ssize_t len = read(fd_, &(line_[i]), 1);
125+
if (len == 0) {
126+
line_[i] = '\0';
127+
null_terminated = true;
128+
break;
129+
} else if (line_[i] == '\n') {
130+
line_[i+1] = '\0';
131+
i++;
132+
null_terminated = true;
133+
break;
134+
} else if (line_[i] == '\0') {
135+
null_terminated = true;
136+
break;
137+
} else if (len < 0) {
138+
// Raise KeyboardInterrupt like mylib.Stdin().readline() does in Python!
139+
// This affects _PlainPromptInput() in frontend/reader.py.
140+
if (errno == EINTR && iolib::gSignalSafe->PollUntrappedSigInt()) {
141+
throw Alloc<KeyboardInterrupt>();
142+
}
143+
144+
if (errno != 0) { // Unexpected error
145+
throw Alloc<IOError>(errno);
146+
}
147+
return kEmptyString; // Indicate EOF with empty string, like Python
148+
}
142149
}
150+
if (!null_terminated)
151+
throw Alloc<IOError>(-1); // Line too long, didn't reach the newline char
152+
153+
BigStr* result = ::StrFromC(line_, i);
143154

144-
// Note: getline() NUL-terminates the buffer
145-
BigStr* result = ::StrFromC(line, len);
146-
free(line);
147155
return result;
148156
}
149157

150158
bool CFile::isatty() {
151-
return ::isatty(fileno(f_));
159+
return ::isatty(fd_);
152160
}
153161

154162
// Problem: most BigStr methods like index() and slice() COPY so they have a
@@ -197,21 +205,19 @@ Writer* gStderr;
197205
void CFile::write(BigStr* s) {
198206
// Writes can be short!
199207
int n = len(s);
200-
int num_written = ::fwrite(s->data_, sizeof(char), n, f_);
208+
int num_written = ::write(fd_, s->data_, n);
201209
// Similar to CPython fileobject.c
202210
if (num_written != n) {
203211
throw Alloc<IOError>(errno);
204212
}
205213
}
206214

207215
void CFile::flush() {
208-
if (::fflush(f_) != 0) {
209-
throw Alloc<IOError>(errno);
210-
}
216+
// no-op for now
211217
}
212218

213219
void CFile::close() {
214-
if (::fclose(f_) != 0) {
220+
if (::close(fd_) != 0) {
215221
throw Alloc<IOError>(errno);
216222
}
217223
}

mycpp/gc_mylib.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "mycpp/gc_tuple.h"
1010

1111
#include <sys/stat.h>
12+
#include <unistd.h>
1213

1314
template <class K, class V>
1415
class Dict;
@@ -155,7 +156,7 @@ class File {
155156
// Wrap a FILE* for read and write
156157
class CFile : public File {
157158
public:
158-
explicit CFile(FILE* f) : File(), f_(f) {
159+
explicit CFile(int fd) : File(), fd_(fd) {
159160
}
160161
// Writer
161162
void write(BigStr* s) override;
@@ -178,7 +179,8 @@ class CFile : public File {
178179
}
179180

180181
private:
181-
FILE* f_;
182+
int fd_;
183+
char line_[131072];
182184

183185
DISALLOW_COPY_AND_ASSIGN(CFile)
184186
};
@@ -234,7 +236,7 @@ extern LineReader* gStdin;
234236

235237
inline LineReader* Stdin() {
236238
if (gStdin == nullptr) {
237-
gStdin = reinterpret_cast<LineReader*>(Alloc<CFile>(stdin));
239+
gStdin = reinterpret_cast<LineReader*>(Alloc<CFile>(STDIN_FILENO));
238240
}
239241
return gStdin;
240242
}
@@ -331,7 +333,7 @@ extern Writer* gStdout;
331333

332334
inline Writer* Stdout() {
333335
if (gStdout == nullptr) {
334-
gStdout = reinterpret_cast<Writer*>(Alloc<CFile>(stdout));
336+
gStdout = reinterpret_cast<Writer*>(Alloc<CFile>(STDOUT_FILENO));
335337
gHeap.RootGlobalVar(gStdout);
336338
}
337339
return gStdout;
@@ -341,7 +343,7 @@ extern Writer* gStderr;
341343

342344
inline Writer* Stderr() {
343345
if (gStderr == nullptr) {
344-
gStderr = reinterpret_cast<Writer*>(Alloc<CFile>(stderr));
346+
gStderr = reinterpret_cast<Writer*>(Alloc<CFile>(STDERR_FILENO));
345347
gHeap.RootGlobalVar(gStderr);
346348
}
347349
return gStderr;

mycpp/gc_mylib_test.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "mycpp/gc_mylib.h"
22

33
#include <unistd.h>
4+
#include <fcntl.h>
45

56
#include "mycpp/gc_alloc.h" // gHeap
67
#include "mycpp/gc_str.h"
@@ -219,14 +220,14 @@ TEST files_test() {
219220
mylib::LineReader* stdin_ = mylib::Stdin();
220221
log("stdin isatty() = %d", stdin_->isatty());
221222

222-
FILE* f = fopen("README.md", "r");
223+
int fd = open("README.md", O_RDONLY);
223224

224225
mylib::CFile* r = nullptr;
225226
BigStr* filename = nullptr;
226227
BigStr* filename2 = nullptr;
227228
StackRoots _roots({&r, &filename, &filename2});
228229

229-
r = Alloc<mylib::CFile>(f);
230+
r = Alloc<mylib::CFile>(fd);
230231
filename = StrFromC("README.md");
231232
filename2 = StrFromC("README.md ");
232233
// auto r = mylib::Stdin();
@@ -254,7 +255,7 @@ TEST files_test() {
254255
auto f3 = mylib::open(filename2->strip());
255256
ASSERT(f3 != nullptr);
256257

257-
auto w = Alloc<mylib::CFile>(stdout);
258+
auto w = Alloc<mylib::CFile>(STDOUT_FILENO);
258259
w->write(StrFromC("stdout"));
259260
w->flush();
260261

0 commit comments

Comments
 (0)