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

338 lines, 184 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 (len < 0) {
135 // Raise KeyboardInterrupt like mylib.Stdin().readline() does in Python!
136 // This affects _PlainPromptInput() in frontend/reader.py.
137 if (errno == EINTR && iolib::gSignalSafe->PollUntrappedSigInt()) {
138 throw Alloc<KeyboardInterrupt>();
139 }
140
141 if (errno != 0) { // Unexpected error
142 throw Alloc<IOError>(errno);
143 }
144 return kEmptyString; // Indicate EOF with empty string, like Python
145 }
146 }
147 if (!null_terminated)
148 throw Alloc<IOError>(90); // Line too long, didn't reach the newline char
149
150 BigStr* result = ::StrFromC(line_, i);
151
152 return result;
153}
154
155bool CFile::isatty() {
156 return ::isatty(fd_);
157}
158
159// Problem: most BigStr methods like index() and slice() COPY so they have a
160// NUL terminator.
161// log("%s") falls back on sprintf, so it expects a NUL terminator.
162// It would be easier for us to just share.
163BigStr* BufLineReader::readline() {
164 BigStr* line = nullptr;
165
166 int str_len = len(s_);
167 if (pos_ == str_len) {
168 return kEmptyString;
169 }
170
171 int orig_pos = pos_;
172 const char* p = strchr(s_->data_ + pos_, '\n');
173 // log("pos_ = %s", pos_);
174 int line_len;
175 if (p) {
176 int new_pos = p - s_->data_;
177 line_len = new_pos - pos_ + 1; // past newline char
178 pos_ = new_pos + 1;
179 } else { // leftover line
180 if (pos_ == 0) { // The string has no newlines at all -- just return it
181 pos_ = str_len; // advance to the end
182 return s_;
183 } else {
184 line_len = str_len - pos_;
185 pos_ = str_len; // advance to the end
186 }
187 }
188
189 line = NewStr(line_len);
190 memcpy(line->data_, s_->data_ + orig_pos, line_len);
191 DCHECK(line->data_[line_len] == '\0');
192 return line;
193}
194
195Writer* gStdout;
196Writer* gStderr;
197
198//
199// CFileWriter
200//
201
202void CFile::write(BigStr* s) {
203 // Writes can be short!
204 int n = len(s);
205 int num_written = ::write(fd_, s->data_, n);
206 // Similar to CPython fileobject.c
207 if (num_written != n) {
208 throw Alloc<IOError>(errno);
209 }
210}
211
212void CFile::flush() {
213 // no-op for now
214}
215
216void CFile::close() {
217 if (::close(fd_) != 0) {
218 throw Alloc<IOError>(errno);
219 }
220}
221
222//
223// BufWriter
224//
225
226void BufWriter::EnsureMoreSpace(int n) {
227 if (str_ == nullptr) {
228 // TODO: we could make the default capacity big enough for a line, e.g. 128
229 // capacity: 128 -> 256 -> 512
230 str_ = NewMutableStr(n);
231 return;
232 }
233
234 int current_cap = len(str_);
235 DCHECK(current_cap >= len_);
236
237 int new_cap = len_ + n;
238
239 if (current_cap < new_cap) {
240 auto* s = NewMutableStr(std::max(current_cap * 2, new_cap));
241 memcpy(s->data_, str_->data_, len_);
242 s->data_[len_] = '\0';
243 str_ = s;
244 }
245}
246
247uint8_t* BufWriter::LengthPointer() {
248 // start + len
249 return reinterpret_cast<uint8_t*>(str_->data_) + len_;
250}
251
252uint8_t* BufWriter::CapacityPointer() {
253 // start + capacity
254 return reinterpret_cast<uint8_t*>(str_->data_) + str_->len_;
255}
256
257void BufWriter::SetLengthFrom(uint8_t* length_ptr) {
258 uint8_t* begin = reinterpret_cast<uint8_t*>(str_->data_);
259 DCHECK(length_ptr >= begin); // we should have written some data
260
261 // Set the length, e.g. so we know where to resume writing from
262 len_ = length_ptr - begin;
263 // printf("SET LEN to %d\n", len_);
264}
265
266void BufWriter::Truncate(int length) {
267 len_ = length;
268}
269
270void BufWriter::WriteRaw(char* s, int n) {
271 DCHECK(is_valid_); // Can't write() after getvalue()
272
273 // write('') is a no-op, so don't create Buf if we don't need to
274 if (n == 0) {
275 return;
276 }
277
278 EnsureMoreSpace(n);
279
280 // Append the contents to the buffer
281 memcpy(str_->data_ + len_, s, n);
282 len_ += n;
283 str_->data_[len_] = '\0';
284}
285
286void BufWriter::WriteConst(const char* c_string) {
287 // meant for short strings like '"'
288 WriteRaw(const_cast<char*>(c_string), strlen(c_string));
289}
290
291void BufWriter::write(BigStr* s) {
292 WriteRaw(s->data_, len(s));
293}
294
295void BufWriter::write_spaces(int n) {
296 DCHECK(n >= 0);
297 if (n == 0) {
298 return;
299 }
300
301 EnsureMoreSpace(n);
302
303 char* dest = str_->data_ + len_;
304 for (int i = 0; i < n; ++i) {
305 dest[i] = ' ';
306 }
307 len_ += n;
308 str_->data_[len_] = '\0';
309}
310
311BigStr* BufWriter::getvalue() {
312 DCHECK(is_valid_); // Check for two INVALID getvalue() in a row
313 is_valid_ = false;
314
315 if (str_ == nullptr) { // if no write() methods are called, the result is ""
316 return kEmptyString;
317 } else {
318 BigStr* s = str_;
319 s->MaybeShrink(len_);
320 str_ = nullptr;
321 len_ = -1;
322 return s;
323 }
324}
325
326bool StatResult::isreg() {
327 return S_ISREG(stat_result_.st_mode);
328}
329
330StatResult* stat(BigStr* filename) {
331 auto* st = Alloc<StatResult>();
332 if (::stat(filename->data_, &st->stat_result_) < 0) {
333 return nullptr;
334 }
335 return st;
336}
337
338} // namespace mylib