1 #ifndef BOTC_DATABUFFER_H |
|
2 #define BOTC_DATABUFFER_H |
|
3 #include <stdio.h> |
|
4 #include <string.h> |
|
5 #include "main.h" |
|
6 #include "stringtable.h" |
|
7 |
|
8 #define MAX_MARKS 512 |
|
9 |
|
10 extern int g_NextMark; |
|
11 |
|
12 // ============================================================================ |
|
13 // DataBuffer: A dynamic data buffer. |
|
14 class DataBuffer { |
|
15 public: |
|
16 // The actual buffer |
|
17 byte* buffer; |
|
18 |
|
19 // Allocated size of the buffer |
|
20 unsigned int allocsize; |
|
21 |
|
22 // Written size of the buffer |
|
23 unsigned int writesize; |
|
24 |
|
25 // Marks and references |
|
26 ScriptMark* marks[MAX_MARKS]; |
|
27 ScriptMarkReference* refs[MAX_MARKS]; |
|
28 |
|
29 // ==================================================================== |
|
30 // METHODS |
|
31 |
|
32 // ==================================================================== |
|
33 // Constructor |
|
34 DataBuffer (unsigned int size=128) { |
|
35 writesize = 0; |
|
36 |
|
37 buffer = new unsigned char[size]; |
|
38 allocsize = size; |
|
39 |
|
40 // Clear the marks table out |
|
41 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
42 marks[u] = null; |
|
43 refs[u] = null; |
|
44 } |
|
45 } |
|
46 |
|
47 // ==================================================================== |
|
48 ~DataBuffer () { |
|
49 delete buffer; |
|
50 |
|
51 // Delete any marks and references |
|
52 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
53 if (marks[u]) |
|
54 delete marks[u]; |
|
55 |
|
56 if (refs[u]) |
|
57 delete refs[u]; |
|
58 } |
|
59 } |
|
60 |
|
61 // ==================================================================== |
|
62 // Write stuff to the buffer |
|
63 template<class T> void Write (T stuff) { |
|
64 if (writesize + sizeof (T) >= allocsize) { |
|
65 // We don't have enough space in the buffer to write |
|
66 // the stuff - thus resize. First, store the old |
|
67 // buffer temporarily: |
|
68 char* copy = new char[allocsize]; |
|
69 memcpy (copy, buffer, allocsize); |
|
70 |
|
71 // Remake the buffer with the new size. Have enough space |
|
72 // for the stuff we're going to write, as well as a bit |
|
73 // of leeway so we don't have to resize immediately again. |
|
74 size_t newsize = allocsize + sizeof (T) + 128; |
|
75 |
|
76 delete buffer; |
|
77 buffer = new unsigned char[newsize]; |
|
78 allocsize = newsize; |
|
79 |
|
80 // Now, copy the stuff back. |
|
81 memcpy (buffer, copy, allocsize); |
|
82 delete copy; |
|
83 } |
|
84 |
|
85 // Buffer is now guaranteed to have enough space. |
|
86 // Write the stuff one byte at a time. |
|
87 union_t<T> uni; |
|
88 uni.val = stuff; |
|
89 for (unsigned int x = 0; x < sizeof (T); x++) { |
|
90 if (writesize >= allocsize) // should NEVER happen because resizing is done above |
|
91 error ("DataBuffer: written size exceeds allocated size!\n"); |
|
92 |
|
93 buffer[writesize] = uni.b[x]; |
|
94 writesize++; |
|
95 } |
|
96 } |
|
97 |
|
98 // ==================================================================== |
|
99 // Merge another data buffer into this one. |
|
100 void Merge (DataBuffer* other) { |
|
101 if (!other) |
|
102 return; |
|
103 int oldsize = writesize; |
|
104 |
|
105 for (unsigned int x = 0; x < other->writesize; x++) |
|
106 Write (*(other->buffer+x)); |
|
107 |
|
108 // Merge its marks and references |
|
109 unsigned int u = 0; |
|
110 for (u = 0; u < MAX_MARKS; u++) { |
|
111 if (other->marks[u]) { |
|
112 // Merge the mark and offset its position. |
|
113 if (marks[u]) |
|
114 error ("DataBuffer: duplicate mark %d!\n"); |
|
115 |
|
116 marks[u] = other->marks[u]; |
|
117 marks[u]->pos += oldsize; |
|
118 |
|
119 // The original mark becomes null so that the deconstructor |
|
120 // will not delete it prematurely. (should it delete any |
|
121 // marks in the first place since there is no such thing |
|
122 // as at temporary mark?) |
|
123 other->marks[u] = null; |
|
124 } |
|
125 |
|
126 if (other->refs[u]) { |
|
127 // Same for references |
|
128 // TODO: add a g_NextRef system like here, akin to marks! |
|
129 unsigned int r = AddMarkReference (other->refs[u]->num, false); |
|
130 refs[r]->pos = other->refs[u]->pos + oldsize; |
|
131 } |
|
132 } |
|
133 |
|
134 delete other; |
|
135 } |
|
136 |
|
137 // Clones this databuffer to a new one and returns it. |
|
138 DataBuffer* Clone () { |
|
139 DataBuffer* other = new DataBuffer; |
|
140 for (unsigned int x = 0; x < writesize; x++) |
|
141 other->Write (*(buffer+x)); |
|
142 return other; |
|
143 } |
|
144 |
|
145 // ==================================================================== |
|
146 // Adds a mark to the buffer. A mark is a "pointer" to a particular |
|
147 // position in the bytecode. The actual permanent position cannot |
|
148 // be predicted in any way or form, thus these things will be used |
|
149 // to "mark" a position like that for future use. |
|
150 unsigned int AddMark (string name) { |
|
151 // Find a free slot for the mark |
|
152 unsigned int u = g_NextMark++; |
|
153 |
|
154 if (marks[u]) |
|
155 error ("DataBuffer: attempted to re-create mark %u!\n", u); |
|
156 |
|
157 if (u >= MAX_MARKS) |
|
158 error ("mark quota exceeded, all labels, if-structs and loops add marks\n"); |
|
159 |
|
160 ScriptMark* m = new ScriptMark; |
|
161 m->name = name; |
|
162 m->pos = writesize; |
|
163 marks[u] = m; |
|
164 return u; |
|
165 } |
|
166 |
|
167 // ==================================================================== |
|
168 // A ref is another "mark" that references a mark. When the bytecode |
|
169 // is written to file, they are changed into their marks' current |
|
170 // positions. Marks themselves are never written to files, only refs are |
|
171 unsigned int AddMarkReference (unsigned int marknum, bool placeholder = true) { |
|
172 unsigned int u; |
|
173 for (u = 0; u < MAX_MARKS; u++) |
|
174 if (!refs[u]) |
|
175 break; |
|
176 |
|
177 if (u == MAX_MARKS) |
|
178 error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n"); |
|
179 |
|
180 ScriptMarkReference* r = new ScriptMarkReference; |
|
181 r->num = marknum; |
|
182 r->pos = writesize; |
|
183 refs[u] = r; |
|
184 |
|
185 // Write a dummy placeholder for the reference |
|
186 if (placeholder) |
|
187 Write (1234); |
|
188 |
|
189 return u; |
|
190 } |
|
191 |
|
192 // Delete a mark and all references to it. |
|
193 void DeleteMark (unsigned int marknum) { |
|
194 if (!marks[marknum]) |
|
195 return; |
|
196 |
|
197 // Delete the mark |
|
198 delete marks[marknum]; |
|
199 marks[marknum] = null; |
|
200 |
|
201 // Delete its references |
|
202 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
203 if (refs[u]->num == marknum) { |
|
204 delete refs[u]; |
|
205 refs[u] = null; |
|
206 } |
|
207 } |
|
208 } |
|
209 |
|
210 // Adjusts a mark to the current position |
|
211 void MoveMark (unsigned int mark) { |
|
212 if (!marks[mark]) |
|
213 return; |
|
214 |
|
215 marks[mark]->pos = writesize; |
|
216 } |
|
217 |
|
218 void OffsetMark (unsigned int mark, int offset) { |
|
219 if (!marks[mark]) |
|
220 return; |
|
221 |
|
222 marks[mark]->pos += offset; |
|
223 } |
|
224 |
|
225 // Dump the buffer (for debugging purposes) |
|
226 void Dump() { |
|
227 for (unsigned int x = 0; x < writesize; x++) |
|
228 printf ("%d. [%d]\n", x, *(buffer+x)); |
|
229 } |
|
230 |
|
231 // Count the amount of marks |
|
232 unsigned int CountMarks () { |
|
233 unsigned int count = 0; |
|
234 for (unsigned int u = 0; u < MAX_MARKS; u++) |
|
235 count += !!marks[u]; |
|
236 return count; |
|
237 } |
|
238 |
|
239 // Count the amount of refs |
|
240 unsigned int CountReferences () { |
|
241 unsigned int count = 0; |
|
242 for (unsigned int u = 0; u < MAX_MARKS; u++) |
|
243 count += !!refs[u]; |
|
244 return count; |
|
245 } |
|
246 |
|
247 // Write a float into the buffer |
|
248 void WriteFloat (string floatstring) { |
|
249 // TODO: Casting float to word causes the decimal to be lost. |
|
250 // Find a way to store the number without such loss. |
|
251 float val = atof (floatstring); |
|
252 Write (DH_PUSHNUMBER); |
|
253 Write (static_cast<word> ((val > 0) ? val : -val)); |
|
254 if (val < 0) |
|
255 Write (DH_UNARYMINUS); |
|
256 } |
|
257 |
|
258 void WriteString (string a) { |
|
259 Write (DH_PUSHSTRINGINDEX); |
|
260 Write (get_string_table_index (a)); |
|
261 } |
|
262 }; |
|
263 |
|
264 #endif // BOTC_DATABUFFER_H |
|