OILS / mycpp / gc_mylib.cc View on Github | oils.pub

341 lines, 187 significant
1#include "mycpp/gc_mylib.h"
2
3#include <errno.h>
4#include <fcntl.h>
5#include <math.h>
6#include <stdio.h>
7#include <unistd.h> // isatty
8
9#include "mycpp/gc_iolib.h"
10
11namespace mylib {
12
13// Note: isinf() and isnan() MAY be macros, because we included <math.h> rather
14// than <cmath>. The latter defines std::isinf()
15
16bool isinf_(double f) {
17 return isinf(f);
18}
19
20bool isnan_(double f) {
21 return isnan(f);
22}
23
24void InitCppOnly() {
25 // We don't seem need this now that we have ctx_FlushStdout().
26 // setvbuf(stdout, 0, _IONBF, 0);
27
28 // Arbitrary threshold of 50K objects based on eyeballing
29 // benchmarks/osh-runtime 10K or 100K aren't too bad either.
30 gHeap.Init(50000);
31}
32
33void print_stderr(BigStr* s) {
34 fputs(s->data_, stderr); // prints until first NUL
35 fputc('\n', stderr);
36}
37
38#if 0
39void writeln(BigStr* s, int fd) {
40 // TODO: handle errors and write in a loop, like posix::write(). If possible,
41 // use posix::write directly, but that introduces some dependency problems.
42
43 if (write(fd, s->data_, len(s)) < 0) {
44 assert(0);
45 }
46 if (write(fd, "\n", 1) < 0) {
47 assert(0);
48 }
49}
50#endif
51
52BigStr* JoinBytes(List<int>* byte_list) {
53 int n = len(byte_list);
54 BigStr* result = NewStr(n);
55 for (int i = 0; i < n; ++i) {
56 result->data_[i] = byte_list->at(i);
57 }
58 return result;
59}
60
61// For BashArray
62void BigIntSort(List<mops::BigInt>* keys) {
63 keys->sort();
64}
65
66class MutableStr : public BigStr {};
67
68MutableStr* NewMutableStr(int n) {
69 // In order for everything to work, MutableStr must be identical in layout to
70 // BigStr. One easy way to achieve this is for MutableStr to have no members
71 // and to inherit from BigStr.
72 static_assert(sizeof(MutableStr) == sizeof(BigStr),
73 "BigStr and MutableStr must have same size");
74 return reinterpret_cast<MutableStr*>(NewStr(n));
75}
76
77Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim) {
78 DCHECK(len(delim) == 1);
79
80 const char* start = s->data_; // note: this pointer may move
81 char c = delim->data_[0];
82 int length = len(s);
83
84 const char* p = static_cast<const char*>(memchr(start, c, length));
85
86 if (p) {
87 int len1 = p - start;
88 int len2 = length - len1 - 1; // -1 for delim
89
90 BigStr* s1 = nullptr;
91 BigStr* s2 = nullptr;
92 // Allocate together to avoid 's' moving in between
93 s1 = NewStr(len1);
94 s2 = NewStr(len2);
95
96 memcpy(s1->data_, s->data_, len1);
97 memcpy(s2->data_, s->data_ + len1 + 1, len2);
98
99 return Tuple2<BigStr*, BigStr*>(s1, s2);
100 } else {
101 return Tuple2<BigStr*, BigStr*>(s, nullptr);
102 }
103}
104
105LineReader* gStdin;
106
107LineReader* open(BigStr* path) {
108 int fd = ::open(path->data_, O_RDONLY);
109
110 if (fd < 0) {
111 throw Alloc<IOError>(errno);
112 }
113
114 return reinterpret_cast<LineReader*>(Alloc<CFile>(fd));
115}
116
117BigStr* CFile::readline() {
118 // Reset errno because we turn the EOF error into empty string (like Python).
119 errno = 0;
120
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 }
149 }
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);
154
155 return result;
156}
157
158bool CFile::isatty() {
159 return ::isatty(fd_);
160}
161
162// Problem: most BigStr methods like index() and slice() COPY so they have a
163// NUL terminator.
164// log("%s") falls back on sprintf, so it expects a NUL terminator.
165// It would be easier for us to just share.
166BigStr* BufLineReader::readline() {
167 BigStr* line = nullptr;
168
169 int str_len = len(s_);
170 if (pos_ == str_len) {
171 return kEmptyString;
172 }
173
174 int orig_pos = pos_;
175 const char* p = strchr(s_->data_ + pos_, '\n');
176 // log("pos_ = %s", pos_);
177 int line_len;
178 if (p) {
179 int new_pos = p - s_->data_;
180 line_len = new_pos - pos_ + 1; // past newline char
181 pos_ = new_pos + 1;
182 } else { // leftover line
183 if (pos_ == 0) { // The string has no newlines at all -- just return it
184 pos_ = str_len; // advance to the end
185 return s_;
186 } else {
187 line_len = str_len - pos_;
188 pos_ = str_len; // advance to the end
189 }
190 }
191
192 line = NewStr(line_len);
193 memcpy(line->data_, s_->data_ + orig_pos, line_len);
194 DCHECK(line->data_[line_len] == '\0');
195 return line;
196}
197
198Writer* gStdout;
199Writer* gStderr;
200
201//
202// CFileWriter
203//
204
205void CFile::write(BigStr* s) {
206 // Writes can be short!
207 int n = len(s);
208 int num_written = ::write(fd_, s->data_, n);
209 // Similar to CPython fileobject.c
210 if (num_written != n) {
211 throw Alloc<IOError>(errno);
212 }
213}
214
215void CFile::flush() {
216 // no-op for now
217}
218
219void CFile::close() {
220 if (::close(fd_) != 0) {
221 throw Alloc<IOError>(errno);
222 }
223}
224
225//
226// BufWriter
227//
228
229void BufWriter::EnsureMoreSpace(int n) {
230 if (str_ == nullptr) {
231 // TODO: we could make the default capacity big enough for a line, e.g. 128
232 // capacity: 128 -> 256 -> 512
233 str_ = NewMutableStr(n);
234 return;
235 }
236
237 int current_cap = len(str_);
238 DCHECK(current_cap >= len_);
239
240 int new_cap = len_ + n;
241
242 if (current_cap < new_cap) {
243 auto* s = NewMutableStr(std::max(current_cap * 2, new_cap));
244 memcpy(s->data_, str_->data_, len_);
245 s->data_[len_] = '\0';
246 str_ = s;
247 }
248}
249
250uint8_t* BufWriter::LengthPointer() {
251 // start + len
252 return reinterpret_cast<uint8_t*>(str_->data_) + len_;
253}
254
255uint8_t* BufWriter::CapacityPointer() {
256 // start + capacity
257 return reinterpret_cast<uint8_t*>(str_->data_) + str_->len_;
258}
259
260void BufWriter::SetLengthFrom(uint8_t* length_ptr) {
261 uint8_t* begin = reinterpret_cast<uint8_t*>(str_->data_);
262 DCHECK(length_ptr >= begin); // we should have written some data
263
264 // Set the length, e.g. so we know where to resume writing from
265 len_ = length_ptr - begin;
266 // printf("SET LEN to %d\n", len_);
267}
268
269void BufWriter::Truncate(int length) {
270 len_ = length;
271}
272
273void BufWriter::WriteRaw(char* s, int n) {
274 DCHECK(is_valid_); // Can't write() after getvalue()
275
276 // write('') is a no-op, so don't create Buf if we don't need to
277 if (n == 0) {
278 return;
279 }
280
281 EnsureMoreSpace(n);
282
283 // Append the contents to the buffer
284 memcpy(str_->data_ + len_, s, n);
285 len_ += n;
286 str_->data_[len_] = '\0';
287}
288
289void BufWriter::WriteConst(const char* c_string) {
290 // meant for short strings like '"'
291 WriteRaw(const_cast<char*>(c_string), strlen(c_string));
292}
293
294void BufWriter::write(BigStr* s) {
295 WriteRaw(s->data_, len(s));
296}
297
298void BufWriter::write_spaces(int n) {
299 DCHECK(n >= 0);
300 if (n == 0) {
301 return;
302 }
303
304 EnsureMoreSpace(n);
305
306 char* dest = str_->data_ + len_;
307 for (int i = 0; i < n; ++i) {
308 dest[i] = ' ';
309 }
310 len_ += n;
311 str_->data_[len_] = '\0';
312}
313
314BigStr* BufWriter::getvalue() {
315 DCHECK(is_valid_); // Check for two INVALID getvalue() in a row
316 is_valid_ = false;
317
318 if (str_ == nullptr) { // if no write() methods are called, the result is ""
319 return kEmptyString;
320 } else {
321 BigStr* s = str_;
322 s->MaybeShrink(len_);
323 str_ = nullptr;
324 len_ = -1;
325 return s;
326 }
327}
328
329bool StatResult::isreg() {
330 return S_ISREG(stat_result_.st_mode);
331}
332
333StatResult* stat(BigStr* filename) {
334 auto* st = Alloc<StatResult>();
335 if (::stat(filename->data_, &st->stat_result_) < 0) {
336 return nullptr;
337 }
338 return st;
339}
340
341} // namespace mylib