cpp

Coverage Report

Created: 2025-05-26 16:01

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