cpp

Coverage Report

Created: 2023-09-13 01:07

/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(Str* 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(Str* 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 Str {};
38
39
218
MutableStr* NewMutableStr(int cap) {
40
  // In order for everything to work, MutableStr must be identical in layout to
41
  // Str. One easy way to achieve this is for MutableStr to have no members and
42
  // to inherit from Str.
43
218
  static_assert(sizeof(MutableStr) == sizeof(Str),
44
218
                "Str and MutableStr must have same size");
45
218
  return reinterpret_cast<MutableStr*>(NewStr(cap));
46
218
}
47
48
10
Tuple2<Str*, Str*> split_once(Str* s, Str* 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
    Str* s1 = nullptr;
62
6
    Str* 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<Str*, Str*>(s1, s2);
71
6
  } else {
72
4
    return Tuple2<Str*, Str*>(s, nullptr);
73
4
  }
74
10
}
75
76
LineReader* gStdin;
77
78
4
LineReader* open(Str* 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
Str* 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
    // Expected EOF
103
2
    return kEmptyString;
104
2
  }
105
106
  // Note: getline() NUL terminates the buffer
107
608
  Str* result = ::StrFromC(line, len);
108
608
  free(line);
109
608
  return result;
110
610
}
111
112
2
bool CFileLineReader::isatty() {
113
2
  return ::isatty(fileno(f_));
114
2
}
115
116
// Problem: most Str methods like index() and slice() COPY so they have a
117
// NUL terminator.
118
// log("%s") falls back on sprintf, so it expects a NUL terminator.
119
// It would be easier for us to just share.
120
16
Str* BufLineReader::readline() {
121
16
  Str* line = nullptr;
122
123
16
  int str_len = len(s_);
124
16
  if (pos_ == str_len) {
125
8
    return kEmptyString;
126
8
  }
127
128
8
  int orig_pos = pos_;
129
8
  const char* p = strchr(s_->data_ + pos_, '\n');
130
  // log("pos_ = %s", pos_);
131
8
  int line_len;
132
8
  if (p) {
133
4
    int new_pos = p - s_->data_;
134
4
    line_len = new_pos - pos_ + 1;  // past newline char
135
4
    pos_ = new_pos + 1;
136
4
  } else {             // leftover line
137
4
    if (pos_ == 0) {   // The string has no newlines at all -- just return it
138
2
      pos_ = str_len;  // advance to the end
139
2
      return s_;
140
2
    } else {
141
2
      line_len = str_len - pos_;
142
2
      pos_ = str_len;  // advance to the end
143
2
    }
144
4
  }
145
146
6
  line = NewStr(line_len);
147
6
  memcpy(line->data_, s_->data_ + orig_pos, line_len);
148
6
  assert(line->data_[line_len] == '\0');
149
0
  return line;
150
8
}
151
152
Writer* gStdout;
153
Writer* gStderr;
154
155
//
156
// CFileWriter
157
//
158
159
125
void CFileWriter::write(Str* s) {
160
  // note: throwing away the return value
161
125
  fwrite(s->data_, sizeof(char), len(s), f_);
162
125
}
163
164
2
void CFileWriter::flush() {
165
2
  ::fflush(f_);
166
2
}
167
168
2
bool CFileWriter::isatty() {
169
2
  return ::isatty(::fileno(f_));
170
2
}
171
172
//
173
// BufWriter
174
//
175
176
796
char* BufWriter::data() {
177
796
  assert(str_);
178
0
  return str_->data_;
179
796
}
180
181
796
char* BufWriter::end() {
182
796
  assert(str_);
183
0
  return str_->data_ + len_;
184
796
}
185
186
2.39k
int BufWriter::capacity() {
187
2.39k
  return str_ ? len(str_) : 0;
188
2.39k
}
189
190
796
void BufWriter::Extend(Str* s) {
191
796
  const int n = len(s);
192
193
796
  assert(capacity() >= len_ + n);
194
195
0
  memcpy(end(), s->data_, n);
196
796
  len_ += n;
197
796
  data()[len_] = '\0';
198
796
}
199
200
// TODO: realloc() to new capacity instead of creating NewBuf()
201
724
void BufWriter::EnsureCapacity(int cap) {
202
724
  assert(capacity() >= len_);
203
204
724
  if (capacity() < cap) {
205
146
    auto* s = NewMutableStr(std::max(capacity() * 2, cap));
206
146
    memcpy(s->data_, str_->data_, len_);
207
146
    s->data_[len_] = '\0';
208
146
    str_ = s;
209
146
  }
210
724
}
211
212
805
void BufWriter::write(Str* s) {
213
805
  assert(is_valid_);  // Can't write() after getvalue()
214
215
0
  int n = len(s);
216
217
  // write('') is a no-op, so don't create Buf if we don't need to
218
805
  if (n == 0) {
219
9
    return;
220
9
  }
221
222
796
  if (str_ == nullptr) {
223
    // TODO: we could make the default capacity big enough for a line, e.g. 128
224
    // capacity: 128 -> 256 -> 512
225
72
    str_ = NewMutableStr(n);
226
724
  } else {
227
724
    EnsureCapacity(len_ + n);
228
724
  }
229
230
  // Append the contents to the buffer
231
796
  Extend(s);
232
796
}
233
234
66
Str* BufWriter::getvalue() {
235
66
  assert(is_valid_);  // Check for two INVALID getvalue() in a row
236
0
  is_valid_ = false;
237
238
66
  if (str_ == nullptr) {  // if no write() methods are called, the result is ""
239
3
    return kEmptyString;
240
63
  } else {
241
63
    Str* s = str_;
242
63
    s->MaybeShrink(len_);
243
63
    str_ = nullptr;
244
63
    len_ = -1;
245
63
    return s;
246
63
  }
247
66
}
248
249
}  // namespace mylib