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