OILS / mycpp / gc_mylib.h View on Github | oils.pub

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