cpp

Coverage Report

Created: 2022-07-20 01:16

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