cpp

Coverage Report

Created: 2022-11-10 11:34

/home/andy/git/oilshell/oil/mycpp/leaky_mylib.cc
Line
Count
Source (jump to first uncovered line)
1
#include <errno.h>
2
#include <stdio.h>
3
#include <unistd.h>  // isatty
4
5
#include "mycpp/runtime.h"
6
7
mylib::FormatStringer gBuf;
8
9
namespace mylib {
10
11
// NOTE: split_once() was in gc_mylib, and is likely not leaky
12
5
Tuple2<Str*, Str*> split_once(Str* s, Str* delim) {
13
5
  StackRoots _roots({&s, &delim});
14
15
5
  assert(len(delim) == 1);
16
17
0
  const char* start = s->data_;  // note: this pointer may move
18
5
  char c = delim->data_[0];
19
5
  int length = len(s);
20
21
5
  const char* p = static_cast<const char*>(memchr(start, c, length));
22
23
5
  if (p) {
24
3
    int len1 = p - start;
25
3
    int len2 = length - len1 - 1;  // -1 for delim
26
27
3
    Str* s1 = nullptr;
28
3
    Str* s2 = nullptr;
29
3
    StackRoots _roots({&s1, &s2});
30
    // Allocate together to avoid 's' moving in between
31
3
    s1 = NewStr(len1);
32
3
    s2 = NewStr(len2);
33
34
3
    memcpy(s1->data_, s->data_, len1);
35
3
    memcpy(s2->data_, s->data_ + len1 + 1, len2);
36
37
3
    return Tuple2<Str*, Str*>(s1, s2);
38
3
  } else {
39
2
    return Tuple2<Str*, Str*>(s, nullptr);
40
2
  }
41
5
}
42
43
LineReader* gStdin;
44
45
2
LineReader* open(Str* path) {
46
2
  StackRoots _roots({&path});
47
48
  // TODO: Don't use C I/O; use POSIX I/O!
49
2
  FILE* f = fopen(path->data_, "r");
50
51
2
  if (f == nullptr) {
52
0
    throw Alloc<IOError>(errno);
53
0
  }
54
2
  return Alloc<CFileLineReader>(f);
55
2
}
56
57
300
Str* CFileLineReader::readline() {
58
300
  char* line = nullptr;
59
300
  size_t allocated_size = 0;  // unused
60
61
  // Reset errno because we turn the EOF error into empty string (like Python).
62
300
  errno = 0;
63
300
  ssize_t len = getline(&line, &allocated_size, f_);
64
300
  if (len < 0) {
65
1
    if (errno != 0) {  // Unexpected error
66
0
      log("getline() error: %s", strerror(errno));
67
0
      throw Alloc<IOError>(errno);
68
0
    }
69
    // Expected EOF
70
1
    return kEmptyString;
71
1
  }
72
73
  // TODO: Fix the leak here.
74
  // Note: getline() NUL terminates the buffer
75
299
  return ::StrFromC(line, len);
76
300
}
77
78
// Problem: most Str methods like index() and slice() COPY so they have a
79
// NUL terminator.
80
// log("%s") falls back on sprintf, so it expects a NUL terminator.
81
// It would be easier for us to just share.
82
4
Str* BufLineReader::readline() {
83
4
  auto self = this;
84
4
  Str* line = nullptr;
85
4
  StackRoots _roots({&self, &line});
86
87
4
  int buf_len = len(s_);
88
4
  if (pos_ == buf_len) {
89
1
    return kEmptyString;
90
1
  }
91
92
3
  int orig_pos = pos_;
93
3
  const char* p = strchr(s_->data_ + pos_, '\n');
94
  // log("pos_ = %s", pos_);
95
3
  int line_len;
96
3
  if (p) {
97
2
    int new_pos = p - self->s_->data_;
98
2
    line_len = new_pos - pos_ + 1;  // past newline char
99
2
    pos_ = new_pos + 1;
100
2
  } else {  // leftover line
101
1
    line_len = buf_len - pos_;
102
1
    pos_ = buf_len;
103
1
  }
104
105
3
  line = NewStr(line_len);
106
3
  memcpy(line->data_, self->s_->data_ + orig_pos, line_len);
107
3
  assert(line->data_[line_len] == '\0');
108
0
  return line;
109
4
}
110
111
Writer* gStdout;
112
Writer* gStderr;
113
114
//
115
// CFileWriter
116
//
117
118
119
void CFileWriter::write(Str* s) {
119
  // note: throwing away the return value
120
119
  fwrite(s->data_, sizeof(char), len(s), f_);
121
119
}
122
123
0
void CFileWriter::flush() {
124
0
  ::fflush(f_);
125
0
}
126
127
1
bool CFileWriter::isatty() {
128
1
  return ::isatty(fileno(f_));
129
1
}
130
131
//
132
// Buf
133
//
134
135
// TODO: Consider renaming MutableStr, or make this a subclass of Str
136
class Buf : Obj {
137
 public:
138
  // The initial capacity is big enough for a line
139
175
  Buf(int cap) : Obj(Tag::Opaque, kZeroMask, 0), len_(0), cap_(cap) {
140
175
  }
141
  void Extend(Str* s);
142
143
 private:
144
  friend class BufWriter;
145
  friend Str* StrFromBuf(const Buf*);
146
  friend Buf* NewBuf(int);
147
148
  // TODO: move this state into BufWriter
149
  int len_;  // data length, not including NUL
150
  int cap_;  // capacity, not including NUL
151
  char data_[1];
152
};
153
154
26
Str* StrFromBuf(const Buf* buf) {
155
26
  return ::StrFromC(buf->data_, buf->len_);
156
26
}
157
158
175
Buf* NewBuf(int cap) {
159
  // TODO: sizeof(Buf) is an overestimate because of flexible array member
160
175
  void* place = gHeap.Allocate(sizeof(Buf) + cap + 1);
161
162
175
  auto* b = new (place) Buf(cap);
163
175
  return b;
164
175
}
165
166
730
void Buf::Extend(Str* s) {
167
730
  const int n = len(s);
168
169
730
  assert(cap_ >= len_ + n);
170
171
0
  memcpy(data_ + len_, s->data_, n);
172
730
  len_ += n;
173
730
  data_[len_] = '\0';
174
730
}
175
176
//
177
// BufWriter
178
//
179
180
// TODO: realloc() to new capacity instead of creating NewBuf()
181
695
Buf* BufWriter::EnsureCapacity(int capacity) {
182
695
  assert(buf_->cap_ >= buf_->len_);
183
184
695
  if (buf_->cap_ < capacity) {
185
140
    auto* b = NewBuf(std::max(buf_->cap_ * 2, capacity));
186
140
    memcpy(b->data_, buf_->data_, buf_->len_);
187
140
    b->len_ = buf_->len_;
188
140
    b->data_[b->len_] = '\0';
189
140
    return b;
190
555
  } else {
191
555
    return buf_;  // no-op
192
555
  }
193
695
}
194
195
738
void BufWriter::write(Str* s) {
196
738
  assert(is_valid_);  // Can't write() after getvalue()
197
198
0
  int n = len(s);
199
200
  // write('') is a no-op, so don't create Buf if we don't need to
201
738
  if (n == 0) {
202
8
    return;
203
8
  }
204
205
730
  if (buf_ == nullptr) {
206
    // TODO: we could make the default capacity big enough for a line, e.g. 128
207
    // capacity: 128 -> 256 -> 512
208
35
    int capacity = n;
209
35
    buf_ = NewBuf(capacity);
210
695
  } else {
211
695
    buf_ = EnsureCapacity(buf_->len_ + n);
212
695
  }
213
214
  // Append the contents to the buffer
215
730
  buf_->Extend(s);
216
730
}
217
218
27
Str* BufWriter::getvalue() {
219
27
  assert(is_valid_);  // Check for two INVALID getvalue() in a row
220
0
  is_valid_ = false;
221
222
27
  if (buf_ == nullptr) {  // if no write() methods are called, the result is ""
223
1
    return kEmptyString;
224
26
  } else {
225
26
    return StrFromBuf(buf_);
226
26
  }
227
27
}
228
229
//
230
// FormatStringer
231
//
232
233
317
Str* FormatStringer::getvalue() {
234
317
  if (data_) {
235
317
    Str* ret = ::StrFromC(data_, len_);
236
317
    reset();  // Invalidate this instance
237
317
    return ret;
238
317
  } else {
239
    // log('') translates to this
240
    // Strings are immutable so we can do this.
241
0
    return kEmptyString;
242
0
  }
243
317
}
244
245
463
void FormatStringer::write_const(const char* s, int len) {
246
463
  int orig_len = len_;
247
463
  len_ += len;
248
  // data_ is nullptr at first
249
463
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
250
251
  // Append to the end
252
463
  memcpy(data_ + orig_len, s, len);
253
463
  data_[len_] = '\0';
254
463
}
255
256
268
void FormatStringer::format_s(Str* s) {
257
268
  int orig_len = len_;
258
268
  int n = len(s);
259
268
  len_ += n;
260
261
  // BUG: This is quadratic!
262
263
  // TODO:
264
  //
265
  // - add capacity_, and double it?  start at 32 bytes -> 64 -> 128
266
  //   - only realloc by doublings?
267
  // - or change this to append to a list?  and then getvalue() does a join()
268
  // on it?
269
  // - DEALLOCATE.  gc_mylib doesn't leak!
270
271
  // data_ is nullptr at first
272
268
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
273
274
  // Append to the end
275
268
  memcpy(data_ + orig_len, s->data_, n);
276
268
  data_[len_] = '\0';
277
268
}
278
279
0
void FormatStringer::format_o(int i) {
280
0
  data_ = static_cast<char*>(realloc(data_, len_ + kIntBufSize));
281
0
  int len = snprintf(data_ + len_, kIntBufSize, "%o", i);
282
0
  len_ += len;
283
0
}
284
285
95
void FormatStringer::format_d(int i) {
286
  // extend to the maximum size
287
95
  data_ = static_cast<char*>(realloc(data_, len_ + kIntBufSize));
288
95
  int len = snprintf(data_ + len_, kIntBufSize, "%d", i);
289
  // but record only the number of bytes written
290
95
  len_ += len;
291
95
}
292
293
// repr() calls this too
294
//
295
// TODO: This could be replaced with QSN?  The upper bound is greater there
296
// because of \u{}.
297
7
void FormatStringer::format_r(Str* s) {
298
  // Worst case: \0 becomes 4 bytes as '\\x00', and then two quote bytes.
299
7
  int n = len(s);
300
7
  int upper_bound = n * 4 + 2;
301
302
  // Extend the buffer
303
7
  data_ = static_cast<char*>(realloc(data_, len_ + upper_bound + 1));
304
305
7
  char quote = '\'';
306
7
  if (memchr(s->data_, '\'', n) && !memchr(s->data_, '"', n)) {
307
2
    quote = '"';
308
2
  }
309
7
  char* p = data_ + len_;  // end of valid data
310
311
  // From PyString_Repr()
312
7
  *p++ = quote;
313
61
  for (int i = 0; i < n; ++i) {
314
54
    char c = s->data_[i];
315
54
    if (c == quote || c == '\\') {
316
0
      *p++ = '\\';
317
0
      *p++ = c;
318
54
    } else if (c == '\t') {
319
1
      *p++ = '\\';
320
1
      *p++ = 't';
321
53
    } else if (c == '\n') {
322
2
      *p++ = '\\';
323
2
      *p++ = 'n';
324
51
    } else if (c == '\r') {
325
1
      *p++ = '\\';
326
1
      *p++ = 'r';
327
50
    } else if (c < ' ' || c >= 0x7f) {
328
3
      sprintf(p, "\\x%02x", c & 0xff);
329
3
      p += 4;
330
47
    } else {
331
47
      *p++ = c;
332
47
    }
333
54
  }
334
7
  *p++ = quote;
335
7
  *p = '\0';
336
337
7
  len_ = p - data_;
338
  // Shrink the buffer.  This is valid usage and GNU libc says it can actually
339
  // release.
340
7
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
341
7
}
342
343
}  // namespace mylib