1 /*
2 defspace.c
3
4 management of data segments
5
6 Copyright (C) 2011 Bill Currie <bill@taniwha.org>
7
8 Author: Bill Currie <bill@taniwha.org>
9 Date: 2011/01/16
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20 See the GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to:
24
25 Free Software Foundation, Inc.
26 59 Temple Place - Suite 330
27 Boston, MA 02111-1307, USA
28
29 */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #endif
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif
40 #include <stdlib.h>
41
42 #include "QF/alloc.h"
43 #include "QF/hash.h"
44 #include "QF/sys.h"
45 #include "QF/va.h"
46
47 #include "qfcc.h"
48 #include "defspace.h"
49 #include "diagnostic.h"
50 #include "expr.h"
51 #include "options.h"
52 #include "reloc.h"
53 #include "strpool.h"
54 #include "struct.h"
55 #include "type.h"
56
57 typedef struct locref_s {
58 struct locref_s *next;
59 int ofs;
60 int size;
61 } locref_t;
62
63 static defspace_t *free_spaces;
64 static locref_t *free_locrefs;
65
66 #define GROW 1024
67
68 static int
grow_space_global(defspace_t * space)69 grow_space_global (defspace_t *space)
70 {
71 int size;
72
73 if (space->size <= space->max_size)
74 return 1;
75
76 size = space->size + GROW;
77 size -= size % GROW;
78 space->data = realloc (space->data, size * sizeof (pr_type_t));
79 memset (space->data + space->max_size, 0, GROW * sizeof (pr_type_t));
80 space->max_size = size;
81 return 1;
82 }
83
84 static int
grow_space_virtual(defspace_t * space)85 grow_space_virtual (defspace_t *space)
86 {
87 int size;
88
89 if (space->size <= space->max_size)
90 return 1;
91
92 size = space->size + GROW;
93 size -= size % GROW;
94 space->max_size = size;
95 return 1;
96 }
97
98 defspace_t *
defspace_new(ds_type_t type)99 defspace_new (ds_type_t type)
100 {
101 defspace_t *space;
102
103 ALLOC (1024, defspace_t, spaces, space);
104 space->def_tail = &space->defs;
105 space->type = type;
106 if (type == ds_backed) {
107 space->grow = grow_space_global;
108 } else if (type == ds_virtual) {
109 space->grow = grow_space_virtual;
110 } else {
111 internal_error (0, "unknown defspace type");
112 }
113 return space;
114 }
115
116 int
defspace_alloc_loc(defspace_t * space,int size)117 defspace_alloc_loc (defspace_t *space, int size)
118 {
119 int ofs;
120 locref_t *loc;
121 locref_t **l = &space->free_locs;
122
123 if (size <= 0)
124 internal_error (0, "invalid number of words requested: %d", size);
125 while (*l && (*l)->size < size)
126 l = &(*l)->next;
127 if ((loc = *l)) {
128 ofs = (*l)->ofs;
129 if ((*l)->size == size) {
130 loc = *l;
131 *l = (*l)->next;
132 FREE (locrefs, loc);
133 } else {
134 (*l)->ofs += size;
135 (*l)->size -= size;
136 }
137 return ofs;
138 }
139 ofs = space->size;
140 space->size += size;
141 if (space->size > space->max_size) {
142 if (!space->grow || !space->grow (space))
143 internal_error (0, "unable to allocate %d words", size);
144 }
145 return ofs;
146 }
147
148 void
defspace_free_loc(defspace_t * space,int ofs,int size)149 defspace_free_loc (defspace_t *space, int ofs, int size)
150 {
151 locref_t **l;
152 locref_t *loc;
153
154 if (size <= 0)
155 internal_error (0, "defspace: freeing size %d location", size);
156 if (ofs < 0 || ofs >= space->size || ofs + size > space->size)
157 internal_error (0, "defspace: freeing bogus location %d:%d",
158 ofs, size);
159 for (l = &space->free_locs; *l; l = &(*l)->next) {
160 loc = *l;
161 // location to be freed is below the free block
162 // need to create a new free block
163 if (ofs + size < loc->ofs)
164 break;
165 // location to be freed is immediately below the free block
166 // merge with the free block
167 if (ofs + size == loc->ofs) {
168 loc->size += size;
169 loc->ofs = ofs;
170 return;
171 }
172 // location to be freed overlaps the free block
173 // this is an error
174 if (ofs + size > loc->ofs && ofs < loc->ofs + loc->size)
175 internal_error (0, "defspace: freeing bogus location %d:%d",
176 ofs, size);
177 // location to be freed is immediately above the free block
178 // merge with the free block
179 if (ofs == loc->ofs + loc->size) {
180 loc->size += size;
181 // location to be freed is immediately below the next free block
182 // merge with the next block too, and unlink that block
183 if (loc->next && loc->next->ofs == loc->ofs + loc->size) {
184 loc->size += loc->next->size;
185 loc = loc->next;
186 *l = loc->next;
187 FREE (locrefs, loc);
188 }
189 return;
190 }
191 }
192 // insert a new free block for the location to be freed
193 ALLOC (1024, locref_t, locrefs, loc);
194 loc->ofs = ofs;
195 loc->size = size;
196 loc->next = *l;
197 *l = loc;
198 }
199
200 int
defspace_add_data(defspace_t * space,pr_type_t * data,int size)201 defspace_add_data (defspace_t *space, pr_type_t *data, int size)
202 {
203 int loc;
204
205 loc = defspace_alloc_loc (space, size);
206 if (data)
207 memcpy (space->data + loc, data, size * sizeof (pr_type_t));
208 return loc;
209 }
210