1 /** This module implements the ConstTable class 2 3 ──────────────────────────────────────────────────────────────────────────────── 4 5 Copyright (C) 2021 pillager86.rf.gd 6 7 This program is free software: you can redistribute it and/or modify it under 8 the terms of the GNU General Public License as published by the Free Software 9 Foundation, either version 3 of the License, or (at your option) any later 10 version. 11 12 This program is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 14 PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License along with 17 this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 module mildew.vm.consttable; 20 21 import mildew.types.any; 22 import mildew.util.encode; 23 24 /** 25 * This is a wrapper around a dynamic array. When a value is added, ConstTable determines if the value is 26 * already in the entries or adds a new entry and returns the index. A ConstTable is shared among all 27 * Chunks compiled under the same Compiler.compile call. 28 */ 29 class ConstTable 30 { 31 public: 32 33 // TODO option to finalize and destroy hash table when no more consts are to be added 34 35 /// add a possibly new value to table and return its index. 36 size_t addValue(ScriptAny value) 37 { 38 if(_isSealed) 39 throw new Exception("Attempt to add to sealed const table"); 40 // already in table? 41 if(value in _lookup) 42 { 43 return _lookup[value]; 44 } 45 // have to add new one 46 auto location = _constants.length; 47 _lookup[value] = location; 48 _constants ~= value; 49 return location; 50 } 51 52 /// same as addValue but returns an uint for easy encoding 53 uint addValueUint(ScriptAny value) 54 { 55 return cast(uint)addValue(value); 56 } 57 58 /// get a specific constant 59 ScriptAny get(size_t index) const 60 { 61 import std.format: format; 62 if(index >= _constants.length) 63 throw new Exception(format("index %s is greater than %s", index, _constants.length)); 64 return cast(immutable)_constants[index]; 65 } 66 67 /// foreach over const table 68 int opApply(scope int delegate(size_t index, ScriptAny value) dg) 69 { 70 int result = 0; 71 foreach (index, item; _constants) 72 { 73 result = dg(index, item); 74 if (result) 75 break; 76 } 77 return result; 78 } 79 80 /** 81 * Seal the const table so that no more constants can be added. 82 */ 83 void seal() 84 { 85 _isSealed = true; 86 destroy(_lookup); 87 _lookup = null; 88 } 89 90 /// convert const table to ubytes 91 ubyte[] serialize() 92 { 93 ubyte[] data = encode!size_t(_constants.length); 94 for(auto i = 0; i < _constants.length; ++i) 95 { 96 data ~= _constants[i].serialize(); 97 } 98 return data; 99 } 100 101 /// reads a ConstTable from an ubyte stream 102 static ConstTable deserialize(ref ubyte[] stream) 103 { 104 import mildew.types.func: ScriptFunction; 105 auto ct = new ConstTable(); 106 ct._constants.length = decode!size_t(stream); 107 stream = stream[size_t.sizeof..$]; 108 for(auto i = 0; i < ct._constants.length; ++i) 109 { 110 ct._constants[i] = ScriptAny.deserialize(stream); 111 if(ct._constants[i].type == ScriptAny.Type.FUNCTION) 112 { 113 ct._constants[i].toValue!ScriptFunction().constTable = ct; 114 } 115 } 116 // build hash table in case the program calls scripts 117 foreach(index, value ; ct._constants) 118 ct._lookup[value] = index; 119 return ct; 120 } 121 122 private: 123 ScriptAny[] _constants; 124 size_t[ScriptAny] _lookup; 125 bool _isSealed = false; 126 }