1 /* |
|
2 * botc source code |
|
3 * Copyright (C) 2012 Santeri `Dusk` Piippo |
|
4 * All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright notice, |
|
10 * this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright notice, |
|
12 * this list of conditions and the following disclaimer in the documentation |
|
13 * and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of the developer nor the names of its contributors may |
|
15 * be used to endorse or promote products derived from this software without |
|
16 * specific prior written permission. |
|
17 * 4. Redistributions in any form must be accompanied by information on how to |
|
18 * obtain complete source code for the software and any accompanying |
|
19 * software that uses the software. The source code must either be included |
|
20 * in the distribution or be available for no more than the cost of |
|
21 * distribution plus a nominal fee, and must be freely redistributable |
|
22 * under reasonable conditions. For an executable file, complete source |
|
23 * code means the source code for all modules it contains. It does not |
|
24 * include source code for modules or files that typically accompany the |
|
25 * major components of the operating system on which the executable file |
|
26 * runs. |
|
27 * |
|
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
38 * POSSIBILITY OF SUCH DAMAGE. |
|
39 */ |
|
40 |
|
41 #ifndef __DATABUFFER_H__ |
|
42 #define __DATABUFFER_H__ |
|
43 #include <stdio.h> |
|
44 #include <string.h> |
|
45 #include "common.h" |
|
46 #include "stringtable.h" |
|
47 |
|
48 #define MAX_MARKS 512 |
|
49 |
|
50 extern int g_NextMark; |
|
51 |
|
52 // ============================================================================ |
|
53 // DataBuffer: A dynamic data buffer. |
|
54 class DataBuffer { |
|
55 public: |
|
56 // The actual buffer |
|
57 byte* buffer; |
|
58 |
|
59 // Allocated size of the buffer |
|
60 unsigned int allocsize; |
|
61 |
|
62 // Written size of the buffer |
|
63 unsigned int writesize; |
|
64 |
|
65 // Marks and references |
|
66 ScriptMark* marks[MAX_MARKS]; |
|
67 ScriptMarkReference* refs[MAX_MARKS]; |
|
68 |
|
69 // ==================================================================== |
|
70 // METHODS |
|
71 |
|
72 // ==================================================================== |
|
73 // Constructor |
|
74 DataBuffer (unsigned int size=128) { |
|
75 writesize = 0; |
|
76 |
|
77 buffer = new unsigned char[size]; |
|
78 allocsize = size; |
|
79 |
|
80 // Clear the marks table out |
|
81 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
82 marks[u] = NULL; |
|
83 refs[u] = NULL; |
|
84 } |
|
85 } |
|
86 |
|
87 // ==================================================================== |
|
88 ~DataBuffer () { |
|
89 delete buffer; |
|
90 |
|
91 // Delete any marks and references |
|
92 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
93 if (marks[u]) |
|
94 delete marks[u]; |
|
95 |
|
96 if (refs[u]) |
|
97 delete refs[u]; |
|
98 } |
|
99 } |
|
100 |
|
101 // ==================================================================== |
|
102 // Write stuff to the buffer |
|
103 template<class T> void DoWrite (const char* func, T stuff) { |
|
104 // printf ("DoWrite: called from %s\n", func); |
|
105 if (writesize + sizeof (T) >= allocsize) { |
|
106 // We don't have enough space in the buffer to write |
|
107 // the stuff - thus resize. First, store the old |
|
108 // buffer temporarily: |
|
109 char* copy = new char[allocsize]; |
|
110 memcpy (copy, buffer, allocsize); |
|
111 |
|
112 // Remake the buffer with the new size. Have enough space |
|
113 // for the stuff we're going to write, as well as a bit |
|
114 // of leeway so we don't have to resize immediately again. |
|
115 size_t newsize = allocsize + sizeof (T) + 128; |
|
116 |
|
117 delete buffer; |
|
118 buffer = new unsigned char[newsize]; |
|
119 allocsize = newsize; |
|
120 |
|
121 // Now, copy the stuff back. |
|
122 memcpy (buffer, copy, allocsize); |
|
123 delete copy; |
|
124 } |
|
125 |
|
126 // Buffer is now guaranteed to have enough space. |
|
127 // Write the stuff one byte at a time. |
|
128 union_t<T> uni; |
|
129 uni.val = stuff; |
|
130 for (unsigned int x = 0; x < sizeof (T); x++) { |
|
131 if (writesize >= allocsize) // should NEVER happen because resizing is done above |
|
132 error ("DataBuffer: written size exceeds allocated size!\n"); |
|
133 |
|
134 buffer[writesize] = uni.b[x]; |
|
135 writesize++; |
|
136 } |
|
137 } |
|
138 |
|
139 // ==================================================================== |
|
140 // Merge another data buffer into this one. |
|
141 void Merge (DataBuffer* other) { |
|
142 if (!other) |
|
143 return; |
|
144 int oldsize = writesize; |
|
145 |
|
146 for (unsigned int x = 0; x < other->writesize; x++) |
|
147 Write (*(other->buffer+x)); |
|
148 |
|
149 // Merge its marks and references |
|
150 unsigned int u = 0; |
|
151 for (u = 0; u < MAX_MARKS; u++) { |
|
152 if (other->marks[u]) { |
|
153 // Merge the mark and offset its position. |
|
154 if (marks[u]) |
|
155 error ("DataBuffer: duplicate mark %d!\n"); |
|
156 |
|
157 marks[u] = other->marks[u]; |
|
158 marks[u]->pos += oldsize; |
|
159 |
|
160 // The original mark becomes NULL so that the deconstructor |
|
161 // will not delete it prematurely. (should it delete any |
|
162 // marks in the first place since there is no such thing |
|
163 // as at temporary mark?) |
|
164 other->marks[u] = NULL; |
|
165 } |
|
166 |
|
167 if (other->refs[u]) { |
|
168 // Same for references |
|
169 // TODO: add a g_NextRef system like here, akin to marks! |
|
170 unsigned int r = AddMarkReference (other->refs[u]->num, false); |
|
171 refs[r]->pos = other->refs[u]->pos + oldsize; |
|
172 } |
|
173 } |
|
174 |
|
175 delete other; |
|
176 } |
|
177 |
|
178 // Clones this databuffer to a new one and returns it. |
|
179 DataBuffer* Clone () { |
|
180 DataBuffer* other = new DataBuffer; |
|
181 for (unsigned int x = 0; x < writesize; x++) |
|
182 other->Write (*(buffer+x)); |
|
183 return other; |
|
184 } |
|
185 |
|
186 // ==================================================================== |
|
187 // Adds a mark to the buffer. A mark is a "pointer" to a particular |
|
188 // position in the bytecode. The actual permanent position cannot |
|
189 // be predicted in any way or form, thus these things will be used |
|
190 // to "mark" a position like that for future use. |
|
191 unsigned int AddMark (str name) { |
|
192 // Find a free slot for the mark |
|
193 unsigned int u = g_NextMark++; |
|
194 |
|
195 if (marks[u]) |
|
196 error ("DataBuffer: attempted to re-create mark %u!\n", u); |
|
197 |
|
198 if (u >= MAX_MARKS) |
|
199 error ("mark quota exceeded, all labels, if-structs and loops add marks\n"); |
|
200 |
|
201 ScriptMark* m = new ScriptMark; |
|
202 m->name = name; |
|
203 m->pos = writesize; |
|
204 marks[u] = m; |
|
205 return u; |
|
206 } |
|
207 |
|
208 // ==================================================================== |
|
209 // A ref is another "mark" that references a mark. When the bytecode |
|
210 // is written to file, they are changed into their marks' current |
|
211 // positions. Marks themselves are never written to files, only refs are |
|
212 unsigned int AddMarkReference (unsigned int marknum, bool placeholder = true) { |
|
213 unsigned int u; |
|
214 for (u = 0; u < MAX_MARKS; u++) |
|
215 if (!refs[u]) |
|
216 break; |
|
217 |
|
218 if (u == MAX_MARKS) |
|
219 error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n"); |
|
220 |
|
221 ScriptMarkReference* r = new ScriptMarkReference; |
|
222 r->num = marknum; |
|
223 r->pos = writesize; |
|
224 refs[u] = r; |
|
225 |
|
226 // Write a dummy placeholder for the reference |
|
227 if (placeholder) |
|
228 Write (1234); |
|
229 |
|
230 return u; |
|
231 } |
|
232 |
|
233 // Delete a mark and all references to it. |
|
234 void DeleteMark (unsigned int marknum) { |
|
235 if (!marks[marknum]) |
|
236 return; |
|
237 |
|
238 // Delete the mark |
|
239 delete marks[marknum]; |
|
240 marks[marknum] = NULL; |
|
241 |
|
242 // Delete its references |
|
243 for (unsigned int u = 0; u < MAX_MARKS; u++) { |
|
244 if (refs[u]->num == marknum) { |
|
245 delete refs[u]; |
|
246 refs[u] = NULL; |
|
247 } |
|
248 } |
|
249 } |
|
250 |
|
251 // Adjusts a mark to the current position |
|
252 void MoveMark (unsigned int mark, int offset = -1) { |
|
253 if (!marks[mark]) |
|
254 return; |
|
255 marks[mark]->pos = writesize; |
|
256 } |
|
257 |
|
258 void OffsetMark (unsigned int mark, size_t offset) { |
|
259 if (!marks[mark]) |
|
260 return; |
|
261 marks[mark]->pos += offset; |
|
262 } |
|
263 |
|
264 // Dump the buffer (for debugging purposes) |
|
265 void Dump() { |
|
266 for (unsigned int x = 0; x < writesize; x++) |
|
267 printf ("%d. [%d]\n", x, *(buffer+x)); |
|
268 } |
|
269 |
|
270 // Count the amount of marks |
|
271 unsigned int CountMarks () { |
|
272 unsigned int count = 0; |
|
273 for (unsigned int u = 0; u < MAX_MARKS; u++) |
|
274 count += !!marks[u]; |
|
275 return count; |
|
276 } |
|
277 |
|
278 // Count the amount of refs |
|
279 unsigned int CountReferences () { |
|
280 unsigned int count = 0; |
|
281 for (unsigned int u = 0; u < MAX_MARKS; u++) |
|
282 count += !!refs[u]; |
|
283 return count; |
|
284 } |
|
285 |
|
286 // Write a float into the buffer |
|
287 void WriteFloat (str floatstring) { |
|
288 // TODO: Casting float to word causes the decimal to be lost. |
|
289 // Find a way to store the number without such loss. |
|
290 float val = atof (floatstring); |
|
291 Write (DH_PUSHNUMBER); |
|
292 Write (static_cast<word> ((val > 0) ? val : -val)); |
|
293 if (val < 0) |
|
294 Write (DH_UNARYMINUS); |
|
295 } |
|
296 |
|
297 void WriteString (str string) { |
|
298 Write (DH_PUSHSTRINGINDEX); |
|
299 Write (PushToStringTable (string)); |
|
300 } |
|
301 }; |
|
302 |
|
303 #endif // __DATABUFFER_H__ |
|