| 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 | 
 | 
| 13 | template <class K, class V>
 | 
| 14 | class 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 | 
 | 
| 22 | const int kIntBufSize = CHAR_BIT * sizeof(int) / 3 + 3;
 | 
| 23 | 
 | 
| 24 | namespace mylib {
 | 
| 25 | 
 | 
| 26 | void InitCppOnly();
 | 
| 27 | 
 | 
| 28 | // Wrappers around our C++ APIs
 | 
| 29 | 
 | 
| 30 | inline void MaybeCollect() {
 | 
| 31 |   gHeap.MaybeCollect();
 | 
| 32 | }
 | 
| 33 | 
 | 
| 34 | void print_stderr(BigStr* s);
 | 
| 35 | 
 | 
| 36 | inline 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 | 
 | 
| 43 | inline 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 | 
 | 
| 52 | inline 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 | 
 | 
| 66 | BigStr* JoinBytes(List<int>* byte_list);
 | 
| 67 | 
 | 
| 68 | void 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 | 
 | 
| 75 | Tuple2<BigStr*, BigStr*> split_once(BigStr* s, BigStr* delim);
 | 
| 76 | 
 | 
| 77 | template <typename K, typename V>
 | 
| 78 | void 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 | 
 | 
| 124 | inline 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
 | 
| 132 | class 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
 | 
| 157 | class 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?
 | 
| 189 | class 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 | 
 | 
| 209 | class 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 | 
 | 
| 234 | extern LineReader* gStdin;
 | 
| 235 | 
 | 
| 236 | inline LineReader* Stdin() {
 | 
| 237 |   if (gStdin == nullptr) {
 | 
| 238 |     gStdin = reinterpret_cast<LineReader*>(Alloc<CFile>(stdin));
 | 
| 239 |   }
 | 
| 240 |   return gStdin;
 | 
| 241 | }
 | 
| 242 | 
 | 
| 243 | LineReader* open(BigStr* path);
 | 
| 244 | 
 | 
| 245 | // Abstract File we can only write to.
 | 
| 246 | // TODO: can we get rid of DCHECK() and reinterpret_cast?
 | 
| 247 | class 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 | 
 | 
| 264 | class MutableStr;
 | 
| 265 | 
 | 
| 266 | class 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 | 
 | 
| 331 | extern Writer* gStdout;
 | 
| 332 | 
 | 
| 333 | inline Writer* Stdout() {
 | 
| 334 |   if (gStdout == nullptr) {
 | 
| 335 |     gStdout = reinterpret_cast<Writer*>(Alloc<CFile>(stdout));
 | 
| 336 |     gHeap.RootGlobalVar(gStdout);
 | 
| 337 |   }
 | 
| 338 |   return gStdout;
 | 
| 339 | }
 | 
| 340 | 
 | 
| 341 | extern Writer* gStderr;
 | 
| 342 | 
 | 
| 343 | inline Writer* Stderr() {
 | 
| 344 |   if (gStderr == nullptr) {
 | 
| 345 |     gStderr = reinterpret_cast<Writer*>(Alloc<CFile>(stderr));
 | 
| 346 |     gHeap.RootGlobalVar(gStderr);
 | 
| 347 |   }
 | 
| 348 |   return gStderr;
 | 
| 349 | }
 | 
| 350 | 
 | 
| 351 | class 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
 |