cpp

Coverage Report

Created: 2023-11-29 23:45

/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
325
void print_stderr(BigStr* s) {
19
325
  fputs(s->data_, stderr);  // prints until first NUL
20
325
  fputc('\n', stderr);
21
325
}
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
class MutableStr : public BigStr {};
38
39
218
MutableStr* NewMutableStr(int cap) {
40
  // In order for everything to work, MutableStr must be identical in layout to
41
  // BigStr. One easy way to achieve this is for MutableStr to have no members
42
  // and to inherit from BigStr.
43
218
  static_assert(sizeof(MutableStr) == sizeof(BigStr),
44
218
                "BigStr and MutableStr must have same size");
45
218
  return reinterpret_cast<MutableStr*>(NewStr(cap));
46
218
}
47
48
10
Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim) {
49
10
  assert(len(delim) == 1);
50
51
0
  const char* start = s->data_;  // note: this pointer may move
52
10
  char c = delim->data_[0];
53
10
  int length = len(s);
54
55
10
  const char* p = static_cast<const char*>(memchr(start, c, length));
56
57
10
  if (p) {
58
6
    int len1 = p - start;
59
6
    int len2 = length - len1 - 1;  // -1 for delim
60
61
6
    BigStr* s1 = nullptr;
62
6
    BigStr* s2 = nullptr;
63
    // Allocate together to avoid 's' moving in between
64
6
    s1 = NewStr(len1);
65
6
    s2 = NewStr(len2);
66
67
6
    memcpy(s1->data_, s->data_, len1);
68
6
    memcpy(s2->data_, s->data_ + len1 + 1, len2);
69
70
6
    return Tuple2<BigStr*, BigStr*>(s1, s2);
71
6
  } else {
72
4
    return Tuple2<BigStr*, BigStr*>(s, nullptr);
73
4
  }
74
10
}
75
76
LineReader* gStdin;
77
78
4
LineReader* open(BigStr* path) {
79
  // TODO: Don't use C I/O; use POSIX I/O!
80
4
  FILE* f = fopen(path->data_, "r");
81
4
  if (f == nullptr) {
82
0
    throw Alloc<IOError>(errno);
83
0
  }
84
85
4
  return Alloc<CFileLineReader>(f);
86
4
}
87
88
610
BigStr* CFileLineReader::readline() {
89
610
  char* line = nullptr;
90
610
  size_t allocated_size = 0;  // unused
91
92
  // Reset errno because we turn the EOF error into empty string (like Python).
93
610
  errno = 0;
94
610
  ssize_t len = getline(&line, &allocated_size, f_);
95
610
  if (len < 0) {
96
    // man page says the buffer should be freed even if getline fails
97
2
    free(line);
98
2
    if (errno != 0) {  // Unexpected error
99
0
      log("getline() error: %s", strerror(errno));
100
0
      throw Alloc<IOError>(errno);
101
0
    }
102
2
    return kEmptyString;  // EOF indicated by by empty string, like Python
103
2
  }
104
105
  // Note: getline() NUL-terminates the buffer
106
608
  BigStr* result = ::StrFromC(line, len);
107
608
  free(line);
108
608
  return result;
109
610
}
110
111
2
bool CFileLineReader::isatty() {
112
2
  return ::isatty(fileno(f_));
113
2
}
114
115
// Problem: most BigStr methods like index() and slice() COPY so they have a
116
// NUL terminator.
117
// log("%s") falls back on sprintf, so it expects a NUL terminator.
118
// It would be easier for us to just share.
119
16
BigStr* BufLineReader::readline() {
120
16
  BigStr* line = nullptr;
121
122
16
  int str_len = len(s_);
123
16
  if (pos_ == str_len) {
124
8
    return kEmptyString;
125
8
  }
126
127
8
  int orig_pos = pos_;
128
8
  const char* p = strchr(s_->data_ + pos_, '\n');
129
  // log("pos_ = %s", pos_);
130
8
  int line_len;
131
8
  if (p) {
132
4
    int new_pos = p - s_->data_;
133
4
    line_len = new_pos - pos_ + 1;  // past newline char
134
4
    pos_ = new_pos + 1;
135
4
  } else {             // leftover line
136
4
    if (pos_ == 0) {   // The string has no newlines at all -- just return it
137
2
      pos_ = str_len;  // advance to the end
138
2
      return s_;
139
2
    } else {
140
2
      line_len = str_len - pos_;
141
2
      pos_ = str_len;  // advance to the end
142
2
    }
143
4
  }
144
145
6
  line = NewStr(line_len);
146
6
  memcpy(line->data_, s_->data_ + orig_pos, line_len);
147
6
  assert(line->data_[line_len] == '\0');
148
0
  return line;
149
8
}
150
151
Writer* gStdout;
152
Writer* gStderr;
153
154
//
155
// CFileWriter
156
//
157
158
125
void CFileWriter::write(BigStr* s) {
159
  // note: throwing away the return value
160
125
  fwrite(s->data_, sizeof(char), len(s), f_);
161
125
}
162
163
2
void CFileWriter::flush() {
164
2
  ::fflush(f_);
165
2
}
166
167
2
bool CFileWriter::isatty() {
168
2
  return ::isatty(::fileno(f_));
169
2
}
170
171
//
172
// BufWriter
173
//
174
175
796
char* BufWriter::data() {
176
796
  assert(str_);
177
0
  return str_->data_;
178
796
}
179
180
796
char* BufWriter::end() {
181
796
  assert(str_);
182
0
  return str_->data_ + len_;
183
796
}
184
185
2.39k
int BufWriter::capacity() {
186
2.39k
  return str_ ? len(str_) : 0;
187
2.39k
}
188
189
796
void BufWriter::Extend(BigStr* s) {
190
796
  const int n = len(s);
191
192
796
  assert(capacity() >= len_ + n);
193
194
0
  memcpy(end(), s->data_, n);
195
796
  len_ += n;
196
796
  data()[len_] = '\0';
197
796
}
198
199
// TODO: realloc() to new capacity instead of creating NewBuf()
200
724
void BufWriter::EnsureCapacity(int cap) {
201
724
  assert(capacity() >= len_);
202
203
724
  if (capacity() < cap) {
204
146
    auto* s = NewMutableStr(std::max(capacity() * 2, cap));
205
146
    memcpy(s->data_, str_->data_, len_);
206
146
    s->data_[len_] = '\0';
207
146
    str_ = s;
208
146
  }
209
724
}
210
211
805
void BufWriter::write(BigStr* s) {
212
805
  assert(is_valid_);  // Can't write() after getvalue()
213
214
0
  int n = len(s);
215
216
  // write('') is a no-op, so don't create Buf if we don't need to
217
805
  if (n == 0) {
218
9
    return;
219
9
  }
220
221
796
  if (str_ == nullptr) {
222
    // TODO: we could make the default capacity big enough for a line, e.g. 128
223
    // capacity: 128 -> 256 -> 512
224
72
    str_ = NewMutableStr(n);
225
724
  } else {
226
724
    EnsureCapacity(len_ + n);
227
724
  }
228
229
  // Append the contents to the buffer
230
796
  Extend(s);
231
796
}
232
233
66
BigStr* BufWriter::getvalue() {
234
66
  assert(is_valid_);  // Check for two INVALID getvalue() in a row
235
0
  is_valid_ = false;
236
237
66
  if (str_ == nullptr) {  // if no write() methods are called, the result is ""
238
3
    return kEmptyString;
239
63
  } else {
240
63
    BigStr* s = str_;
241
63
    s->MaybeShrink(len_);
242
63
    str_ = nullptr;
243
63
    len_ = -1;
244
63
    return s;
245
63
  }
246
66
}
247
248
}  // namespace mylib