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
list_hash(LIST * key)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
list_equal(LIST * lhs,LIST * rhs)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
ps_map_init(struct ps_map * map)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 = (struct ps_map_entry * *)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
ps_map_destroy(struct ps_map * map)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
ps_map_rehash(struct ps_map * map)93 static void ps_map_rehash( struct ps_map * map )
94 {
95 struct ps_map old = *map;
96 size_t i;
97 map->table = (struct ps_map_entry * *)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
ps_map_insert(struct ps_map * map,LIST * key)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 = (struct ps_map_entry *)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
property_set_create(FRAME * frame,int flags)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 LIST * imports = list_new( object_new( "errors" ) );
183 import_module( imports, frame->module );
184 rulename = object_new( "errors.error" );
185 call_rule( rulename, frame,
186 list_new( object_new( message->value ) ), 0 );
187 /* unreachable */
188 string_free( message );
189 object_free( list_front( imports ) );
190 list_free( imports );
191 object_free( rulename );
192 }
193 }
194
195 return val;
196 }
197 }
198
199 /* binary search for the property value */
property_set_get(FRAME * frame,int flags)200 LIST * property_set_get( FRAME * frame, int flags )
201 {
202 OBJECT * varname = object_new( "self.raw" );
203 LIST * props = var_get( frame->module, varname );
204 const char * name = object_str( list_front( lol_get( frame->args, 0 ) ) );
205 size_t name_len = strlen( name );
206 LISTITER begin, end;
207 LIST * result = L0;
208 object_free( varname );
209
210 /* Assumes random access */
211 begin = list_begin( props ), end = list_end( props );
212
213 while ( 1 )
214 {
215 ptrdiff_t diff = (end - begin);
216 LISTITER mid = begin + diff / 2;
217 int res;
218 if ( diff == 0 )
219 {
220 return L0;
221 }
222 res = strncmp( object_str( list_item( mid ) ), name, name_len );
223 if ( res < 0 )
224 {
225 begin = mid + 1;
226 }
227 else if ( res > 0 )
228 {
229 end = mid;
230 }
231 else /* We've found the property */
232 {
233 /* Find the beginning of the group */
234 LISTITER tmp = mid;
235 while ( tmp > begin )
236 {
237 --tmp;
238 res = strncmp( object_str( list_item( tmp ) ), name, name_len );
239 if ( res != 0 )
240 {
241 ++tmp;
242 break;
243 }
244 }
245 begin = tmp;
246 /* Find the end of the group */
247 tmp = mid + 1;
248 while ( tmp < end )
249 {
250 res = strncmp( object_str( list_item( tmp ) ), name, name_len );
251 if ( res != 0 ) break;
252 ++tmp;
253 }
254 end = tmp;
255 break;
256 }
257 }
258
259 for ( ; begin != end; ++begin )
260 {
261 result = list_push_back( result,
262 object_new( object_str( list_item( begin ) ) + name_len ) );
263 }
264
265 return result;
266 }
267
268 /* binary search for the property value */
property_set_contains_features(FRAME * frame,int flags)269 LIST * property_set_contains_features( FRAME * frame, int flags )
270 {
271 OBJECT * varname = object_new( "self.raw" );
272 LIST * props = var_get( frame->module, varname );
273 LIST * features = lol_get( frame->args, 0 );
274 LISTITER features_iter = list_begin( features );
275 LISTITER features_end = list_end( features ) ;
276 object_free( varname );
277
278 for ( ; features_iter != features_end; ++features_iter )
279 {
280 const char * name = object_str( list_item( features_iter ) );
281 size_t name_len = strlen( name );
282 LISTITER begin, end;
283 /* Assumes random access */
284 begin = list_begin( props ), end = list_end( props );
285
286 while ( 1 )
287 {
288 ptrdiff_t diff = (end - begin);
289 LISTITER mid = begin + diff / 2;
290 int res;
291 if ( diff == 0 )
292 {
293 /* The feature is missing */
294 return L0;
295 }
296 res = strncmp( object_str( list_item( mid ) ), name, name_len );
297 if ( res < 0 )
298 {
299 begin = mid + 1;
300 }
301 else if ( res > 0 )
302 {
303 end = mid;
304 }
305 else /* We've found the property */
306 {
307 break;
308 }
309 }
310 }
311 return list_new( object_copy( constant_true ) );
312 }
313
init_property_set()314 void init_property_set()
315 {
316 {
317 char const * args[] = { "raw-properties", "*", 0 };
318 declare_native_rule( "property-set", "create", args, property_set_create, 1 );
319 }
320 {
321 char const * args[] = { "feature", 0 };
322 declare_native_rule( "class@property-set", "get", args, property_set_get, 1 );
323 }
324 {
325 char const * args[] = { "features", "*", 0 };
326 declare_native_rule( "class@property-set", "contains-features", args, property_set_contains_features, 1 );
327 }
328 ps_map_init( &all_property_sets );
329 }
330
property_set_done()331 void property_set_done()
332 {
333 ps_map_destroy( &all_property_sets );
334 }
335