cpp

Coverage Report

Created: 2023-01-21 22:37

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