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

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