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

405 lines, 215 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 <sys/stat.h>
7#include <unistd.h>
8
9#include "mycpp/gc_alloc.h" // gHeap
10#include "mycpp/gc_dict.h" // for dict_erase()
11#include "mycpp/gc_mops.h"
12#include "mycpp/gc_tuple.h"
13
14template <class K, class V>
15class Dict;
16
17namespace mylib {
18
19bool isinf_(double f);
20bool isnan_(double f);
21
22void InitCppOnly();
23
24// Wrappers around our C++ APIs
25
26inline void MaybeCollect() {
27 gHeap.MaybeCollect();
28}
29
30inline void PrintGcStats() {
31 gHeap.PrintShortStats(); // print to stderr
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#define BUF_SIZE 131072
157
158// Wrap a FILE* for read and write
159class CFile : public File {
160 public:
161 explicit CFile(int fd) : File(), fd_(fd) {
162 }
163 // Writer
164 void write(BigStr* s) override;
165 void flush() override;
166
167 // Reader
168 BigStr* readline() override;
169
170 // Both
171 bool isatty() override;
172 void close() override;
173
174 static constexpr ObjHeader obj_header() {
175 return ObjHeader::ClassFixed(field_mask(), sizeof(CFile));
176 }
177
178 static constexpr uint32_t field_mask() {
179 // not mutating field_mask because FILE* isn't a GC object
180 return File::field_mask();
181 }
182
183 private:
184 int fd_;
185 char buffer_[BUF_SIZE];
186 size_t pos_ = 0;
187 size_t end_ = 0;
188 bool eof_ = false;
189
190 DISALLOW_COPY_AND_ASSIGN(CFile)
191};
192
193// Abstract File we can only read from.
194// TODO: can we get rid of DCHECK() and reinterpret_cast?
195class LineReader : public File {
196 public:
197 LineReader() : File() {
198 }
199 void write(BigStr* s) override {
200 CHECK(false); // should not happen
201 }
202 void flush() override {
203 CHECK(false); // should not happen
204 }
205
206 static constexpr ObjHeader obj_header() {
207 return ObjHeader::ClassFixed(field_mask(), sizeof(LineReader));
208 }
209
210 static constexpr uint32_t field_mask() {
211 return kZeroMask;
212 }
213};
214
215class BufLineReader : public LineReader {
216 public:
217 explicit BufLineReader(BigStr* s) : LineReader(), s_(s), pos_(0) {
218 }
219 virtual BigStr* readline();
220 virtual bool isatty() {
221 return false;
222 }
223 virtual void close() {
224 }
225
226 BigStr* s_;
227 int pos_;
228
229 static constexpr ObjHeader obj_header() {
230 return ObjHeader::ClassFixed(field_mask(), sizeof(LineReader));
231 }
232
233 static constexpr uint32_t field_mask() {
234 return LineReader::field_mask() | maskbit(offsetof(BufLineReader, s_));
235 }
236
237 DISALLOW_COPY_AND_ASSIGN(BufLineReader)
238};
239
240extern LineReader* gStdin;
241
242inline LineReader* Stdin() {
243 if (gStdin == nullptr) {
244 gStdin = reinterpret_cast<LineReader*>(Alloc<CFile>(STDIN_FILENO));
245 }
246 return gStdin;
247}
248
249LineReader* open(BigStr* path);
250
251// Abstract File we can only write to.
252// TODO: can we get rid of DCHECK() and reinterpret_cast?
253class Writer : public File {
254 public:
255 Writer() : File() {
256 }
257 BigStr* readline() override {
258 CHECK(false); // should not happen
259 }
260
261 static constexpr ObjHeader obj_header() {
262 return ObjHeader::ClassFixed(field_mask(), sizeof(Writer));
263 }
264
265 static constexpr uint32_t field_mask() {
266 return kZeroMask;
267 }
268};
269
270class MutableStr;
271
272class BufWriter : public Writer {
273 public:
274 BufWriter() : Writer(), str_(nullptr), len_(0) {
275 }
276 void write(BigStr* s) override;
277 void write_spaces(int n);
278 void clear() { // Reuse this instance
279 str_ = nullptr;
280 len_ = 0;
281 is_valid_ = true;
282 }
283 void close() override {
284 }
285 void flush() override {
286 }
287 bool isatty() override {
288 return false;
289 }
290 BigStr* getvalue(); // part of cStringIO API
291
292 //
293 // Low Level API for C++ usage only
294 //
295
296 // Convenient API that avoids BigStr*
297 void WriteConst(const char* c_string);
298
299 // Potentially resizes the buffer.
300 void EnsureMoreSpace(int n);
301 // After EnsureMoreSpace(42), you can write 42 more bytes safely.
302 //
303 // Note that if you call EnsureMoreSpace(42), write 5 byte, and then
304 // EnsureMoreSpace(42) again, the amount of additional space reserved is 47.
305
306 // (Similar to vector::reserve(n), but it takes an integer to ADD to the
307 // capacity.)
308
309 uint8_t* LengthPointer(); // start + length
310 uint8_t* CapacityPointer(); // start + capacity
311 void SetLengthFrom(uint8_t* length_ptr);
312
313 int Length() {
314 return len_;
315 }
316
317 // Rewind to earlier position, future writes start there
318 void Truncate(int length);
319
320 static constexpr ObjHeader obj_header() {
321 return ObjHeader::ClassFixed(field_mask(), sizeof(BufWriter));
322 }
323
324 static constexpr unsigned field_mask() {
325 // maskvit_v() because BufWriter has virtual methods
326 return Writer::field_mask() | maskbit(offsetof(BufWriter, str_));
327 }
328
329 private:
330 void WriteRaw(char* s, int n);
331
332 MutableStr* str_; // getvalue() turns this directly into Str*, no copying
333 int len_; // how many bytes have been written so far
334 bool is_valid_ = true; // It becomes invalid after getvalue() is called
335};
336
337extern Writer* gStdout;
338
339inline Writer* Stdout() {
340 if (gStdout == nullptr) {
341 gStdout = reinterpret_cast<Writer*>(Alloc<CFile>(STDOUT_FILENO));
342 gHeap.RootGlobalVar(gStdout);
343 }
344 return gStdout;
345}
346
347extern Writer* gStderr;
348
349inline Writer* Stderr() {
350 if (gStderr == nullptr) {
351 gStderr = reinterpret_cast<Writer*>(Alloc<CFile>(STDERR_FILENO));
352 gHeap.RootGlobalVar(gStderr);
353 }
354 return gStderr;
355}
356
357class UniqueObjects {
358 // Can't be expressed in typed Python because we don't have uint64_t for
359 // addresses
360
361 public:
362 UniqueObjects() {
363 }
364 void Add(void* obj) {
365 }
366 int Get(void* obj) {
367 return -1;
368 }
369
370 static constexpr ObjHeader obj_header() {
371 return ObjHeader::ClassFixed(field_mask(), sizeof(UniqueObjects));
372 }
373
374 // SPECIAL CASE? We should never have a unique reference to an object? So
375 // don't bother tracing
376 static constexpr uint32_t field_mask() {
377 return kZeroMask;
378 }
379
380 private:
381 // address -> small integer ID
382 Dict<void*, int> addresses_;
383};
384
385
386class StatResult {
387public:
388 bool isreg();
389
390 static constexpr ObjHeader obj_header() {
391 return ObjHeader::ClassFixed(field_mask(), sizeof(StatResult));
392 }
393
394 static constexpr uint32_t field_mask() {
395 return kZeroMask;
396 }
397
398 struct stat stat_result_;
399};
400
401StatResult* stat(BigStr* filename);
402
403} // namespace mylib
404
405#endif // MYCPP_GC_MYLIB_H