/home/andy/git/oilshell/oil/mycpp/leaky_builtins.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <ctype.h> // isspace() |
2 | | |
3 | | #include "mycpp/runtime.h" |
4 | | |
5 | | mylib::BufWriter gBuf; |
6 | | |
7 | | // Translation of Python's print(). |
8 | 81 | void print(Str* s) { |
9 | 81 | fputs(s->data(), stdout); |
10 | 81 | fputs("\n", stdout); |
11 | 81 | } |
12 | | |
13 | | // Like print(..., file=sys.stderr), but Python code explicitly calls it. |
14 | 211 | void println_stderr(Str* s) { |
15 | 211 | fputs(s->data(), stderr); |
16 | 211 | fputs("\n", stderr); |
17 | 211 | } |
18 | | |
19 | 18 | Str* str(int i) { |
20 | 18 | Str* s = OverAllocatedStr(kIntBufSize); |
21 | 18 | int length = snprintf(s->data(), kIntBufSize, "%d", i); |
22 | 18 | s->SetObjLenFromStrLen(length); |
23 | 18 | return s; |
24 | 18 | } |
25 | | |
26 | 7 | Str* repr(Str* s) { |
27 | 7 | mylib::BufWriter f; |
28 | 7 | f.format_r(s); |
29 | 7 | return f.getvalue(); |
30 | 7 | } |
31 | | |
32 | | // Helper for str_to_int() that doesn't use exceptions. |
33 | | // Like atoi(), but with better error checking. |
34 | 39 | bool _str_to_int(Str* s, int* result, int base) { |
35 | 39 | int s_len = len(s); |
36 | 39 | if (s_len == 0) { |
37 | 1 | return false; // special case for empty string |
38 | 1 | } |
39 | | |
40 | 38 | char* p; // mutated by strtol |
41 | | |
42 | 38 | long v = strtol(s->data(), &p, base); |
43 | 38 | switch (v) { |
44 | 1 | case LONG_MIN: |
45 | | // log("underflow"); |
46 | 1 | return false; |
47 | 1 | case LONG_MAX: |
48 | | // log("overflow"); |
49 | 1 | return false; |
50 | 38 | } |
51 | | |
52 | 36 | *result = v; |
53 | | |
54 | | // Return true if it consumed ALL characters. |
55 | 36 | const char* end = s->data_ + s_len; |
56 | | |
57 | | // log("start %p p %p end %p", s->data_, p, end); |
58 | 36 | if (p == end) { |
59 | 31 | return true; |
60 | 31 | } |
61 | | |
62 | | // Trailing space is OK! |
63 | 7 | while (p < end) { |
64 | 6 | if (!isspace(*p)) { |
65 | 4 | return false; |
66 | 4 | } |
67 | 2 | p++; |
68 | 2 | } |
69 | 1 | return true; |
70 | 5 | } |
71 | | |
72 | 4 | int to_int(Str* s, int base) { |
73 | 4 | int i; |
74 | 4 | if (_str_to_int(s, &i, base)) { |
75 | 4 | return i; |
76 | 4 | } else { |
77 | 0 | throw Alloc<ValueError>(); |
78 | 0 | } |
79 | 4 | } |
80 | | |
81 | 24 | int to_int(Str* s) { |
82 | 24 | int i; |
83 | 24 | if (_str_to_int(s, &i, 10)) { |
84 | 22 | return i; |
85 | 22 | } else { |
86 | 2 | throw Alloc<ValueError>(); |
87 | 2 | } |
88 | 24 | } |
89 | | |
90 | 431 | Str* chr(int i) { |
91 | | // NOTE: i should be less than 256, in which we could return an object from |
92 | | // GLOBAL_STR() pool, like StrIter |
93 | 431 | auto result = AllocStr(1); |
94 | 431 | result->data_[0] = i; |
95 | 431 | return result; |
96 | 431 | } |
97 | | |
98 | 434 | int ord(Str* s) { |
99 | 434 | assert(len(s) == 1); |
100 | | // signed to unsigned conversion, so we don't get values like -127 |
101 | 0 | uint8_t c = static_cast<uint8_t>(s->data_[0]); |
102 | 434 | return c; |
103 | 434 | } |
104 | | |
105 | 0 | bool to_bool(Str* s) { |
106 | 0 | return len(s) != 0; |
107 | 0 | } |
108 | | |
109 | 0 | double to_float(Str* s) { |
110 | 0 | double result = atof(s->data_); |
111 | 0 | return result; |
112 | 0 | } |
113 | | |
114 | | // e.g. ('a' in 'abc') |
115 | 72 | bool str_contains(Str* haystack, Str* needle) { |
116 | | // Common case |
117 | 72 | if (len(needle) == 1) { |
118 | 66 | return memchr(haystack->data_, needle->data_[0], len(haystack)); |
119 | 66 | } |
120 | | |
121 | 6 | if (len(needle) > len(haystack)) { |
122 | 1 | return false; |
123 | 1 | } |
124 | | |
125 | | // General case. TODO: We could use a smarter substring algorithm. |
126 | | |
127 | 5 | const char* end = haystack->data_ + len(haystack); |
128 | 5 | const char* last_possible = end - len(needle); |
129 | 5 | const char* p = haystack->data_; |
130 | | |
131 | 11 | while (p <= last_possible) { |
132 | 10 | if (memcmp(p, needle->data_, len(needle)) == 0) { |
133 | 4 | return true; |
134 | 4 | } |
135 | 6 | p++; |
136 | 6 | } |
137 | 1 | return false; |
138 | 5 | } |
139 | | |
140 | 43 | Str* str_repeat(Str* s, int times) { |
141 | | // Python allows -1 too, and Oil used that |
142 | 43 | if (times <= 0) { |
143 | 15 | return kEmptyString; |
144 | 15 | } |
145 | 28 | int len_ = len(s); |
146 | 28 | int new_len = len_ * times; |
147 | 28 | Str* result = AllocStr(new_len); |
148 | | |
149 | 28 | char* dest = result->data_; |
150 | 117 | for (int i = 0; i < times; i++) { |
151 | 89 | memcpy(dest, s->data_, len_); |
152 | 89 | dest += len_; |
153 | 89 | } |
154 | 28 | return result; |
155 | 43 | } |
156 | | |
157 | | // for os_path.join() |
158 | | // NOTE(Jesse): Perfect candidate for bounded_buffer |
159 | 9 | Str* str_concat3(Str* a, Str* b, Str* c) { |
160 | 9 | int a_len = len(a); |
161 | 9 | int b_len = len(b); |
162 | 9 | int c_len = len(c); |
163 | | |
164 | 9 | int new_len = a_len + b_len + c_len; |
165 | 9 | Str* result = AllocStr(new_len); |
166 | 9 | char* pos = result->data_; |
167 | | |
168 | 9 | memcpy(pos, a->data_, a_len); |
169 | 9 | pos += a_len; |
170 | | |
171 | 9 | memcpy(pos, b->data_, b_len); |
172 | 9 | pos += b_len; |
173 | | |
174 | 9 | memcpy(pos, c->data_, c_len); |
175 | | |
176 | 9 | assert(pos + c_len == result->data_ + new_len); |
177 | | |
178 | 0 | return result; |
179 | 9 | } |
180 | | |
181 | 59 | Str* str_concat(Str* a, Str* b) { |
182 | 59 | int a_len = len(a); |
183 | 59 | int b_len = len(b); |
184 | 59 | int new_len = a_len + b_len; |
185 | 59 | Str* result = AllocStr(new_len); |
186 | 59 | char* buf = result->data_; |
187 | | |
188 | 59 | memcpy(buf, a->data_, a_len); |
189 | 59 | memcpy(buf + a_len, b->data_, b_len); |
190 | | |
191 | 59 | return result; |
192 | 59 | } |
193 | | |
194 | | // |
195 | | // Comparators |
196 | | // |
197 | | |
198 | 1.30k | bool str_equals(Str* left, Str* right) { |
199 | | // Fast path for identical strings. String deduplication during GC could |
200 | | // make this more likely. String interning could guarantee it, allowing us |
201 | | // to remove memcmp(). |
202 | 1.30k | if (left == right) { |
203 | 27 | return true; |
204 | 27 | } |
205 | | |
206 | | // obj_len_ equal implies string lengths are equal |
207 | | |
208 | 1.27k | if (left->obj_len_ == right->obj_len_) { |
209 | 489 | assert(len(left) == len(right)); |
210 | 0 | return memcmp(left->data_, right->data_, len(left)) == 0; |
211 | 489 | } |
212 | | |
213 | 790 | return false; |
214 | 1.27k | } |
215 | | |
216 | 4 | bool maybe_str_equals(Str* left, Str* right) { |
217 | 4 | if (left && right) { |
218 | 2 | return str_equals(left, right); |
219 | 2 | } |
220 | | |
221 | 2 | if (!left && !right) { |
222 | 0 | return true; // None == None |
223 | 0 | } |
224 | | |
225 | 2 | return false; // one is None and one is a Str* |
226 | 2 | } |
227 | | |
228 | | // TODO(Jesse): Make an inline version of this |
229 | 911 | bool are_equal(Str* left, Str* right) { |
230 | 911 | return str_equals(left, right); |
231 | 911 | } |
232 | | |
233 | | // TODO(Jesse): Make an inline version of this |
234 | 19 | bool are_equal(int left, int right) { |
235 | 19 | return left == right; |
236 | 19 | } |
237 | | |
238 | | // TODO(Jesse): Make an inline version of this |
239 | 159 | bool keys_equal(int left, int right) { |
240 | 159 | return left == right; |
241 | 159 | } |
242 | | |
243 | | // TODO(Jesse): Make an inline version of this |
244 | 868 | bool keys_equal(Str* left, Str* right) { |
245 | 868 | return are_equal(left, right); |
246 | 868 | } |
247 | | |
248 | 0 | bool are_equal(Tuple2<Str*, int>* t1, Tuple2<Str*, int>* t2) { |
249 | 0 | bool result = are_equal(t1->at0(), t2->at0()); |
250 | 0 | result = result && (t1->at1() == t2->at1()); |
251 | 0 | return result; |
252 | 0 | } |
253 | | |
254 | 148 | bool str_equals0(const char* c_string, Str* s) { |
255 | 148 | int n = strlen(c_string); |
256 | 148 | if (len(s) == n) { |
257 | 68 | return memcmp(s->data_, c_string, n) == 0; |
258 | 80 | } else { |
259 | 80 | return false; |
260 | 80 | } |
261 | 148 | } |