/home/uke/oil/mycpp/gc_mylib.cc
Line | Count | Source (jump to first uncovered line) |
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 | | |
9 | | namespace mylib { |
10 | | |
11 | 1 | void 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 | 1 | gHeap.Init(50000); |
18 | 1 | } |
19 | | |
20 | 5 | void print_stderr(BigStr* s) { |
21 | 5 | fputs(s->data_, stderr); // prints until first NUL |
22 | 5 | fputc('\n', stderr); |
23 | 5 | } |
24 | | |
25 | | #if 0 |
26 | | void 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 | | |
39 | 0 | BigStr* JoinBytes(List<int>* byte_list) { |
40 | 0 | int n = len(byte_list); |
41 | 0 | BigStr* result = NewStr(n); |
42 | 0 | for (int i = 0; i < n; ++i) { |
43 | 0 | result->data_[i] = byte_list->at(i); |
44 | 0 | } |
45 | 0 | return result; |
46 | 0 | } |
47 | | |
48 | | // For BashArray |
49 | 0 | void BigIntSort(List<mops::BigInt>* keys) { |
50 | 0 | keys->sort(); |
51 | 0 | } |
52 | | |
53 | | class MutableStr : public BigStr {}; |
54 | | |
55 | 5 | MutableStr* 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 | 5 | static_assert(sizeof(MutableStr) == sizeof(BigStr), |
60 | 5 | "BigStr and MutableStr must have same size"); |
61 | 5 | return reinterpret_cast<MutableStr*>(NewStr(n)); |
62 | 5 | } |
63 | | |
64 | 5 | Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim) { |
65 | 5 | DCHECK(len(delim) == 1); |
66 | | |
67 | 0 | const char* start = s->data_; // note: this pointer may move |
68 | 5 | char c = delim->data_[0]; |
69 | 5 | int length = len(s); |
70 | | |
71 | 5 | const char* p = static_cast<const char*>(memchr(start, c, length)); |
72 | | |
73 | 5 | if (p) { |
74 | 3 | int len1 = p - start; |
75 | 3 | int len2 = length - len1 - 1; // -1 for delim |
76 | | |
77 | 3 | BigStr* s1 = nullptr; |
78 | 3 | BigStr* s2 = nullptr; |
79 | | // Allocate together to avoid 's' moving in between |
80 | 3 | s1 = NewStr(len1); |
81 | 3 | s2 = NewStr(len2); |
82 | | |
83 | 3 | memcpy(s1->data_, s->data_, len1); |
84 | 3 | memcpy(s2->data_, s->data_ + len1 + 1, len2); |
85 | | |
86 | 3 | return Tuple2<BigStr*, BigStr*>(s1, s2); |
87 | 3 | } else { |
88 | 2 | return Tuple2<BigStr*, BigStr*>(s, nullptr); |
89 | 2 | } |
90 | 5 | } |
91 | | |
92 | | LineReader* gStdin; |
93 | | |
94 | 2 | LineReader* open(BigStr* path) { |
95 | | // TODO: Don't use C I/O; use POSIX I/O! |
96 | 2 | FILE* f = fopen(path->data_, "r"); |
97 | 2 | if (f == nullptr) { |
98 | 0 | throw Alloc<IOError>(errno); |
99 | 0 | } |
100 | | |
101 | 2 | return reinterpret_cast<LineReader*>(Alloc<CFile>(f)); |
102 | 2 | } |
103 | | |
104 | 141 | BigStr* CFile::readline() { |
105 | 141 | char* line = nullptr; |
106 | 141 | size_t allocated_size = 0; // unused |
107 | | |
108 | | // Reset errno because we turn the EOF error into empty string (like Python). |
109 | 141 | errno = 0; |
110 | 141 | ssize_t len = getline(&line, &allocated_size, f_); |
111 | | // log("getline = %d", len); |
112 | 141 | if (len < 0) { |
113 | | // Reset EOF flag so the next readline() will get a line. |
114 | 1 | clearerr(f_); |
115 | | |
116 | | // man page says the buffer should be freed even if getline fails |
117 | 1 | free(line); |
118 | | |
119 | | // Raise KeyboardInterrupt like mylib.Stdin().readline() does in Python! |
120 | | // This affects _PlainPromptInput() in frontend/reader.py. |
121 | 1 | if (errno == EINTR && iolib::gSignalSafe->PollUntrappedSigInt()) { |
122 | 0 | throw Alloc<KeyboardInterrupt>(); |
123 | 0 | } |
124 | | |
125 | 1 | if (errno != 0) { // Unexpected error |
126 | | // log("getline() error: %s", strerror(errno)); |
127 | 0 | throw Alloc<IOError>(errno); |
128 | 0 | } |
129 | 1 | return kEmptyString; // Indicate EOF with empty string, like Python |
130 | 1 | } |
131 | | |
132 | | // Note: getline() NUL-terminates the buffer |
133 | 140 | BigStr* result = ::StrFromC(line, len); |
134 | 140 | free(line); |
135 | 140 | return result; |
136 | 141 | } |
137 | | |
138 | 2 | bool CFile::isatty() { |
139 | 2 | return ::isatty(fileno(f_)); |
140 | 2 | } |
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. |
146 | 8 | BigStr* BufLineReader::readline() { |
147 | 8 | BigStr* line = nullptr; |
148 | | |
149 | 8 | int str_len = len(s_); |
150 | 8 | if (pos_ == str_len) { |
151 | 4 | return kEmptyString; |
152 | 4 | } |
153 | | |
154 | 4 | int orig_pos = pos_; |
155 | 4 | const char* p = strchr(s_->data_ + pos_, '\n'); |
156 | | // log("pos_ = %s", pos_); |
157 | 4 | int line_len; |
158 | 4 | if (p) { |
159 | 2 | int new_pos = p - s_->data_; |
160 | 2 | line_len = new_pos - pos_ + 1; // past newline char |
161 | 2 | pos_ = new_pos + 1; |
162 | 2 | } else { // leftover line |
163 | 2 | if (pos_ == 0) { // The string has no newlines at all -- just return it |
164 | 1 | pos_ = str_len; // advance to the end |
165 | 1 | return s_; |
166 | 1 | } else { |
167 | 1 | line_len = str_len - pos_; |
168 | 1 | pos_ = str_len; // advance to the end |
169 | 1 | } |
170 | 2 | } |
171 | | |
172 | 3 | line = NewStr(line_len); |
173 | 3 | memcpy(line->data_, s_->data_ + orig_pos, line_len); |
174 | 3 | DCHECK(line->data_[line_len] == '\0'); |
175 | 0 | return line; |
176 | 4 | } |
177 | | |
178 | | Writer* gStdout; |
179 | | Writer* gStderr; |
180 | | |
181 | | // |
182 | | // CFileWriter |
183 | | // |
184 | | |
185 | 2 | void CFile::write(BigStr* s) { |
186 | | // Writes can be short! |
187 | 2 | int n = len(s); |
188 | 2 | int num_written = ::fwrite(s->data_, sizeof(char), n, f_); |
189 | | // Similar to CPython fileobject.c |
190 | 2 | if (num_written != n) { |
191 | 0 | throw Alloc<IOError>(errno); |
192 | 0 | } |
193 | 2 | } |
194 | | |
195 | 1 | void CFile::flush() { |
196 | 1 | if (::fflush(f_) != 0) { |
197 | 0 | throw Alloc<IOError>(errno); |
198 | 0 | } |
199 | 1 | } |
200 | | |
201 | 1 | void CFile::close() { |
202 | 1 | if (::fclose(f_) != 0) { |
203 | 0 | throw Alloc<IOError>(errno); |
204 | 0 | } |
205 | 1 | } |
206 | | |
207 | | // |
208 | | // BufWriter |
209 | | // |
210 | | |
211 | 5 | void BufWriter::EnsureMoreSpace(int n) { |
212 | 5 | 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 | 3 | str_ = NewMutableStr(n); |
216 | 3 | return; |
217 | 3 | } |
218 | | |
219 | 2 | int current_cap = len(str_); |
220 | 2 | DCHECK(current_cap >= len_); |
221 | | |
222 | 0 | int new_cap = len_ + n; |
223 | | |
224 | 2 | if (current_cap < new_cap) { |
225 | 2 | auto* s = NewMutableStr(std::max(current_cap * 2, new_cap)); |
226 | 2 | memcpy(s->data_, str_->data_, len_); |
227 | 2 | s->data_[len_] = '\0'; |
228 | 2 | str_ = s; |
229 | 2 | } |
230 | 2 | } |
231 | | |
232 | 0 | uint8_t* BufWriter::LengthPointer() { |
233 | | // start + len |
234 | 0 | return reinterpret_cast<uint8_t*>(str_->data_) + len_; |
235 | 0 | } |
236 | | |
237 | 0 | uint8_t* BufWriter::CapacityPointer() { |
238 | | // start + capacity |
239 | 0 | return reinterpret_cast<uint8_t*>(str_->data_) + str_->len_; |
240 | 0 | } |
241 | | |
242 | 0 | void BufWriter::SetLengthFrom(uint8_t* length_ptr) { |
243 | 0 | uint8_t* begin = reinterpret_cast<uint8_t*>(str_->data_); |
244 | 0 | 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 | 0 | len_ = length_ptr - begin; |
248 | | // printf("SET LEN to %d\n", len_); |
249 | 0 | } |
250 | | |
251 | 0 | void BufWriter::Truncate(int length) { |
252 | 0 | len_ = length; |
253 | 0 | } |
254 | | |
255 | 4 | void BufWriter::WriteRaw(char* s, int n) { |
256 | 4 | 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 | 4 | if (n == 0) { |
260 | 0 | return; |
261 | 0 | } |
262 | | |
263 | 4 | EnsureMoreSpace(n); |
264 | | |
265 | | // Append the contents to the buffer |
266 | 4 | memcpy(str_->data_ + len_, s, n); |
267 | 4 | len_ += n; |
268 | 4 | str_->data_[len_] = '\0'; |
269 | 4 | } |
270 | | |
271 | 0 | void BufWriter::WriteConst(const char* c_string) { |
272 | | // meant for short strings like '"' |
273 | 0 | WriteRaw(const_cast<char*>(c_string), strlen(c_string)); |
274 | 0 | } |
275 | | |
276 | 4 | void BufWriter::write(BigStr* s) { |
277 | 4 | WriteRaw(s->data_, len(s)); |
278 | 4 | } |
279 | | |
280 | 2 | void BufWriter::write_spaces(int n) { |
281 | 2 | DCHECK(n >= 0); |
282 | 2 | if (n == 0) { |
283 | 1 | return; |
284 | 1 | } |
285 | | |
286 | 1 | EnsureMoreSpace(n); |
287 | | |
288 | 1 | char* dest = str_->data_ + len_; |
289 | 4 | for (int i = 0; i < n; ++i) { |
290 | 3 | dest[i] = ' '; |
291 | 3 | } |
292 | 1 | len_ += n; |
293 | 1 | str_->data_[len_] = '\0'; |
294 | 1 | } |
295 | | |
296 | 4 | BigStr* BufWriter::getvalue() { |
297 | 4 | DCHECK(is_valid_); // Check for two INVALID getvalue() in a row |
298 | 0 | is_valid_ = false; |
299 | | |
300 | 4 | if (str_ == nullptr) { // if no write() methods are called, the result is "" |
301 | 1 | return kEmptyString; |
302 | 3 | } else { |
303 | 3 | BigStr* s = str_; |
304 | 3 | s->MaybeShrink(len_); |
305 | 3 | str_ = nullptr; |
306 | 3 | len_ = -1; |
307 | 3 | return s; |
308 | 3 | } |
309 | 4 | } |
310 | | |
311 | | } // namespace mylib |