mycpp

Coverage Report

Created: 2024-06-09 04:56

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