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         return ct;
117     }
118 
119 private:
120     ScriptAny[] _constants;
121     size_t[ScriptAny] _lookup;
122     bool _isSealed = false;
123 }