/home/uke/oil/mycpp/mylib_leaky.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // mylib_leaky.cc |
2 | | |
3 | | #include "mylib_leaky.h" |
4 | | |
5 | | #include <errno.h> |
6 | | #include <unistd.h> // isatty |
7 | | |
8 | | #include <cassert> |
9 | | #include <cstdio> |
10 | | #include <exception> // std::exception |
11 | | |
12 | | Str* kEmptyString = new Str("", 0); |
13 | | |
14 | | // Translation of Python's print(). |
15 | 9 | void print(Str* s) { |
16 | 9 | mylib::Str0 s0(s); |
17 | 9 | fputs(s0.Get(), stdout); |
18 | 9 | fputs("\n", stdout); |
19 | 9 | } |
20 | | |
21 | | // Like print(..., file=sys.stderr), but Python code explicitly calls it. |
22 | 0 | void println_stderr(Str* s) { |
23 | 0 | mylib::Str0 s0(s); |
24 | 0 | fputs(s0.Get(), stderr); |
25 | 0 | fputs("\n", stderr); |
26 | 0 | } |
27 | | |
28 | | // NOTE: |
29 | | // - Oil interpreter code only uses the very common case of len(old) == 1. |
30 | | // - But we probably want to expose this more general function to OIL USERS. |
31 | | // - We could have a special case for this like CPython. It detects if |
32 | | // len(old) == len(new) as well. |
33 | | // - Python replace() has a third 'count' argument we're not using. |
34 | 36 | Str* Str::replace(Str* old, Str* new_str) { |
35 | | // log("replacing %s with %s", old_data, new_str->data_); |
36 | | |
37 | 36 | const char* old_data = old->data_; |
38 | 36 | int old_len = old->len_; |
39 | 36 | const char* last_possible = data_ + len_ - old_len; |
40 | | |
41 | 36 | const char* p_this = data_; // advances through 'this' |
42 | | |
43 | | // First pass: Calculate number of replacements, and hence new length |
44 | 36 | int replace_count = 0; |
45 | 206 | while (p_this <= last_possible) { |
46 | 170 | if (memcmp(p_this, old_data, old_len) == 0) { // equal |
47 | 60 | replace_count++; |
48 | 60 | p_this += old_len; |
49 | 110 | } else { |
50 | 110 | p_this++; |
51 | 110 | } |
52 | 170 | } |
53 | | |
54 | | // log("replacements %d", replace_count); |
55 | | |
56 | 36 | if (replace_count == 0) { |
57 | 2 | return this; // Reuse the string if there were no replacements |
58 | 2 | } |
59 | | |
60 | 34 | int result_len = |
61 | 34 | this->len_ - (replace_count * old_len) + (replace_count * new_str->len_); |
62 | | |
63 | 34 | char* result = static_cast<char*>(malloc(result_len + 1)); // +1 for NUL |
64 | | |
65 | 34 | const char* new_data = new_str->data_; |
66 | 34 | const size_t new_len = new_str->len_; |
67 | | |
68 | | // Second pass: Copy pieces into 'result' |
69 | 34 | p_this = data_; // back to beginning |
70 | 34 | char* p_result = result; // advances through 'result' |
71 | | |
72 | 196 | while (p_this <= last_possible) { |
73 | | // Note: would be more efficient if we remembered the match positions |
74 | 162 | if (memcmp(p_this, old_data, old_len) == 0) { // equal |
75 | 60 | memcpy(p_result, new_data, new_len); // Copy from new_str |
76 | 60 | p_result += new_len; |
77 | 60 | p_this += old_len; |
78 | 102 | } else { // copy 1 byte |
79 | 102 | *p_result = *p_this; |
80 | 102 | p_result++; |
81 | 102 | p_this++; |
82 | 102 | } |
83 | 162 | } |
84 | 34 | memcpy(p_result, p_this, data_ + len_ - p_this); // last part of string |
85 | 34 | result[result_len] = '\0'; // NUL terminate |
86 | | |
87 | 34 | return new Str(result, result_len); |
88 | 36 | } Line | Count | Source | 34 | 18 | Str* Str::replace(Str* old, Str* new_str) { | 35 | | // log("replacing %s with %s", old_data, new_str->data_); | 36 | | | 37 | 18 | const char* old_data = old->data_; | 38 | 18 | int old_len = old->len_; | 39 | 18 | const char* last_possible = data_ + len_ - old_len; | 40 | | | 41 | 18 | const char* p_this = data_; // advances through 'this' | 42 | | | 43 | | // First pass: Calculate number of replacements, and hence new length | 44 | 18 | int replace_count = 0; | 45 | 103 | while (p_this <= last_possible) { | 46 | 85 | if (memcmp(p_this, old_data, old_len) == 0) { // equal | 47 | 30 | replace_count++; | 48 | 30 | p_this += old_len; | 49 | 55 | } else { | 50 | 55 | p_this++; | 51 | 55 | } | 52 | 85 | } | 53 | | | 54 | | // log("replacements %d", replace_count); | 55 | | | 56 | 18 | if (replace_count == 0) { | 57 | 1 | return this; // Reuse the string if there were no replacements | 58 | 1 | } | 59 | | | 60 | 17 | int result_len = | 61 | 17 | this->len_ - (replace_count * old_len) + (replace_count * new_str->len_); | 62 | | | 63 | 17 | char* result = static_cast<char*>(malloc(result_len + 1)); // +1 for NUL | 64 | | | 65 | 17 | const char* new_data = new_str->data_; | 66 | 17 | const size_t new_len = new_str->len_; | 67 | | | 68 | | // Second pass: Copy pieces into 'result' | 69 | 17 | p_this = data_; // back to beginning | 70 | 17 | char* p_result = result; // advances through 'result' | 71 | | | 72 | 98 | while (p_this <= last_possible) { | 73 | | // Note: would be more efficient if we remembered the match positions | 74 | 81 | if (memcmp(p_this, old_data, old_len) == 0) { // equal | 75 | 30 | memcpy(p_result, new_data, new_len); // Copy from new_str | 76 | 30 | p_result += new_len; | 77 | 30 | p_this += old_len; | 78 | 51 | } else { // copy 1 byte | 79 | 51 | *p_result = *p_this; | 80 | 51 | p_result++; | 81 | 51 | p_this++; | 82 | 51 | } | 83 | 81 | } | 84 | 17 | memcpy(p_result, p_this, data_ + len_ - p_this); // last part of string | 85 | 17 | result[result_len] = '\0'; // NUL terminate | 86 | | | 87 | 17 | return new Str(result, result_len); | 88 | 18 | } |
Line | Count | Source | 34 | 18 | Str* Str::replace(Str* old, Str* new_str) { | 35 | | // log("replacing %s with %s", old_data, new_str->data_); | 36 | | | 37 | 18 | const char* old_data = old->data_; | 38 | 18 | int old_len = old->len_; | 39 | 18 | const char* last_possible = data_ + len_ - old_len; | 40 | | | 41 | 18 | const char* p_this = data_; // advances through 'this' | 42 | | | 43 | | // First pass: Calculate number of replacements, and hence new length | 44 | 18 | int replace_count = 0; | 45 | 103 | while (p_this <= last_possible) { | 46 | 85 | if (memcmp(p_this, old_data, old_len) == 0) { // equal | 47 | 30 | replace_count++; | 48 | 30 | p_this += old_len; | 49 | 55 | } else { | 50 | 55 | p_this++; | 51 | 55 | } | 52 | 85 | } | 53 | | | 54 | | // log("replacements %d", replace_count); | 55 | | | 56 | 18 | if (replace_count == 0) { | 57 | 1 | return this; // Reuse the string if there were no replacements | 58 | 1 | } | 59 | | | 60 | 17 | int result_len = | 61 | 17 | this->len_ - (replace_count * old_len) + (replace_count * new_str->len_); | 62 | | | 63 | 17 | char* result = static_cast<char*>(malloc(result_len + 1)); // +1 for NUL | 64 | | | 65 | 17 | const char* new_data = new_str->data_; | 66 | 17 | const size_t new_len = new_str->len_; | 67 | | | 68 | | // Second pass: Copy pieces into 'result' | 69 | 17 | p_this = data_; // back to beginning | 70 | 17 | char* p_result = result; // advances through 'result' | 71 | | | 72 | 98 | while (p_this <= last_possible) { | 73 | | // Note: would be more efficient if we remembered the match positions | 74 | 81 | if (memcmp(p_this, old_data, old_len) == 0) { // equal | 75 | 30 | memcpy(p_result, new_data, new_len); // Copy from new_str | 76 | 30 | p_result += new_len; | 77 | 30 | p_this += old_len; | 78 | 51 | } else { // copy 1 byte | 79 | 51 | *p_result = *p_this; | 80 | 51 | p_result++; | 81 | 51 | p_this++; | 82 | 51 | } | 83 | 81 | } | 84 | 17 | memcpy(p_result, p_this, data_ + len_ - p_this); // last part of string | 85 | 17 | result[result_len] = '\0'; // NUL terminate | 86 | | | 87 | 17 | return new Str(result, result_len); | 88 | 18 | } |
|
89 | | |
90 | 0 | Str* Str::ljust(int width, Str* fillchar) { |
91 | 0 | assert(len(fillchar) == 1); |
92 | | |
93 | 0 | int num_fill = width - len_; |
94 | 0 | if (num_fill < 0) { |
95 | 0 | return this; |
96 | 0 | } else { |
97 | 0 | char* buf = static_cast<char*>(malloc(width + 1)); |
98 | 0 | char c = fillchar->data_[0]; |
99 | 0 | memcpy(buf, data_, len_); |
100 | 0 | for (int i = len_; i < width; ++i) { |
101 | 0 | buf[i] = c; |
102 | 0 | } |
103 | 0 | buf[width] = '\0'; |
104 | 0 | return new Str(buf, width); |
105 | 0 | } |
106 | 0 | } Unexecuted instantiation: _ZN3Str5ljustEiPS_ Unexecuted instantiation: _ZN3Str5ljustEiPS_ |
107 | | |
108 | 0 | Str* Str::rjust(int width, Str* fillchar) { |
109 | 0 | assert(len(fillchar) == 1); |
110 | | |
111 | 0 | int num_fill = width - len_; |
112 | 0 | if (num_fill < 0) { |
113 | 0 | return this; |
114 | 0 | } else { |
115 | 0 | char* buf = static_cast<char*>(malloc(width + 1)); |
116 | 0 | char c = fillchar->data_[0]; |
117 | 0 | for (int i = 0; i < num_fill; ++i) { |
118 | 0 | buf[i] = c; |
119 | 0 | } |
120 | 0 | memcpy(buf + num_fill, data_, len_); |
121 | 0 | buf[width] = '\0'; |
122 | 0 | return new Str(buf, width); |
123 | 0 | } |
124 | 0 | } Unexecuted instantiation: _ZN3Str5rjustEiPS_ Unexecuted instantiation: _ZN3Str5rjustEiPS_ |
125 | | |
126 | 0 | List<Str*>* Str::split(Str* sep) { |
127 | 0 | assert(sep->len_ == 1); // we can only split one char |
128 | 0 | char sep_char = sep->data_[0]; |
129 | |
|
130 | 0 | if (len_ == 0) { |
131 | | // weird case consistent with Python: ''.split(':') == [''] |
132 | 0 | return new List<Str*>({kEmptyString}); |
133 | 0 | } |
134 | | |
135 | | // log("--- split()"); |
136 | | // log("data [%s]", data_); |
137 | | |
138 | 0 | auto result = new List<Str*>({}); |
139 | |
|
140 | 0 | int n = len_; |
141 | 0 | const char* pos = data_; |
142 | 0 | const char* end = data_ + len_; |
143 | | |
144 | | // log("pos %p", pos); |
145 | 0 | while (true) { |
146 | | // log("n %d, pos %p", n, pos); |
147 | |
|
148 | 0 | const char* new_pos = static_cast<const char*>(memchr(pos, sep_char, n)); |
149 | 0 | if (new_pos == nullptr) { |
150 | 0 | result->append(new Str(pos, end - pos)); // rest of the string |
151 | 0 | break; |
152 | 0 | } |
153 | 0 | int new_len = new_pos - pos; |
154 | |
|
155 | 0 | result->append(new Str(pos, new_len)); |
156 | 0 | n -= new_len + 1; |
157 | 0 | pos = new_pos + 1; |
158 | 0 | if (pos >= end) { // separator was at end of string |
159 | 0 | result->append(kEmptyString); |
160 | 0 | break; |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | return result; |
165 | 0 | } |
166 | | |
167 | 0 | List<Str*>* Str::splitlines(bool keep) { |
168 | 0 | assert(keep == true); |
169 | 0 | return nullptr; |
170 | 0 | } |
171 | | |
172 | 0 | Str* Str::join(List<Str*>* items) { |
173 | 0 | int len = 0; |
174 | 0 | const std::vector<Str*>& v = items->v_; |
175 | 0 | int num_parts = v.size(); |
176 | 0 | if (num_parts == 0) { // " ".join([]) == "" |
177 | 0 | return kEmptyString; |
178 | 0 | } |
179 | 0 | for (int i = 0; i < num_parts; ++i) { |
180 | 0 | len += v[i]->len_; |
181 | 0 | } |
182 | | // add length of all the separators |
183 | 0 | len += len_ * (num_parts - 1); |
184 | | |
185 | | // log("len: %d", len); |
186 | | // log("v.size(): %d", v.size()); |
187 | |
|
188 | 0 | char* result = static_cast<char*>(malloc(len + 1)); |
189 | 0 | char* p_result = result; // advances through |
190 | |
|
191 | 0 | for (int i = 0; i < num_parts; ++i) { |
192 | | // log("i %d", i); |
193 | 0 | if (i != 0 && len_) { // optimize common case of ''.join() |
194 | 0 | memcpy(p_result, data_, len_); // copy the separator |
195 | 0 | p_result += len_; |
196 | | // log("len_ %d", len_); |
197 | 0 | } |
198 | |
|
199 | 0 | int n = v[i]->len_; |
200 | | // log("n: %d", n); |
201 | 0 | memcpy(p_result, v[i]->data_, n); // copy the list item |
202 | 0 | p_result += n; |
203 | 0 | } |
204 | |
|
205 | 0 | result[len] = '\0'; // NUL terminator |
206 | |
|
207 | 0 | return new Str(result, len); |
208 | 0 | } Unexecuted instantiation: _ZN3Str4joinEP4ListIPS_E Unexecuted instantiation: _ZN3Str4joinEP4ListIPS_E |
209 | | |
210 | 0 | Str* Str::upper() { |
211 | 0 | Str* result = mylib::AllocStr(len_); |
212 | 0 | char* buffer = result->data(); |
213 | 0 | for (int char_index = 0; char_index < len_; ++char_index) { |
214 | 0 | buffer[char_index] = toupper(data_[char_index]); |
215 | 0 | } |
216 | 0 | return result; |
217 | 0 | } |
218 | | |
219 | 0 | Str* Str::lower() { |
220 | 0 | Str* result = mylib::AllocStr(len_); |
221 | 0 | char* buffer = result->data(); |
222 | 0 | for (int char_index = 0; char_index < len_; ++char_index) { |
223 | 0 | buffer[char_index] = tolower(data_[char_index]); |
224 | 0 | } |
225 | 0 | return result; |
226 | 0 | } |
227 | | |
228 | | // Get a string with one character |
229 | 294 | Str* StrIter::Value() { |
230 | 294 | char* buf = static_cast<char*>(malloc(2)); |
231 | 294 | buf[0] = s_->data_[i_]; |
232 | 294 | buf[1] = '\0'; |
233 | 294 | return new Str(buf, 1); |
234 | 294 | } Line | Count | Source | 229 | 147 | Str* StrIter::Value() { | 230 | 147 | char* buf = static_cast<char*>(malloc(2)); | 231 | 147 | buf[0] = s_->data_[i_]; | 232 | 147 | buf[1] = '\0'; | 233 | 147 | return new Str(buf, 1); | 234 | 147 | } |
Line | Count | Source | 229 | 147 | Str* StrIter::Value() { | 230 | 147 | char* buf = static_cast<char*>(malloc(2)); | 231 | 147 | buf[0] = s_->data_[i_]; | 232 | 147 | buf[1] = '\0'; | 233 | 147 | return new Str(buf, 1); | 234 | 147 | } |
|
235 | | |
236 | | namespace mylib { |
237 | | |
238 | 4 | Tuple2<Str*, Str*> split_once(Str* s, Str* delim) { |
239 | 4 | assert(delim->len_ == 1); |
240 | | |
241 | 0 | const char* start = s->data_; |
242 | 4 | char c = delim->data_[0]; |
243 | 4 | int len = s->len_; |
244 | | |
245 | 4 | const char* p = static_cast<const char*>(memchr(start, c, len)); |
246 | | |
247 | 4 | if (p) { |
248 | | // NOTE: Using SHARED SLICES, not memcpy() like some other functions. |
249 | 2 | int len1 = p - start; |
250 | 2 | Str* first = new Str(start, len1); |
251 | 2 | Str* second = new Str(p + 1, len - len1 - 1); |
252 | 2 | return Tuple2<Str*, Str*>(first, second); |
253 | 2 | } else { |
254 | 2 | return Tuple2<Str*, Str*>(s, nullptr); |
255 | 2 | } |
256 | 4 | } |
257 | | |
258 | | // |
259 | | // LineReader |
260 | | // |
261 | | |
262 | | LineReader* gStdin; |
263 | | |
264 | 267 | Str* CFileLineReader::readline() { |
265 | 267 | char* line = nullptr; |
266 | 267 | size_t allocated_size = 0; // unused |
267 | | |
268 | 267 | errno = 0; // must be reset because we check it below! |
269 | 267 | ssize_t len = getline(&line, &allocated_size, f_); |
270 | 267 | if (len < 0) { |
271 | | // log("getline() result: %d", len); |
272 | 1 | if (errno != 0) { |
273 | | // Unexpected error |
274 | 0 | log("getline() error: %s", strerror(errno)); |
275 | 0 | throw new AssertionError(errno); |
276 | 0 | } |
277 | | // Expected EOF |
278 | 1 | return kEmptyString; |
279 | 1 | } |
280 | | // log("len = %d", len); |
281 | | |
282 | | // Note: it's NUL terminated |
283 | 266 | return new Str(line, len); |
284 | 267 | } |
285 | | |
286 | | // problem: most Str methods like index() and slice() COPY so they have a |
287 | | // NUL terminator. |
288 | | // log("%s") falls back on sprintf, so it expects a NUL terminator. |
289 | | // It would be easier for us to just share. |
290 | 0 | Str* BufLineReader::readline() { |
291 | 0 | const char* end = s_->data_ + s_->len_; |
292 | 0 | if (pos_ == end) { |
293 | 0 | return kEmptyString; |
294 | 0 | } |
295 | | |
296 | 0 | const char* orig_pos = pos_; |
297 | 0 | const char* new_pos = strchr(pos_, '\n'); |
298 | | // log("pos_ = %s", pos_); |
299 | 0 | int len; |
300 | 0 | if (new_pos) { |
301 | 0 | len = new_pos - pos_ + 1; // past newline char |
302 | 0 | pos_ = new_pos + 1; |
303 | 0 | } else { // leftover line |
304 | 0 | len = end - pos_; |
305 | 0 | pos_ = end; |
306 | 0 | } |
307 | |
|
308 | 0 | char* result = static_cast<char*>(malloc(len + 1)); |
309 | 0 | memcpy(result, orig_pos, len); // copy the list item |
310 | 0 | result[len] = '\0'; |
311 | 0 | Str* line = new Str(result, len); |
312 | | |
313 | | // Easier way: |
314 | | // Str* line = new Str(pos_, new_pos - pos_); |
315 | 0 | return line; |
316 | 0 | } Unexecuted instantiation: _ZN5mylib13BufLineReader8readlineEv Unexecuted instantiation: _ZN5mylib13BufLineReader8readlineEv |
317 | | |
318 | | // |
319 | | // Writer |
320 | | // |
321 | | |
322 | | Writer* gStdout; |
323 | | Writer* gStderr; |
324 | | |
325 | 0 | void BufWriter::write(Str* s) { |
326 | 0 | int orig_len = len_; |
327 | 0 | len_ += s->len_; |
328 | | |
329 | | // BUG: This is quadratic! |
330 | | |
331 | | // data_ is nullptr at first |
332 | 0 | data_ = static_cast<char*>(realloc(data_, len_ + 1)); |
333 | | |
334 | | // Append to the end |
335 | 0 | memcpy(data_ + orig_len, s->data_, s->len_); |
336 | 0 | data_[len_] = '\0'; |
337 | 0 | } |
338 | | |
339 | 430 | void BufWriter::write_const(const char* s, int len) { |
340 | 430 | int orig_len = len_; |
341 | 430 | len_ += len; |
342 | | // data_ is nullptr at first |
343 | 430 | data_ = static_cast<char*>(realloc(data_, len_ + 1)); |
344 | | |
345 | | // Append to the end |
346 | 430 | memcpy(data_ + orig_len, s, len); |
347 | 430 | data_[len_] = '\0'; |
348 | 430 | } |
349 | | |
350 | 0 | void BufWriter::format_s(Str* s) { |
351 | 0 | this->write(s); |
352 | 0 | } |
353 | | |
354 | 89 | void BufWriter::format_d(int i) { |
355 | | // extend to the maximum size |
356 | 89 | data_ = static_cast<char*>(realloc(data_, len_ + kIntBufSize)); |
357 | 89 | int len = snprintf(data_ + len_, kIntBufSize, "%d", i); |
358 | | // but record only the number of bytes written |
359 | 89 | len_ += len; |
360 | 89 | } |
361 | | |
362 | 0 | void BufWriter::format_o(int i) { |
363 | 0 | NotImplemented(); |
364 | 0 | } |
365 | | |
366 | | // repr() calls this too |
367 | | // |
368 | | // TODO: This could be replaced with QSN? The upper bound is greater there |
369 | | // because of \u{}. |
370 | 0 | void BufWriter::format_r(Str* s) { |
371 | | // Worst case: \0 becomes 4 bytes as '\\x00', and then two quote bytes. |
372 | 0 | int upper_bound = s->len_ * 4 + 2; |
373 | | |
374 | | // Extend the buffer |
375 | 0 | data_ = static_cast<char*>(realloc(data_, len_ + upper_bound + 1)); |
376 | |
|
377 | 0 | char quote = '\''; |
378 | 0 | if (memchr(s->data_, '\'', s->len_) && !memchr(s->data_, '"', s->len_)) { |
379 | 0 | quote = '"'; |
380 | 0 | } |
381 | 0 | char* p = data_ + len_; // end of valid data |
382 | | |
383 | | // From PyString_Repr() |
384 | 0 | *p++ = quote; |
385 | 0 | for (int i = 0; i < s->len_; ++i) { |
386 | 0 | char c = s->data_[i]; |
387 | 0 | if (c == quote || c == '\\') { |
388 | 0 | *p++ = '\\'; |
389 | 0 | *p++ = c; |
390 | 0 | } else if (c == '\t') { |
391 | 0 | *p++ = '\\'; |
392 | 0 | *p++ = 't'; |
393 | 0 | } else if (c == '\n') { |
394 | 0 | *p++ = '\\'; |
395 | 0 | *p++ = 'n'; |
396 | 0 | } else if (c == '\r') { |
397 | 0 | *p++ = '\\'; |
398 | 0 | *p++ = 'r'; |
399 | 0 | } else if (c < ' ' || c >= 0x7f) { |
400 | 0 | sprintf(p, "\\x%02x", c & 0xff); |
401 | 0 | p += 4; |
402 | 0 | } else { |
403 | 0 | *p++ = c; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | *p++ = quote; |
407 | 0 | *p = '\0'; |
408 | |
|
409 | 0 | len_ = p - data_; |
410 | | // Shrink the buffer. This is valid usage and GNU libc says it can actually |
411 | | // release. |
412 | 0 | data_ = static_cast<char*>(realloc(data_, len_ + 1)); |
413 | 0 | } |
414 | | |
415 | | // void BufWriter::format_s(const char* s) { |
416 | | // this->write_const(s, strlen(s)); |
417 | | //} |
418 | | |
419 | 0 | void CFileWriter::write(Str* s) { |
420 | | // note: throwing away the return value |
421 | 0 | fwrite(s->data_, s->len_, 1, f_); |
422 | | |
423 | | // Necessary for 'echo hi > x' to work. Otherwise it gets buffered so the |
424 | | // write() happens AFTER ~ctx_Redirect(). |
425 | | // |
426 | | // TODO: use write() directly to avoid buffering problems? But we also want |
427 | | // fast command sub like ${.echo x} |
428 | 0 | fflush(f_); |
429 | 0 | } |
430 | | |
431 | 0 | void CFileWriter::flush() { |
432 | 0 | fflush(f_); |
433 | 0 | } |
434 | | |
435 | 1 | bool CFileWriter::isatty() { |
436 | 1 | return ::isatty(fileno(f_)); |
437 | 1 | } |
438 | | |
439 | | } // namespace mylib |
440 | | |
441 | | // |
442 | | // Free functions |
443 | | // |
444 | | |
445 | 0 | Str* repr(Str* s) { |
446 | 0 | mylib::BufWriter f; |
447 | 0 | f.format_r(s); |
448 | 0 | return f.getvalue(); |
449 | 0 | } |
450 | | |
451 | 0 | Str* str_concat(Str* a, Str* b) { |
452 | 0 | int new_len = a->len_ + b->len_; |
453 | 0 | char* buf = static_cast<char*>(malloc(new_len + 1)); |
454 | |
|
455 | 0 | int len_a = a->len_; |
456 | 0 | memcpy(buf, a->data_, len_a); |
457 | 0 | memcpy(buf + len_a, b->data_, b->len_); |
458 | 0 | buf[new_len] = '\0'; |
459 | |
|
460 | 0 | return new Str(buf, new_len); |
461 | 0 | } Unexecuted instantiation: _Z10str_concatP3StrS0_ Unexecuted instantiation: _Z10str_concatP3StrS0_ |
462 | | |
463 | | // for os_path.join() |
464 | 0 | Str* str_concat3(Str* a, Str* b, Str* c) { |
465 | 0 | int new_len = a->len_ + b->len_ + c->len_; |
466 | 0 | char* buf = static_cast<char*>(malloc(new_len + 1)); |
467 | 0 | char* pos = buf; |
468 | |
|
469 | 0 | memcpy(pos, a->data_, a->len_); |
470 | 0 | pos += a->len_; |
471 | |
|
472 | 0 | memcpy(pos, b->data_, b->len_); |
473 | 0 | pos += b->len_; |
474 | |
|
475 | 0 | memcpy(pos, c->data_, c->len_); |
476 | |
|
477 | 0 | buf[new_len] = '\0'; |
478 | |
|
479 | 0 | return new Str(buf, new_len); |
480 | 0 | } Unexecuted instantiation: _Z11str_concat3P3StrS0_S0_ Unexecuted instantiation: _Z11str_concat3P3StrS0_S0_ |
481 | | |
482 | 0 | Str* str_repeat(Str* s, int times) { |
483 | | // Python allows -1 too, and Oil used that |
484 | 0 | if (times <= 0) { |
485 | 0 | return kEmptyString; |
486 | 0 | } |
487 | 0 | int len = s->len_; |
488 | 0 | int new_len = len * times; |
489 | 0 | char* data = static_cast<char*>(malloc(new_len + 1)); |
490 | |
|
491 | 0 | char* dest = data; |
492 | 0 | for (int i = 0; i < times; i++) { |
493 | 0 | memcpy(dest, s->data_, len); |
494 | 0 | dest += len; |
495 | 0 | } |
496 | 0 | data[new_len] = '\0'; |
497 | 0 | return new Str(data, new_len); |
498 | 0 | } Unexecuted instantiation: _Z10str_repeatP3Stri Unexecuted instantiation: _Z10str_repeatP3Stri |
499 | | |
500 | | // Helper for str_to_int() that doesn't use exceptions. |
501 | | // Like atoi(), but with better error checking. |
502 | 18 | bool _str_to_int(Str* s, int* result, int base) { |
503 | 18 | if (s->len_ == 0) { |
504 | 1 | return false; // special case for empty string |
505 | 1 | } |
506 | | |
507 | 17 | char* p; // mutated by strtol |
508 | | |
509 | 17 | mylib::Str0 s0(s); |
510 | 17 | long v = strtol(s0.Get(), &p, base); // base 10 |
511 | 17 | switch (v) { |
512 | 1 | case LONG_MIN: |
513 | | // log("underflow"); |
514 | 1 | return false; |
515 | 1 | case LONG_MAX: |
516 | | // log("overflow"); |
517 | 1 | return false; |
518 | 17 | } |
519 | | |
520 | 15 | *result = v; |
521 | | |
522 | | // Return true if it consumed ALL characters. |
523 | 15 | const char* end = s->data_ + s->len_; |
524 | | |
525 | | // log("start %p p %p end %p", s->data_, p, end); |
526 | 15 | if (p == end) { |
527 | 9 | return true; |
528 | 9 | } |
529 | | |
530 | | // Trailing space is OK! |
531 | 8 | while (p < end) { |
532 | 6 | if (!isspace(*p)) { |
533 | 4 | return false; |
534 | 4 | } |
535 | 2 | p++; |
536 | 2 | } |
537 | 2 | return true; |
538 | 6 | } |
539 | | |
540 | | // Python-like wrapper |
541 | 3 | int to_int(Str* s) { |
542 | 3 | int i; |
543 | 3 | if (_str_to_int(s, &i, 10)) { |
544 | 1 | return i; |
545 | 2 | } else { |
546 | 2 | throw new ValueError(); |
547 | 2 | } |
548 | 3 | } |
549 | | |
550 | 4 | int to_int(Str* s, int base) { |
551 | 4 | int i; |
552 | 4 | if (_str_to_int(s, &i, base)) { |
553 | 4 | return i; |
554 | 4 | } else { |
555 | 0 | throw new ValueError(); |
556 | 0 | } |
557 | 4 | } |
558 | | |
559 | | // |
560 | | // Formatter |
561 | | // |
562 | | |
563 | | mylib::BufWriter gBuf; |