]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright 2013 Steven Watanabe | |
3 | * Distributed under the Boost Software License, Version 1.0. | |
4 | * (See accompanying file LICENSE_1_0.txt or copy at | |
5 | * http://www.boost.org/LICENSE_1_0.txt) | |
6 | */ | |
7 | ||
8 | #include "../object.h" | |
9 | #include "../lists.h" | |
10 | #include "../modules.h" | |
11 | #include "../rules.h" | |
12 | #include "../variable.h" | |
13 | #include "../native.h" | |
14 | #include "../compile.h" | |
15 | #include "../mem.h" | |
16 | #include "../constants.h" | |
17 | #include "string.h" | |
18 | ||
19 | struct ps_map_entry | |
20 | { | |
21 | struct ps_map_entry * next; | |
22 | LIST * key; | |
23 | OBJECT * value; | |
24 | }; | |
25 | ||
26 | struct ps_map | |
27 | { | |
28 | struct ps_map_entry * * table; | |
29 | size_t table_size; | |
30 | size_t num_elems; | |
31 | }; | |
32 | ||
33 | static unsigned list_hash(LIST * key) | |
34 | { | |
35 | unsigned int hash = 0; | |
36 | LISTITER iter = list_begin( key ), end = list_end( key ); | |
37 | for ( ; iter != end; ++iter ) | |
38 | { | |
39 | hash = hash * 2147059363 + object_hash( list_item( iter ) ); | |
40 | } | |
41 | return hash; | |
42 | } | |
43 | ||
44 | static int list_equal( LIST * lhs, LIST * rhs ) | |
45 | { | |
46 | LISTITER lhs_iter, lhs_end, rhs_iter; | |
47 | if ( list_length( lhs ) != list_length( rhs ) ) | |
48 | { | |
49 | return 0; | |
50 | } | |
51 | lhs_iter = list_begin( lhs ); | |
52 | lhs_end = list_end( lhs ); | |
53 | rhs_iter = list_begin( rhs ); | |
54 | for ( ; lhs_iter != lhs_end; ++lhs_iter, ++rhs_iter ) | |
55 | { | |
56 | if ( ! object_equal( list_item( lhs_iter ), list_item( rhs_iter ) ) ) | |
57 | { | |
58 | return 0; | |
59 | } | |
60 | } | |
61 | return 1; | |
62 | } | |
63 | ||
64 | static void ps_map_init( struct ps_map * map ) | |
65 | { | |
66 | size_t i; | |
67 | map->table_size = 2; | |
68 | map->num_elems = 0; | |
69 | map->table = BJAM_MALLOC( map->table_size * sizeof( struct ps_map_entry * ) ); | |
70 | for ( i = 0; i < map->table_size; ++i ) | |
71 | { | |
72 | map->table[ i ] = NULL; | |
73 | } | |
74 | } | |
75 | ||
76 | static void ps_map_destroy( struct ps_map * map ) | |
77 | { | |
78 | size_t i; | |
79 | for ( i = 0; i < map->table_size; ++i ) | |
80 | { | |
81 | struct ps_map_entry * pos; | |
82 | for ( pos = map->table[ i ]; pos; ) | |
83 | { | |
84 | struct ps_map_entry * tmp = pos->next; | |
85 | object_free( pos->value ); | |
86 | BJAM_FREE( pos ); | |
87 | pos = tmp; | |
88 | } | |
89 | } | |
90 | BJAM_FREE( map->table ); | |
91 | } | |
92 | ||
93 | static void ps_map_rehash( struct ps_map * map ) | |
94 | { | |
95 | struct ps_map old = *map; | |
96 | size_t i; | |
97 | map->table = BJAM_MALLOC( map->table_size * 2 * sizeof( struct ps_map_entry * ) ); | |
98 | map->table_size *= 2; | |
99 | for ( i = 0; i < map->table_size; ++i ) | |
100 | { | |
101 | map->table[ i ] = NULL; | |
102 | } | |
103 | for ( i = 0; i < old.table_size; ++i ) | |
104 | { | |
105 | struct ps_map_entry * pos; | |
106 | for ( pos = old.table[ i ]; pos; ) | |
107 | { | |
108 | struct ps_map_entry * tmp = pos->next; | |
109 | ||
110 | unsigned hash_val = list_hash( pos->key ); | |
111 | unsigned bucket = hash_val % map->table_size; | |
112 | pos->next = map->table[ bucket ]; | |
113 | map->table[ bucket ] = pos; | |
114 | ||
115 | pos = tmp; | |
116 | } | |
117 | } | |
118 | BJAM_FREE( old.table ); | |
119 | } | |
120 | ||
121 | static struct ps_map_entry * ps_map_insert(struct ps_map * map, LIST * key) | |
122 | { | |
123 | unsigned hash_val = list_hash( key ); | |
124 | unsigned bucket = hash_val % map->table_size; | |
125 | struct ps_map_entry * pos; | |
126 | for ( pos = map->table[bucket]; pos ; pos = pos->next ) | |
127 | { | |
128 | if ( list_equal( pos->key, key ) ) | |
129 | return pos; | |
130 | } | |
131 | ||
132 | if ( map->num_elems >= map->table_size ) | |
133 | { | |
134 | ps_map_rehash( map ); | |
135 | bucket = hash_val % map->table_size; | |
136 | } | |
137 | pos = BJAM_MALLOC( sizeof( struct ps_map_entry ) ); | |
138 | pos->next = map->table[bucket]; | |
139 | pos->key = key; | |
140 | pos->value = 0; | |
141 | map->table[bucket] = pos; | |
142 | ++map->num_elems; | |
143 | return pos; | |
144 | } | |
145 | ||
146 | static struct ps_map all_property_sets; | |
147 | ||
148 | LIST * property_set_create( FRAME * frame, int flags ) | |
149 | { | |
150 | LIST * properties = lol_get( frame->args, 0 ); | |
151 | LIST * sorted = list_sort( properties ); | |
152 | LIST * unique = list_unique( sorted ); | |
153 | struct ps_map_entry * pos = ps_map_insert( &all_property_sets, unique ); | |
154 | list_free( sorted ); | |
155 | if ( pos->value ) | |
156 | { | |
157 | list_free( unique ); | |
158 | return list_new( object_copy( pos->value ) ); | |
159 | } | |
160 | else | |
161 | { | |
162 | OBJECT * rulename = object_new( "new" ); | |
163 | OBJECT * varname = object_new( "self.raw" ); | |
164 | LIST * val = call_rule( rulename, frame, | |
165 | list_new( object_new( "property-set" ) ), 0 ); | |
166 | LISTITER iter, end; | |
167 | object_free( rulename ); | |
168 | pos->value = object_copy( list_front( val ) ); | |
169 | var_set( bindmodule( pos->value ), varname, unique, VAR_SET ); | |
170 | object_free( varname ); | |
171 | ||
172 | for ( iter = list_begin( unique ), end = list_end( unique ); iter != end; ++iter ) | |
173 | { | |
174 | const char * str = object_str( list_item( iter ) ); | |
175 | if ( str[ 0 ] != '<' || ! strchr( str, '>' ) ) | |
176 | { | |
177 | string message[ 1 ]; | |
178 | string_new( message ); | |
179 | string_append( message, "Invalid property: '" ); | |
180 | string_append( message, str ); | |
181 | string_append( message, "'" ); | |
182 | rulename = object_new( "errors.error" ); | |
183 | call_rule( rulename, frame, | |
184 | list_new( object_new( message->value ) ), 0 ); | |
185 | /* unreachable */ | |
186 | string_free( message ); | |
187 | object_free( rulename ); | |
188 | } | |
189 | } | |
190 | ||
191 | return val; | |
192 | } | |
193 | } | |
194 | ||
195 | /* binary search for the property value */ | |
196 | LIST * property_set_get( FRAME * frame, int flags ) | |
197 | { | |
198 | OBJECT * varname = object_new( "self.raw" ); | |
199 | LIST * props = var_get( frame->module, varname ); | |
200 | const char * name = object_str( list_front( lol_get( frame->args, 0 ) ) ); | |
201 | size_t name_len = strlen( name ); | |
202 | LISTITER begin, end; | |
203 | LIST * result = L0; | |
204 | object_free( varname ); | |
205 | ||
206 | /* Assumes random access */ | |
207 | begin = list_begin( props ), end = list_end( props ); | |
208 | ||
209 | while ( 1 ) | |
210 | { | |
211 | ptrdiff_t diff = (end - begin); | |
212 | LISTITER mid = begin + diff / 2; | |
213 | int res; | |
214 | if ( diff == 0 ) | |
215 | { | |
216 | return L0; | |
217 | } | |
218 | res = strncmp( object_str( list_item( mid ) ), name, name_len ); | |
219 | if ( res < 0 ) | |
220 | { | |
221 | begin = mid + 1; | |
222 | } | |
223 | else if ( res > 0 ) | |
224 | { | |
225 | end = mid; | |
226 | } | |
227 | else /* We've found the property */ | |
228 | { | |
229 | /* Find the beginning of the group */ | |
230 | LISTITER tmp = mid; | |
231 | while ( tmp > begin ) | |
232 | { | |
233 | --tmp; | |
234 | res = strncmp( object_str( list_item( tmp ) ), name, name_len ); | |
235 | if ( res != 0 ) | |
236 | { | |
237 | ++tmp; | |
238 | break; | |
239 | } | |
240 | } | |
241 | begin = tmp; | |
242 | /* Find the end of the group */ | |
243 | tmp = mid + 1; | |
244 | while ( tmp < end ) | |
245 | { | |
246 | res = strncmp( object_str( list_item( tmp ) ), name, name_len ); | |
247 | if ( res != 0 ) break; | |
248 | ++tmp; | |
249 | } | |
250 | end = tmp; | |
251 | break; | |
252 | } | |
253 | } | |
254 | ||
255 | for ( ; begin != end; ++begin ) | |
256 | { | |
257 | result = list_push_back( result, | |
258 | object_new( object_str( list_item( begin ) ) + name_len ) ); | |
259 | } | |
260 | ||
261 | return result; | |
262 | } | |
263 | ||
264 | /* binary search for the property value */ | |
265 | LIST * property_set_contains_features( FRAME * frame, int flags ) | |
266 | { | |
267 | OBJECT * varname = object_new( "self.raw" ); | |
268 | LIST * props = var_get( frame->module, varname ); | |
269 | LIST * features = lol_get( frame->args, 0 ); | |
270 | LIST * result = L0; | |
271 | LISTITER features_iter = list_begin( features ); | |
272 | LISTITER features_end = list_end( features ) ; | |
273 | object_free( varname ); | |
274 | ||
275 | for ( ; features_iter != features_end; ++features_iter ) | |
276 | { | |
277 | const char * name = object_str( list_item( features_iter ) ); | |
278 | size_t name_len = strlen( name ); | |
279 | LISTITER begin, end; | |
280 | /* Assumes random access */ | |
281 | begin = list_begin( props ), end = list_end( props ); | |
282 | ||
283 | while ( 1 ) | |
284 | { | |
285 | ptrdiff_t diff = (end - begin); | |
286 | LISTITER mid = begin + diff / 2; | |
287 | int res; | |
288 | if ( diff == 0 ) | |
289 | { | |
290 | /* The feature is missing */ | |
291 | return L0; | |
292 | } | |
293 | res = strncmp( object_str( list_item( mid ) ), name, name_len ); | |
294 | if ( res < 0 ) | |
295 | { | |
296 | begin = mid + 1; | |
297 | } | |
298 | else if ( res > 0 ) | |
299 | { | |
300 | end = mid; | |
301 | } | |
302 | else /* We've found the property */ | |
303 | { | |
304 | break; | |
305 | } | |
306 | } | |
307 | } | |
308 | return list_new( object_copy( constant_true ) ); | |
309 | } | |
310 | ||
311 | void init_property_set() | |
312 | { | |
313 | { | |
314 | char const * args[] = { "raw-properties", "*", 0 }; | |
315 | declare_native_rule( "property-set", "create", args, property_set_create, 1 ); | |
316 | } | |
317 | { | |
318 | char const * args[] = { "feature", 0 }; | |
319 | declare_native_rule( "class@property-set", "get", args, property_set_get, 1 ); | |
320 | } | |
321 | { | |
322 | char const * args[] = { "features", "*", 0 }; | |
323 | declare_native_rule( "class@property-set", "contains-features", args, property_set_contains_features, 1 ); | |
324 | } | |
325 | ps_map_init( &all_property_sets ); | |
326 | } | |
327 | ||
328 | void property_set_done() | |
329 | { | |
330 | ps_map_destroy( &all_property_sets ); | |
331 | } |