cpp

Coverage Report

Created: 2022-09-21 22:22

/home/andy/git/oilshell/oil/mycpp/leaky_mylib.cc
Line
Count
Source (jump to first uncovered line)
1
// clang-format off
2
#include "mycpp/myerror.h"
3
// clang-format on
4
5
#include <errno.h>
6
#include <stdio.h>
7
#include <unistd.h>  // isatty
8
9
#include "mycpp/runtime.h"
10
11
namespace mylib {
12
13
// NOTE: split_once() was in gc_mylib, and is likely not leaky
14
5
Tuple2<Str*, Str*> split_once(Str* s, Str* delim) {
15
5
  StackRoots _roots({&s, &delim});
16
17
5
  assert(len(delim) == 1);
18
19
0
  const char* start = s->data_;  // note: this pointer may move
20
5
  char c = delim->data_[0];
21
5
  int length = len(s);
22
23
5
  const char* p = static_cast<const char*>(memchr(start, c, length));
24
25
5
  if (p) {
26
3
    int len1 = p - start;
27
3
    int len2 = length - len1 - 1;  // -1 for delim
28
29
3
    Str* s1 = nullptr;
30
3
    Str* s2 = nullptr;
31
3
    StackRoots _roots({&s1, &s2});
32
    // Allocate together to avoid 's' moving in between
33
3
    s1 = AllocStr(len1);
34
3
    s2 = AllocStr(len2);
35
36
3
    memcpy(s1->data_, s->data_, len1);
37
3
    memcpy(s2->data_, s->data_ + len1 + 1, len2);
38
39
3
    return Tuple2<Str*, Str*>(s1, s2);
40
3
  } else {
41
2
    return Tuple2<Str*, Str*>(s, nullptr);
42
2
  }
43
5
}
44
45
326
Str* BufWriter::getvalue() {
46
326
  if (data_) {
47
326
    Str* ret = ::StrFromC(data_, len_);
48
326
    reset();  // Invalidate this instance
49
326
    return ret;
50
326
  } else {
51
    // log('') translates to this
52
    // Strings are immutable so we can do this.
53
0
    return kEmptyString;
54
0
  }
55
326
}
56
57
LineReader* gStdin;
58
59
2
LineReader* open(Str* path) {
60
2
  StackRoots _roots({&path});
61
62
  // TODO: Don't use C I/O; use POSIX I/O!
63
2
  FILE* f = fopen(path->data_, "r");
64
65
2
  if (f == nullptr) {
66
0
    throw Alloc<IOError>(errno);
67
0
  }
68
2
  return Alloc<CFileLineReader>(f);
69
2
}
70
71
300
Str* CFileLineReader::readline() {
72
300
  char* line = nullptr;
73
300
  size_t allocated_size = 0;  // unused
74
75
  // Reset errno because we turn the EOF error into empty string (like Python).
76
300
  errno = 0;
77
300
  ssize_t len = getline(&line, &allocated_size, f_);
78
300
  if (len < 0) {
79
1
    if (errno != 0) {  // Unexpected error
80
0
      log("getline() error: %s", strerror(errno));
81
0
      throw Alloc<IOError>(errno);
82
0
    }
83
    // Expected EOF
84
1
    return kEmptyString;
85
1
  }
86
87
  // TODO: Fix the leak here.
88
  // Note: getline() NUL terminates the buffer
89
299
  return ::StrFromC(line, len);
90
300
}
91
92
// Problem: most Str methods like index() and slice() COPY so they have a
93
// NUL terminator.
94
// log("%s") falls back on sprintf, so it expects a NUL terminator.
95
// It would be easier for us to just share.
96
4
Str* BufLineReader::readline() {
97
4
  auto self = this;
98
4
  Str* line = nullptr;
99
4
  StackRoots _roots({&self, &line});
100
101
4
  int buf_len = len(s_);
102
4
  if (pos_ == buf_len) {
103
1
    return kEmptyString;
104
1
  }
105
106
3
  int orig_pos = pos_;
107
3
  const char* p = strchr(s_->data_ + pos_, '\n');
108
  // log("pos_ = %s", pos_);
109
3
  int line_len;
110
3
  if (p) {
111
2
    int new_pos = p - self->s_->data_;
112
2
    line_len = new_pos - pos_ + 1;  // past newline char
113
2
    pos_ = new_pos + 1;
114
2
  } else {  // leftover line
115
1
    line_len = buf_len - pos_;
116
1
    pos_ = buf_len;
117
1
  }
118
119
3
  line = AllocStr(line_len);
120
3
  memcpy(line->data_, self->s_->data_ + orig_pos, line_len);
121
3
  assert(line->data_[line_len] == '\0');
122
0
  return line;
123
4
}
124
125
Writer* gStdout;
126
Writer* gStderr;
127
128
985
void BufWriter::write(Str* s) {
129
985
  int orig_len = len_;
130
985
  int n = len(s);
131
985
  len_ += n;
132
133
  // BUG: This is quadratic!
134
135
  // TODO:
136
  //
137
  // - add capacity_, and double it?  start at 32 bytes -> 64 -> 128
138
  //   - only realloc by doublings?
139
  // - or change this to append to a list?  and then getvalue() does a join()
140
  // on it?
141
  // - DEALLOCATE.  gc_mylib doesn't leak!
142
143
  // data_ is nullptr at first
144
985
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
145
146
  // Append to the end
147
985
  memcpy(data_ + orig_len, s->data_, n);
148
985
  data_[len_] = '\0';
149
985
}
150
151
434
void BufWriter::write_const(const char* s, int len) {
152
434
  int orig_len = len_;
153
434
  len_ += len;
154
  // data_ is nullptr at first
155
434
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
156
157
  // Append to the end
158
434
  memcpy(data_ + orig_len, s, len);
159
434
  data_[len_] = '\0';
160
434
}
161
162
250
void BufWriter::format_s(Str* s) {
163
250
  this->write(s);
164
250
}
165
166
0
void BufWriter::format_o(int i) {
167
0
  NotImplemented();
168
0
}
169
170
91
void BufWriter::format_d(int i) {
171
  // extend to the maximum size
172
91
  data_ = static_cast<char*>(realloc(data_, len_ + kIntBufSize));
173
91
  int len = snprintf(data_ + len_, kIntBufSize, "%d", i);
174
  // but record only the number of bytes written
175
91
  len_ += len;
176
91
}
177
178
// repr() calls this too
179
//
180
// TODO: This could be replaced with QSN?  The upper bound is greater there
181
// because of \u{}.
182
7
void BufWriter::format_r(Str* s) {
183
  // Worst case: \0 becomes 4 bytes as '\\x00', and then two quote bytes.
184
7
  int n = len(s);
185
7
  int upper_bound = n * 4 + 2;
186
187
  // Extend the buffer
188
7
  data_ = static_cast<char*>(realloc(data_, len_ + upper_bound + 1));
189
190
7
  char quote = '\'';
191
7
  if (memchr(s->data_, '\'', n) && !memchr(s->data_, '"', n)) {
192
2
    quote = '"';
193
2
  }
194
7
  char* p = data_ + len_;  // end of valid data
195
196
  // From PyString_Repr()
197
7
  *p++ = quote;
198
61
  for (int i = 0; i < n; ++i) {
199
54
    char c = s->data_[i];
200
54
    if (c == quote || c == '\\') {
201
0
      *p++ = '\\';
202
0
      *p++ = c;
203
54
    } else if (c == '\t') {
204
1
      *p++ = '\\';
205
1
      *p++ = 't';
206
53
    } else if (c == '\n') {
207
2
      *p++ = '\\';
208
2
      *p++ = 'n';
209
51
    } else if (c == '\r') {
210
1
      *p++ = '\\';
211
1
      *p++ = 'r';
212
50
    } else if (c < ' ' || c >= 0x7f) {
213
3
      sprintf(p, "\\x%02x", c & 0xff);
214
3
      p += 4;
215
47
    } else {
216
47
      *p++ = c;
217
47
    }
218
54
  }
219
7
  *p++ = quote;
220
7
  *p = '\0';
221
222
7
  len_ = p - data_;
223
  // Shrink the buffer.  This is valid usage and GNU libc says it can actually
224
  // release.
225
7
  data_ = static_cast<char*>(realloc(data_, len_ + 1));
226
7
}
227
228
119
void CFileWriter::write(Str* s) {
229
  // note: throwing away the return value
230
119
  fwrite(s->data_, sizeof(char), len(s), f_);
231
119
}
232
233
0
void CFileWriter::flush() {
234
0
  ::fflush(f_);
235
0
}
236
237
1
bool CFileWriter::isatty() {
238
1
  return ::isatty(fileno(f_));
239
1
}
240
241
}  // namespace mylib