• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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