summaryrefslogtreecommitdiff
path: root/tools/build/src/engine/modules/property-set.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/build/src/engine/modules/property-set.c')
-rw-r--r--tools/build/src/engine/modules/property-set.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/tools/build/src/engine/modules/property-set.c b/tools/build/src/engine/modules/property-set.c
new file mode 100644
index 0000000000..21e35d5ab7
--- /dev/null
+++ b/tools/build/src/engine/modules/property-set.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2013 Steven Watanabe
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "../object.h"
+#include "../lists.h"
+#include "../modules.h"
+#include "../rules.h"
+#include "../variable.h"
+#include "../native.h"
+#include "../compile.h"
+#include "../mem.h"
+#include "../constants.h"
+#include "string.h"
+
+struct ps_map_entry
+{
+ struct ps_map_entry * next;
+ LIST * key;
+ OBJECT * value;
+};
+
+struct ps_map
+{
+ struct ps_map_entry * * table;
+ size_t table_size;
+ size_t num_elems;
+};
+
+static unsigned list_hash(LIST * key)
+{
+ unsigned int hash = 0;
+ LISTITER iter = list_begin( key ), end = list_end( key );
+ for ( ; iter != end; ++iter )
+ {
+ hash = hash * 2147059363 + object_hash( list_item( iter ) );
+ }
+ return hash;
+}
+
+static int list_equal( LIST * lhs, LIST * rhs )
+{
+ LISTITER lhs_iter, lhs_end, rhs_iter;
+ if ( list_length( lhs ) != list_length( rhs ) )
+ {
+ return 0;
+ }
+ lhs_iter = list_begin( lhs );
+ lhs_end = list_end( lhs );
+ rhs_iter = list_begin( rhs );
+ for ( ; lhs_iter != lhs_end; ++lhs_iter, ++rhs_iter )
+ {
+ if ( ! object_equal( list_item( lhs_iter ), list_item( rhs_iter ) ) )
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void ps_map_init( struct ps_map * map )
+{
+ size_t i;
+ map->table_size = 2;
+ map->num_elems = 0;
+ map->table = BJAM_MALLOC( map->table_size * sizeof( struct ps_map_entry * ) );
+ for ( i = 0; i < map->table_size; ++i )
+ {
+ map->table[ i ] = NULL;
+ }
+}
+
+static void ps_map_destroy( struct ps_map * map )
+{
+ size_t i;
+ for ( i = 0; i < map->table_size; ++i )
+ {
+ struct ps_map_entry * pos;
+ for ( pos = map->table[ i ]; pos; )
+ {
+ struct ps_map_entry * tmp = pos->next;
+ BJAM_FREE( pos );
+ pos = tmp;
+ }
+ }
+ BJAM_FREE( map->table );
+}
+
+static void ps_map_rehash( struct ps_map * map )
+{
+ struct ps_map old = *map;
+ size_t i;
+ map->table = BJAM_MALLOC( map->table_size * 2 * sizeof( struct ps_map_entry * ) );
+ map->table_size *= 2;
+ for ( i = 0; i < map->table_size; ++i )
+ {
+ map->table[ i ] = NULL;
+ }
+ for ( i = 0; i < old.table_size; ++i )
+ {
+ struct ps_map_entry * pos;
+ for ( pos = old.table[ i ]; pos; )
+ {
+ struct ps_map_entry * tmp = pos->next;
+
+ unsigned hash_val = list_hash( pos->key );
+ unsigned bucket = hash_val % map->table_size;
+ pos->next = map->table[ bucket ];
+ map->table[ bucket ] = pos;
+
+ pos = tmp;
+ }
+ }
+ BJAM_FREE( old.table );
+}
+
+static struct ps_map_entry * ps_map_insert(struct ps_map * map, LIST * key)
+{
+ unsigned hash_val = list_hash( key );
+ unsigned bucket = hash_val % map->table_size;
+ struct ps_map_entry * pos;
+ for ( pos = map->table[bucket]; pos ; pos = pos->next )
+ {
+ if ( list_equal( pos->key, key ) )
+ return pos;
+ }
+
+ if ( map->num_elems >= map->table_size )
+ {
+ ps_map_rehash( map );
+ bucket = hash_val % map->table_size;
+ }
+ pos = BJAM_MALLOC( sizeof( struct ps_map_entry ) );
+ pos->next = map->table[bucket];
+ pos->key = key;
+ pos->value = 0;
+ map->table[bucket] = pos;
+ ++map->num_elems;
+ return pos;
+}
+
+static struct ps_map all_property_sets;
+
+LIST * property_set_create( FRAME * frame, int flags )
+{
+ LIST * properties = lol_get( frame->args, 0 );
+ LIST * sorted = list_sort( properties );
+ LIST * unique = list_unique( sorted );
+ struct ps_map_entry * pos = ps_map_insert( &all_property_sets, unique );
+ list_free( sorted );
+ if ( pos->value )
+ {
+ list_free( unique );
+ return list_new( object_copy( pos->value ) );
+ }
+ else
+ {
+ OBJECT * rulename = object_new( "new" );
+ OBJECT * varname = object_new( "self.raw" );
+ LIST * val = call_rule( rulename, frame,
+ list_new( object_new( "property-set" ) ), 0 );
+ LISTITER iter, end;
+ object_free( rulename );
+ pos->value = list_front( val );
+ var_set( bindmodule( pos->value ), varname, unique, VAR_SET );
+ object_free( varname );
+
+ for ( iter = list_begin( unique ), end = list_end( unique ); iter != end; ++iter )
+ {
+ const char * str = object_str( list_item( iter ) );
+ if ( str[ 0 ] != '<' || ! strchr( str, '>' ) )
+ {
+ string message[ 1 ];
+ string_new( message );
+ string_append( message, "Invalid property: '" );
+ string_append( message, str );
+ string_append( message, "'" );
+ rulename = object_new( "errors.error" );
+ call_rule( rulename, frame,
+ list_new( object_new( message->value ) ), 0 );
+ /* unreachable */
+ string_free( message );
+ object_free( rulename );
+ }
+ }
+
+ return val;
+ }
+}
+
+/* binary search for the property value */
+LIST * property_set_get( FRAME * frame, int flags )
+{
+ OBJECT * varname = object_new( "self.raw" );
+ LIST * props = var_get( frame->module, varname );
+ const char * name = object_str( list_front( lol_get( frame->args, 0 ) ) );
+ size_t name_len = strlen( name );
+ LISTITER begin, end;
+ LIST * result = L0;
+ object_free( varname );
+
+ /* Assumes random access */
+ begin = list_begin( props ), end = list_end( props );
+
+ while ( 1 )
+ {
+ ptrdiff_t diff = (end - begin);
+ LISTITER mid = begin + diff / 2;
+ int res;
+ if ( diff == 0 )
+ {
+ return L0;
+ }
+ res = strncmp( object_str( list_item( mid ) ), name, name_len );
+ if ( res < 0 )
+ {
+ begin = mid + 1;
+ }
+ else if ( res > 0 )
+ {
+ end = mid;
+ }
+ else /* We've found the property */
+ {
+ /* Find the beginning of the group */
+ LISTITER tmp = mid;
+ while ( tmp > begin )
+ {
+ --tmp;
+ res = strncmp( object_str( list_item( tmp ) ), name, name_len );
+ if ( res != 0 )
+ {
+ ++tmp;
+ break;
+ }
+ }
+ begin = tmp;
+ /* Find the end of the group */
+ tmp = mid + 1;
+ while ( tmp < end )
+ {
+ res = strncmp( object_str( list_item( tmp ) ), name, name_len );
+ if ( res != 0 ) break;
+ ++tmp;
+ }
+ end = tmp;
+ break;
+ }
+ }
+
+ for ( ; begin != end; ++begin )
+ {
+ result = list_push_back( result,
+ object_new( object_str( list_item( begin ) ) + name_len ) );
+ }
+
+ return result;
+}
+
+/* binary search for the property value */
+LIST * property_set_contains_features( FRAME * frame, int flags )
+{
+ OBJECT * varname = object_new( "self.raw" );
+ LIST * props = var_get( frame->module, varname );
+ LIST * features = lol_get( frame->args, 0 );
+ LIST * result = L0;
+ LISTITER features_iter = list_begin( features );
+ LISTITER features_end = list_end( features ) ;
+ object_free( varname );
+
+ for ( ; features_iter != features_end; ++features_iter )
+ {
+ const char * name = object_str( list_item( features_iter ) );
+ size_t name_len = strlen( name );
+ LISTITER begin, end;
+ /* Assumes random access */
+ begin = list_begin( props ), end = list_end( props );
+
+ while ( 1 )
+ {
+ ptrdiff_t diff = (end - begin);
+ LISTITER mid = begin + diff / 2;
+ int res;
+ if ( diff == 0 )
+ {
+ /* The feature is missing */
+ return L0;
+ }
+ res = strncmp( object_str( list_item( mid ) ), name, name_len );
+ if ( res < 0 )
+ {
+ begin = mid + 1;
+ }
+ else if ( res > 0 )
+ {
+ end = mid;
+ }
+ else /* We've found the property */
+ {
+ break;
+ }
+ }
+ }
+ return list_new( object_copy( constant_true ) );
+}
+
+void init_property_set()
+{
+ {
+ char const * args[] = { "raw-properties", "*", 0 };
+ declare_native_rule( "property-set", "create", args, property_set_create, 1 );
+ }
+ {
+ char const * args[] = { "feature", 0 };
+ declare_native_rule( "class@property-set", "get", args, property_set_get, 1 );
+ }
+ {
+ char const * args[] = { "features", "*", 0 };
+ declare_native_rule( "class@property-set", "contains-features", args, property_set_contains_features, 1 );
+ }
+ ps_map_init( &all_property_sets );
+}
+
+void property_set_done()
+{
+ ps_map_destroy( &all_property_sets );
+}