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