OILS / mycpp / gc_mylib.h View on Github | oilshell.org

381 lines, 196 significant
1// gc_mylib.h - corresponds to mycpp/mylib.py
2
3#ifndef MYCPP_GC_MYLIB_H
4#define MYCPP_GC_MYLIB_H
5
6#include <limits.h> // CHAR_BIT
7
8#include "mycpp/gc_alloc.h" // gHeap
9#include "mycpp/gc_dict.h" // for dict_erase()
10#include "mycpp/gc_mops.h"
11#include "mycpp/gc_tuple.h"
12
13template <class K, class V>
14class Dict;
15
16// https://stackoverflow.com/questions/3919995/determining-sprintf-buffer-size-whats-the-standard/11092994#11092994
17// Notes:
18// - Python 2.7's intobject.c has an erroneous +6
19// - This is 13, but len('-2147483648') is 11, which means we only need 12?
20// - This formula is valid for octal(), because 2^(3 bits) = 8
21
22const int kIntBufSize = CHAR_BIT * sizeof(int) / 3 + 3;
23
24namespace mylib {
25
26void InitCppOnly();
27
28// Wrappers around our C++ APIs
29
30inline void MaybeCollect() {
31 gHeap.MaybeCollect();
32}
33
34void print_stderr(BigStr* s);
35
36inline int ByteAt(BigStr* s, int i) {
37 DCHECK(0 <= i);
38 DCHECK(i <= len(s));
39
40 return static_cast<unsigned char>(s->data_[i]);
41}
42
43inline int ByteEquals(int byte, BigStr* ch) {
44 DCHECK(0 <= byte);
45 DCHECK(byte < 256);
46
47 DCHECK(len(ch) == 1);
48
49 return byte == static_cast<unsigned char>(ch->data_[0]);
50}
51
52inline int ByteInSet(int byte, BigStr* byte_set) {
53 DCHECK(0 <= byte);
54 DCHECK(byte < 256);
55
56 int n = len(byte_set);
57 for (int i = 0; i < n; ++i) {
58 int b = static_cast<unsigned char>(byte_set->data_[i]);
59 if (byte == b) {
60 return true;
61 }
62 }
63 return false;
64}
65
66BigStr* JoinBytes(List<int>* byte_list);
67
68void BigIntSort(List<mops::BigInt>* keys);
69
70// const int kStdout = 1;
71// const int kStderr = 2;
72
73// void writeln(BigStr* s, int fd = kStdout);
74
75Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim);
76
77template <typename K, typename V>
78void dict_erase(Dict<K, V>* haystack, K needle) {
79 DCHECK(haystack->obj_header().heap_tag != HeapTag::Global);
80
81 int pos = haystack->hash_and_probe(needle);
82 if (pos == kTooSmall) {
83 return;
84 }
85 DCHECK(pos >= 0);
86 int kv_index = haystack->index_->items_[pos];
87 if (kv_index < 0) {
88 return;
89 }
90
91 int last_kv_index = haystack->len_ - 1;
92 DCHECK(kv_index <= last_kv_index);
93
94 // Swap the target entry with the most recently inserted one before removing
95 // it. This has two benefits.
96 // (1) It keeps the entry arrays compact. All valid entries occupy a
97 // contiguous region in memory.
98 // (2) It prevents holes in the entry arrays. This makes iterating over
99 // entries (e.g. in keys() or DictIter()) trivial and doesn't require
100 // any extra validity state (like a bitset of unusable slots). This is
101 // important because keys and values wont't always be pointers, so we
102 // can't rely on NULL checks for validity. We also can't wrap the slab
103 // entry types in some other type without modifying the garbage
104 // collector to trace through unmanaged types (or paying the extra
105 // allocations for the outer type).
106 if (kv_index != last_kv_index) {
107 K last_key = haystack->keys_->items_[last_kv_index];
108 V last_val = haystack->values_->items_[last_kv_index];
109 int last_pos = haystack->hash_and_probe(last_key);
110 DCHECK(last_pos != kNotFound);
111 haystack->keys_->items_[kv_index] = last_key;
112 haystack->values_->items_[kv_index] = last_val;
113 haystack->index_->items_[last_pos] = kv_index;
114 }
115
116 // Zero out for GC. These could be nullptr or 0
117 haystack->keys_->items_[last_kv_index] = 0;
118 haystack->values_->items_[last_kv_index] = 0;
119 haystack->index_->items_[pos] = kDeletedEntry;
120 haystack->len_--;
121 DCHECK(haystack->len_ < haystack->capacity_);
122}
123
124inline BigStr* hex_lower(int i) {
125 // Note: Could also use OverAllocatedStr, but most strings are small?
126 char buf[kIntBufSize];
127 int len = snprintf(buf, kIntBufSize, "%x", i);
128 return ::StrFromC(buf, len);
129}
130
131// Abstract type: Union of LineReader and Writer
132class File {
133 public:
134 File() {
135 }
136 // Writer
137 virtual void write(BigStr* s) = 0;
138 virtual void flush() = 0;
139
140 // Reader
141 virtual BigStr* readline() = 0;
142
143 // Both
144 virtual bool isatty() = 0;
145 virtual void close() = 0;
146
147 static constexpr ObjHeader obj_header() {
148 return ObjHeader::ClassFixed(field_mask(), sizeof(File));
149 }
150
151 static constexpr uint32_t field_mask() {
152 return kZeroMask;
153 }
154};
155
156// Wrap a FILE* for read and write
157class CFile : public File {
158 public:
159 explicit CFile(FILE* f) : File(), f_(f) {
160 }
161 // Writer
162 void write(BigStr* s) override;
163 void flush() override;
164
165 // Reader
166 BigStr* readline() override;
167
168 // Both
169 bool isatty() override;
170 void close() override;
171
172 static constexpr ObjHeader obj_header() {
173 return ObjHeader::ClassFixed(field_mask(), sizeof(CFile));
174 }
175
176 static constexpr uint32_t field_mask() {
177 // not mutating field_mask because FILE* isn't a GC object
178 return File::field_mask();
179 }
180
181 private:
182 FILE* f_;
183
184 DISALLOW_COPY_AND_ASSIGN(CFile)
185};
186
187// Abstract File we can only read from.
188// TODO: can we get rid of DCHECK() and reinterpret_cast?
189class LineReader : public File {
190 public:
191 LineReader() : File() {
192 }
193 void write(BigStr* s) override {
194 CHECK(false); // should not happen
195 }
196 void flush() override {
197 CHECK(false); // should not happen
198 }
199
200 static constexpr ObjHeader obj_header() {
201 return ObjHeader::ClassFixed(field_mask(), sizeof(LineReader));
202 }
203
204 static constexpr uint32_t field_mask() {
205 return kZeroMask;
206 }
207};
208
209class BufLineReader : public LineReader {
210 public:
211 explicit BufLineReader(BigStr* s) : LineReader(), s_(s), pos_(0) {
212 }
213 virtual BigStr* readline();
214 virtual bool isatty() {
215 return false;
216 }
217 virtual void close() {
218 }
219
220 BigStr* s_;
221 int pos_;
222
223 static constexpr ObjHeader obj_header() {
224 return ObjHeader::ClassFixed(field_mask(), sizeof(LineReader));
225 }
226
227 static constexpr uint32_t field_mask() {
228 return LineReader::field_mask() | maskbit(offsetof(BufLineReader, s_));
229 }
230
231 DISALLOW_COPY_AND_ASSIGN(BufLineReader)
232};
233
234extern LineReader* gStdin;
235
236inline LineReader* Stdin() {
237 if (gStdin == nullptr) {
238 gStdin = reinterpret_cast<LineReader*>(Alloc<CFile>(stdin));
239 }
240 return gStdin;
241}
242
243LineReader* open(BigStr* path);
244
245// Abstract File we can only write to.
246// TODO: can we get rid of DCHECK() and reinterpret_cast?
247class Writer : public File {
248 public:
249 Writer() : File() {
250 }
251 BigStr* readline() override {
252 CHECK(false); // should not happen
253 }
254
255 static constexpr ObjHeader obj_header() {
256 return ObjHeader::ClassFixed(field_mask(), sizeof(Writer));
257 }
258
259 static constexpr uint32_t field_mask() {
260 return kZeroMask;
261 }
262};
263
264class MutableStr;
265
266class BufWriter : public Writer {
267 public:
268 BufWriter() : Writer(), str_(nullptr), len_(0) {
269 }
270 void write(BigStr* s) override;
271 void write_spaces(int n);
272 void clear() { // Reuse this instance
273 str_ = nullptr;
274 len_ = 0;
275 is_valid_ = true;
276 }
277 void close() override {
278 }
279 void flush() override {
280 }
281 bool isatty() override {
282 return false;
283 }
284 BigStr* getvalue(); // part of cStringIO API
285
286 //
287 // Low Level API for C++ usage only
288 //
289
290 // Convenient API that avoids BigStr*
291 void WriteConst(const char* c_string);
292
293 // Potentially resizes the buffer.
294 void EnsureMoreSpace(int n);
295 // After EnsureMoreSpace(42), you can write 42 more bytes safely.
296 //
297 // Note that if you call EnsureMoreSpace(42), write 5 byte, and then
298 // EnsureMoreSpace(42) again, the amount of additional space reserved is 47.
299
300 // (Similar to vector::reserve(n), but it takes an integer to ADD to the
301 // capacity.)
302
303 uint8_t* LengthPointer(); // start + length
304 uint8_t* CapacityPointer(); // start + capacity
305 void SetLengthFrom(uint8_t* length_ptr);
306
307 int Length() {
308 return len_;
309 }
310
311 // Rewind to earlier position, future writes start there
312 void Truncate(int length);
313
314 static constexpr ObjHeader obj_header() {
315 return ObjHeader::ClassFixed(field_mask(), sizeof(BufWriter));
316 }
317
318 static constexpr unsigned field_mask() {
319 // maskvit_v() because BufWriter has virtual methods
320 return Writer::field_mask() | maskbit(offsetof(BufWriter, str_));
321 }
322
323 private:
324 void WriteRaw(char* s, int n);
325
326 MutableStr* str_; // getvalue() turns this directly into Str*, no copying
327 int len_; // how many bytes have been written so far
328 bool is_valid_ = true; // It becomes invalid after getvalue() is called
329};
330
331extern Writer* gStdout;
332
333inline Writer* Stdout() {
334 if (gStdout == nullptr) {
335 gStdout = reinterpret_cast<Writer*>(Alloc<CFile>(stdout));
336 gHeap.RootGlobalVar(gStdout);
337 }
338 return gStdout;
339}
340
341extern Writer* gStderr;
342
343inline Writer* Stderr() {
344 if (gStderr == nullptr) {
345 gStderr = reinterpret_cast<Writer*>(Alloc<CFile>(stderr));
346 gHeap.RootGlobalVar(gStderr);
347 }
348 return gStderr;
349}
350
351class UniqueObjects {
352 // Can't be expressed in typed Python because we don't have uint64_t for
353 // addresses
354
355 public:
356 UniqueObjects() {
357 }
358 void Add(void* obj) {
359 }
360 int Get(void* obj) {
361 return -1;
362 }
363
364 static constexpr ObjHeader obj_header() {
365 return ObjHeader::ClassFixed(field_mask(), sizeof(UniqueObjects));
366 }
367
368 // SPECIAL CASE? We should never have a unique reference to an object? So
369 // don't bother tracing
370 static constexpr uint32_t field_mask() {
371 return kZeroMask;
372 }
373
374 private:
375 // address -> small integer ID
376 Dict<void*, int> addresses_;
377};
378
379} // namespace mylib
380
381#endif // MYCPP_GC_MYLIB_H