cpp

Coverage Report

Created: 2024-08-25 11:48

/home/andy/git/oilshell/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
namespace mylib {
8
9
2
void InitCppOnly() {
10
  // We don't seem need this now that we have ctx_FlushStdout().
11
  // setvbuf(stdout, 0, _IONBF, 0);
12
13
  // Arbitrary threshold of 50K objects based on eyeballing
14
  // benchmarks/osh-runtime 10K or 100K aren't too bad either.
15
2
  gHeap.Init(50000);
16
2
}
17
18
323
void print_stderr(BigStr* s) {
19
323
  fputs(s->data_, stderr);  // prints until first NUL
20
323
  fputc('\n', stderr);
21
323
}
22
23
#if 0
24
void writeln(BigStr* s, int fd) {
25
  // TODO: handle errors and write in a loop, like posix::write().  If possible,
26
  // use posix::write directly, but that introduces some dependency problems.
27
28
  if (write(fd, s->data_, len(s)) < 0) {
29
    assert(0);
30
  }
31
  if (write(fd, "\n", 1) < 0) {
32
    assert(0);
33
  }
34
}
35
#endif
36
37
1
BigStr* JoinBytes(List<int>* byte_list) {
38
1
  int n = len(byte_list);
39
1
  BigStr* result = NewStr(n);
40
257
  for (int i = 0; i < n; ++i) {
41
256
    result->data_[i] = byte_list->at(i);
42
256
  }
43
1
  return result;
44
1
}
45
46
// For BashArray
47
0
void BigIntSort(List<mops::BigInt>* keys) {
48
0
  keys->sort();
49
0
}
50
51
class MutableStr : public BigStr {};
52
53
305
MutableStr* NewMutableStr(int n) {
54
  // In order for everything to work, MutableStr must be identical in layout to
55
  // BigStr. One easy way to achieve this is for MutableStr to have no members
56
  // and to inherit from BigStr.
57
305
  static_assert(sizeof(MutableStr) == sizeof(BigStr),
58
305
                "BigStr and MutableStr must have same size");
59
305
  return reinterpret_cast<MutableStr*>(NewStr(n));
60
305
}
61
62
10
Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim) {
63
10
  DCHECK(len(delim) == 1);
64
65
0
  const char* start = s->data_;  // note: this pointer may move
66
10
  char c = delim->data_[0];
67
10
  int length = len(s);
68
69
10
  const char* p = static_cast<const char*>(memchr(start, c, length));
70
71
10
  if (p) {
72
6
    int len1 = p - start;
73
6
    int len2 = length - len1 - 1;  // -1 for delim
74
75
6
    BigStr* s1 = nullptr;
76
6
    BigStr* s2 = nullptr;
77
    // Allocate together to avoid 's' moving in between
78
6
    s1 = NewStr(len1);
79
6
    s2 = NewStr(len2);
80
81
6
    memcpy(s1->data_, s->data_, len1);
82
6
    memcpy(s2->data_, s->data_ + len1 + 1, len2);
83
84
6
    return Tuple2<BigStr*, BigStr*>(s1, s2);
85
6
  } else {
86
4
    return Tuple2<BigStr*, BigStr*>(s, nullptr);
87
4
  }
88
10
}
89
90
LineReader* gStdin;
91
92
4
LineReader* open(BigStr* path) {
93
  // TODO: Don't use C I/O; use POSIX I/O!
94
4
  FILE* f = fopen(path->data_, "r");
95
4
  if (f == nullptr) {
96
0
    throw Alloc<IOError>(errno);
97
0
  }
98
99
4
  return reinterpret_cast<LineReader*>(Alloc<CFile>(f));
100
4
}
101
102
638
BigStr* CFile::readline() {
103
638
  char* line = nullptr;
104
638
  size_t allocated_size = 0;  // unused
105
106
  // Reset errno because we turn the EOF error into empty string (like Python).
107
638
  errno = 0;
108
638
  ssize_t len = getline(&line, &allocated_size, f_);
109
  // log("getline = %d", len);
110
638
  if (len < 0) {
111
    // Reset EOF flag so the next readline() will get a line.
112
2
    clearerr(f_);
113
114
    // man page says the buffer should be freed even if getline fails
115
2
    free(line);
116
117
2
    if (errno != 0) {  // Unexpected error
118
      // log("getline() error: %s", strerror(errno));
119
0
      throw Alloc<IOError>(errno);
120
0
    }
121
2
    return kEmptyString;  // Indicate EOF with empty string, like Python
122
2
  }
123
124
  // Note: getline() NUL-terminates the buffer
125
636
  BigStr* result = ::StrFromC(line, len);
126
636
  free(line);
127
636
  return result;
128
638
}
129
130
4
bool CFile::isatty() {
131
4
  return ::isatty(fileno(f_));
132
4
}
133
134
// Problem: most BigStr methods like index() and slice() COPY so they have a
135
// NUL terminator.
136
// log("%s") falls back on sprintf, so it expects a NUL terminator.
137
// It would be easier for us to just share.
138
16
BigStr* BufLineReader::readline() {
139
16
  BigStr* line = nullptr;
140
141
16
  int str_len = len(s_);
142
16
  if (pos_ == str_len) {
143
8
    return kEmptyString;
144
8
  }
145
146
8
  int orig_pos = pos_;
147
8
  const char* p = strchr(s_->data_ + pos_, '\n');
148
  // log("pos_ = %s", pos_);
149
8
  int line_len;
150
8
  if (p) {
151
4
    int new_pos = p - s_->data_;
152
4
    line_len = new_pos - pos_ + 1;  // past newline char
153
4
    pos_ = new_pos + 1;
154
4
  } else {             // leftover line
155
4
    if (pos_ == 0) {   // The string has no newlines at all -- just return it
156
2
      pos_ = str_len;  // advance to the end
157
2
      return s_;
158
2
    } else {
159
2
      line_len = str_len - pos_;
160
2
      pos_ = str_len;  // advance to the end
161
2
    }
162
4
  }
163
164
6
  line = NewStr(line_len);
165
6
  memcpy(line->data_, s_->data_ + orig_pos, line_len);
166
6
  DCHECK(line->data_[line_len] == '\0');
167
0
  return line;
168
8
}
169
170
Writer* gStdout;
171
Writer* gStderr;
172
173
//
174
// CFileWriter
175
//
176
177
125
void CFile::write(BigStr* s) {
178
  // Writes can be short!
179
125
  int n = len(s);
180
125
  int num_written = ::fwrite(s->data_, sizeof(char), n, f_);
181
  // Similar to CPython fileobject.c
182
125
  if (num_written != n) {
183
0
    throw Alloc<IOError>(errno);
184
0
  }
185
125
}
186
187
2
void CFile::flush() {
188
2
  if (::fflush(f_) != 0) {
189
0
    throw Alloc<IOError>(errno);
190
0
  }
191
2
}
192
193
2
void CFile::close() {
194
2
  if (::fclose(f_) != 0) {
195
0
    throw Alloc<IOError>(errno);
196
0
  }
197
2
}
198
199
//
200
// BufWriter
201
//
202
203
1.05k
void BufWriter::EnsureMoreSpace(int n) {
204
1.05k
  if (str_ == nullptr) {
205
    // TODO: we could make the default capacity big enough for a line, e.g. 128
206
    // capacity: 128 -> 256 -> 512
207
119
    str_ = NewMutableStr(n);
208
119
    return;
209
119
  }
210
211
936
  int current_cap = len(str_);
212
936
  DCHECK(current_cap >= len_);
213
214
0
  int new_cap = len_ + n;
215
216
936
  if (current_cap < new_cap) {
217
186
    auto* s = NewMutableStr(std::max(current_cap * 2, new_cap));
218
186
    memcpy(s->data_, str_->data_, len_);
219
186
    s->data_[len_] = '\0';
220
186
    str_ = s;
221
186
  }
222
936
}
223
224
131
uint8_t* BufWriter::LengthPointer() {
225
  // start + len
226
131
  return reinterpret_cast<uint8_t*>(str_->data_) + len_;
227
131
}
228
229
131
uint8_t* BufWriter::CapacityPointer() {
230
  // start + capacity
231
131
  return reinterpret_cast<uint8_t*>(str_->data_) + str_->len_;
232
131
}
233
234
119
void BufWriter::SetLengthFrom(uint8_t* length_ptr) {
235
119
  uint8_t* begin = reinterpret_cast<uint8_t*>(str_->data_);
236
119
  DCHECK(length_ptr >= begin);  // we should have written some data
237
238
  // Set the length, e.g. so we know where to resume writing from
239
0
  len_ = length_ptr - begin;
240
  // printf("SET LEN to %d\n", len_);
241
119
}
242
243
12
void BufWriter::Truncate(int length) {
244
12
  len_ = length;
245
12
}
246
247
943
void BufWriter::WriteRaw(char* s, int n) {
248
943
  DCHECK(is_valid_);  // Can't write() after getvalue()
249
250
  // write('') is a no-op, so don't create Buf if we don't need to
251
943
  if (n == 0) {
252
9
    return;
253
9
  }
254
255
934
  EnsureMoreSpace(n);
256
257
  // Append the contents to the buffer
258
934
  memcpy(str_->data_ + len_, s, n);
259
934
  len_ += n;
260
934
  str_->data_[len_] = '\0';
261
934
}
262
263
170
void BufWriter::WriteConst(const char* c_string) {
264
  // meant for short strings like '"'
265
170
  WriteRaw(const_cast<char*>(c_string), strlen(c_string));
266
170
}
267
268
773
void BufWriter::write(BigStr* s) {
269
773
  WriteRaw(s->data_, len(s));
270
773
}
271
272
4
void BufWriter::write_spaces(int n) {
273
4
  DCHECK(n >= 0);
274
4
  if (n == 0) {
275
2
    return;
276
2
  }
277
278
2
  EnsureMoreSpace(n);
279
280
2
  char* dest = str_->data_ + len_;
281
8
  for (int i = 0; i < n; ++i) {
282
6
    dest[i] = ' ';
283
6
  }
284
2
  len_ += n;
285
2
  str_->data_[len_] = '\0';
286
2
}
287
288
112
BigStr* BufWriter::getvalue() {
289
112
  DCHECK(is_valid_);  // Check for two INVALID getvalue() in a row
290
0
  is_valid_ = false;
291
292
112
  if (str_ == nullptr) {  // if no write() methods are called, the result is ""
293
2
    return kEmptyString;
294
110
  } else {
295
110
    BigStr* s = str_;
296
110
    s->MaybeShrink(len_);
297
110
    str_ = nullptr;
298
110
    len_ = -1;
299
110
    return s;
300
110
  }
301
112
}
302
303
}  // namespace mylib