/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 |