examples

Coverage Report

Created: 2025-05-31 15:38

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