cpp

Coverage Report

Created: 2023-03-07 20:24

/home/andy/git/oilshell/oil/mycpp/gc_builtins.cc
Line
Count
Source (jump to first uncovered line)
1
#include <ctype.h>  // isspace()
2
#include <errno.h>  // errno
3
4
#include "_build/detected-cpp-config.h"
5
6
#ifdef HAVE_READLINE
7
  #include <readline/readline.h>
8
#endif
9
10
#include "mycpp/runtime.h"
11
12
// forward decl
13
namespace py_readline {
14
Str* readline(Str*);
15
}
16
17
// Translation of Python's print().
18
118
void print(Str* s) {
19
118
  fputs(s->data_, stdout);  // print until first NUL
20
118
  fputc('\n', stdout);
21
118
}
22
23
23
Str* str(int i) {
24
23
  Str* s = OverAllocatedStr(kIntBufSize);
25
23
  int length = snprintf(s->data(), kIntBufSize, "%d", i);
26
23
  s->MaybeShrink(length);
27
23
  return s;
28
23
}
29
30
// Print quoted string.  TODO: use C-style strings (YSTR)
31
56
Str* repr(Str* s) {
32
  // Worst case: \0 becomes 4 bytes as '\\x00', and then two quote bytes.
33
56
  int n = len(s);
34
56
  int upper_bound = n * 4 + 2;
35
36
56
  Str* result = OverAllocatedStr(upper_bound);
37
38
  // Single quote by default.
39
56
  char quote = '\'';
40
56
  if (memchr(s->data_, '\'', n) && !memchr(s->data_, '"', n)) {
41
10
    quote = '"';
42
10
  }
43
56
  char* p = result->data_;
44
45
  // From PyString_Repr()
46
56
  *p++ = quote;
47
474
  for (int i = 0; i < n; ++i) {
48
418
    char c = s->data_[i];
49
418
    if (c == quote || c == '\\') {
50
0
      *p++ = '\\';
51
0
      *p++ = c;
52
418
    } else if (c == '\t') {
53
7
      *p++ = '\\';
54
7
      *p++ = 't';
55
411
    } else if (c == '\n') {
56
14
      *p++ = '\\';
57
14
      *p++ = 'n';
58
397
    } else if (c == '\r') {
59
7
      *p++ = '\\';
60
7
      *p++ = 'r';
61
390
    } else if (isprint(c)) {
62
372
      *p++ = c;
63
372
    } else {  // Unprintable is \xff
64
18
      sprintf(p, "\\x%02x", c & 0xff);
65
18
      p += 4;
66
18
    }
67
418
  }
68
56
  *p++ = quote;
69
56
  *p = '\0';
70
71
56
  int length = p - result->data_;
72
56
  result->MaybeShrink(length);
73
56
  return result;
74
56
}
75
76
// Helper for str_to_int() that doesn't use exceptions.
77
84
bool StringToInteger(const char* s, int length, int base, int* result) {
78
84
  if (length == 0) {
79
2
    return false;  // empty string isn't a valid integer
80
2
  }
81
82
82
  char* pos;  // mutated by strtol
83
82
  long v = strtol(s, &pos, base);
84
85
82
  switch (v) {
86
2
  case LONG_MIN:
87
2
    return false;  // underflow
88
2
  case LONG_MAX:
89
2
    return false;  // overflow
90
82
  }
91
92
78
  const char* end = s + length;
93
78
  if (pos == end) {
94
69
    *result = v;
95
69
    return true;  // strtol() consumed ALL characters.
96
69
  }
97
98
13
  while (pos < end) {
99
11
    if (!isspace(*pos)) {
100
7
      return false;  // Trailing non-space
101
7
    }
102
4
    pos++;
103
4
  }
104
105
2
  *result = v;
106
2
  return true;  // Trailing space is OK
107
9
}
108
109
8
int to_int(Str* s, int base) {
110
8
  int i;
111
8
  if (StringToInteger(s->data_, len(s), base, &i)) {
112
8
    return i;
113
8
  } else {
114
0
    throw Alloc<ValueError>();
115
0
  }
116
8
}
117
118
31
int to_int(Str* s) {
119
31
  int i;
120
31
  if (StringToInteger(s->data_, len(s), 10, &i)) {
121
28
    return i;
122
28
  } else {
123
3
    throw Alloc<ValueError>();
124
3
  }
125
31
}
126
127
832
Str* chr(int i) {
128
  // NOTE: i should be less than 256, in which we could return an object from
129
  // GLOBAL_STR() pool, like StrIter
130
832
  auto result = NewStr(1);
131
832
  result->data_[0] = i;
132
832
  return result;
133
832
}
134
135
836
int ord(Str* s) {
136
836
  assert(len(s) == 1);
137
  // signed to unsigned conversion, so we don't get values like -127
138
0
  uint8_t c = static_cast<uint8_t>(s->data_[0]);
139
836
  return c;
140
836
}
141
142
4
bool to_bool(Str* s) {
143
4
  return len(s) != 0;
144
4
}
145
146
22
double to_float(Str* s) {
147
22
  char* begin = s->data_;
148
22
  char* end = begin + len(s);
149
150
22
  errno = 0;
151
22
  double result = strtod(begin, &end);
152
153
22
  if (errno == ERANGE) {  // error: overflow or underflow
154
    // log("OVERFLOW or UNDERFLOW %s", s->data_);
155
    // log("result %f", result);
156
4
    throw Alloc<ValueError>();
157
4
  }
158
18
  if (end == begin) {  // error: not a floating point number
159
4
    throw Alloc<ValueError>();
160
4
  }
161
162
14
  return result;
163
18
}
164
165
// e.g. ('a' in 'abc')
166
84
bool str_contains(Str* haystack, Str* needle) {
167
  // Common case
168
84
  if (len(needle) == 1) {
169
72
    return memchr(haystack->data_, needle->data_[0], len(haystack));
170
72
  }
171
172
12
  if (len(needle) > len(haystack)) {
173
2
    return false;
174
2
  }
175
176
  // General case. TODO: We could use a smarter substring algorithm.
177
178
10
  const char* end = haystack->data_ + len(haystack);
179
10
  const char* last_possible = end - len(needle);
180
10
  const char* p = haystack->data_;
181
182
22
  while (p <= last_possible) {
183
20
    if (memcmp(p, needle->data_, len(needle)) == 0) {
184
8
      return true;
185
8
    }
186
12
    p++;
187
12
  }
188
2
  return false;
189
10
}
190
191
52
Str* str_repeat(Str* s, int times) {
192
  // Python allows -1 too, and Oil used that
193
52
  if (times <= 0) {
194
18
    return kEmptyString;
195
18
  }
196
34
  int len_ = len(s);
197
34
  int new_len = len_ * times;
198
34
  Str* result = NewStr(new_len);
199
200
34
  char* dest = result->data_;
201
527
  for (int i = 0; i < times; i++) {
202
493
    memcpy(dest, s->data_, len_);
203
493
    dest += len_;
204
493
  }
205
34
  return result;
206
52
}
207
208
// for os_path.join()
209
// NOTE(Jesse): Perfect candidate for BoundedBuffer
210
20
Str* str_concat3(Str* a, Str* b, Str* c) {
211
20
  int a_len = len(a);
212
20
  int b_len = len(b);
213
20
  int c_len = len(c);
214
215
20
  int new_len = a_len + b_len + c_len;
216
20
  Str* result = NewStr(new_len);
217
20
  char* pos = result->data_;
218
219
20
  memcpy(pos, a->data_, a_len);
220
20
  pos += a_len;
221
222
20
  memcpy(pos, b->data_, b_len);
223
20
  pos += b_len;
224
225
20
  memcpy(pos, c->data_, c_len);
226
227
20
  assert(pos + c_len == result->data_ + new_len);
228
229
0
  return result;
230
20
}
231
232
71
Str* str_concat(Str* a, Str* b) {
233
71
  int a_len = len(a);
234
71
  int b_len = len(b);
235
71
  int new_len = a_len + b_len;
236
71
  Str* result = NewStr(new_len);
237
71
  char* buf = result->data_;
238
239
71
  memcpy(buf, a->data_, a_len);
240
71
  memcpy(buf + a_len, b->data_, b_len);
241
242
71
  return result;
243
71
}
244
245
//
246
// Comparators
247
//
248
249
4.58k
bool str_equals(Str* left, Str* right) {
250
  // Fast path for identical strings.  String deduplication during GC could
251
  // make this more likely.  String interning could guarantee it, allowing us
252
  // to remove memcmp().
253
4.58k
  if (left == right) {
254
157
    return true;
255
157
  }
256
257
4.42k
  if (left == nullptr || right == nullptr) {
258
0
    return false;
259
0
  }
260
261
  // obj_len equal implies string lengths are equal
262
263
4.42k
  if (left->len_ == right->len_) {
264
    // assert(len(left) == len(right));
265
806
    return memcmp(left->data_, right->data_, left->len_) == 0;
266
806
  }
267
268
3.62k
  return false;
269
4.42k
}
270
271
10
bool maybe_str_equals(Str* left, Str* right) {
272
10
  if (left && right) {
273
4
    return str_equals(left, right);
274
4
  }
275
276
6
  if (!left && !right) {
277
2
    return true;  // None == None
278
2
  }
279
280
4
  return false;  // one is None and one is a Str*
281
6
}
282
283
// TODO(Jesse): Make an inline version of this
284
3.99k
bool are_equal(Str* left, Str* right) {
285
3.99k
  return str_equals(left, right);
286
3.99k
}
287
288
// TODO(Jesse): Make an inline version of this
289
35
bool are_equal(int left, int right) {
290
35
  return left == right;
291
35
}
292
293
// TODO(Jesse): Make an inline version of this
294
356
bool keys_equal(int left, int right) {
295
356
  return left == right;
296
356
}
297
298
// TODO(Jesse): Make an inline version of this
299
3.89k
bool keys_equal(Str* left, Str* right) {
300
3.89k
  return are_equal(left, right);
301
3.89k
}
302
303
12
bool are_equal(Tuple2<Str*, int>* t1, Tuple2<Str*, int>* t2) {
304
12
  bool result = are_equal(t1->at0(), t2->at0());
305
12
  result = result && (t1->at1() == t2->at1());
306
12
  return result;
307
12
}
308
309
8
bool are_equal(Tuple2<int, int>* t1, Tuple2<int, int>* t2) {
310
8
  return t1->at0() == t2->at0() && t1->at1() == t2->at1();
311
8
}
312
313
8
bool keys_equal(Tuple2<int, int>* t1, Tuple2<int, int>* t2) {
314
8
  return are_equal(t1, t2);
315
8
}
316
317
8
bool keys_equal(Tuple2<Str*, int>* t1, Tuple2<Str*, int>* t2) {
318
8
  return are_equal(t1, t2);
319
8
}
320
321
237
bool str_equals0(const char* c_string, Str* s) {
322
237
  int n = strlen(c_string);
323
237
  if (len(s) == n) {
324
148
    return memcmp(s->data_, c_string, n) == 0;
325
148
  } else {
326
89
    return false;
327
89
  }
328
237
}
329
330
4
int hash(Str* s) {
331
  // FNV-1 from http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1
332
4
  int h = 2166136261;          // 32-bit FNV-1 offset basis
333
4
  constexpr int p = 16777619;  // 32-bit FNV-1 prime
334
40
  for (int i = 0; i < len(s); i++) {
335
36
    h *= s->data()[i];
336
36
    h ^= p;
337
36
  }
338
4
  return h;
339
4
}
340
341
8
int max(int a, int b) {
342
8
  return std::max(a, b);
343
8
}
344
345
2
int max(List<int>* elems) {
346
2
  int n = len(elems);
347
2
  if (n < 1) {
348
0
    throw Alloc<ValueError>();
349
0
  }
350
351
2
  int ret = elems->index_(0);
352
10
  for (int i = 0; i < n; ++i) {
353
8
    int cand = elems->index_(i);
354
8
    if (cand > ret) {
355
2
      ret = cand;
356
2
    }
357
8
  }
358
359
2
  return ret;
360
2
}
361
362
0
Str* raw_input(Str* prompt) {
363
0
#ifdef HAVE_READLINE
364
0
  Str* ret = py_readline::readline(prompt);
365
0
  if (ret == nullptr) {
366
0
    throw Alloc<EOFError>();
367
0
  }
368
0
  return ret;
369
#else
370
  assert(0);  // not implemented
371
#endif
372
0
}