OILS / mycpp / gc_mylib.cc View on Github | oilshell.org

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