/* * Copyright (C) 2003, 2004, 2005, 2014, 2015 Filip Pizlo. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tsf_internal.h" #include "tsf_format.h" #include #include #include #include #ifdef HAVE_PTHREAD #include #endif #ifdef DEBUG_TYPE_ALLOC static int64_t type_balance = 0; int64_t tsf_type_get_balance() { return type_balance; } #define increment_type_balance() do {\ type_balance++;\ } while(0) #define decrement_type_balance() do {\ type_balance--;\ } while(0) #else #define increment_type_balance() do {\ } while(0) #define decrement_type_balance() do {\ } while(0) #endif static tsf_type_t void_type, int8_type, uint8_type, int16_type, uint16_type, int32_type, uint32_type, int64_type, uint64_type, integer_type, long_type, float_type, double_type, bit_type, string_type, any_type; static tsf_st_table *memo_table; static tsf_mutex_t memo_lock; static void init_basic_type(tsf_type_t *type) { type->is_dynamic = tsf_false; type->comment = NULL; tsf_atomic_count_init(&type->ref_count, 1); type->byte_size = 1; type->name = NULL; type->version = 0; type->memo_master = type; } static void init_types(void) { tsf_assert(memo_table = tsf_st_init_typetable()); tsf_mutex_init(&memo_lock); void_type.kind_code = TSF_TK_VOID; void_type.hash_code = TSF_TK_VOID; init_basic_type(&void_type); int8_type.kind_code = TSF_TK_INT8; int8_type.hash_code = TSF_TK_INT8; init_basic_type(&int8_type); uint8_type.kind_code = TSF_TK_UINT8; uint8_type.hash_code = TSF_TK_UINT8; init_basic_type(&uint8_type); int16_type.kind_code = TSF_TK_INT16; int16_type.hash_code = TSF_TK_INT16; init_basic_type(&int16_type); uint16_type.kind_code = TSF_TK_UINT16; uint16_type.hash_code = TSF_TK_UINT16; init_basic_type(&uint16_type); int32_type.kind_code = TSF_TK_INT32; int32_type.hash_code = TSF_TK_INT32; init_basic_type(&int32_type); uint32_type.kind_code = TSF_TK_UINT32; uint32_type.hash_code = TSF_TK_UINT32; init_basic_type(&uint32_type); int64_type.kind_code = TSF_TK_INT64; int64_type.hash_code = TSF_TK_INT64; init_basic_type(&int64_type); uint64_type.kind_code = TSF_TK_UINT64; uint64_type.hash_code = TSF_TK_UINT64; init_basic_type(&uint64_type); integer_type.kind_code = TSF_TK_INTEGER; integer_type.hash_code = TSF_TK_INTEGER; init_basic_type(&integer_type); long_type.kind_code = TSF_TK_LONG; long_type.hash_code = TSF_TK_LONG; init_basic_type(&long_type); float_type.kind_code = TSF_TK_FLOAT; float_type.hash_code = TSF_TK_FLOAT; init_basic_type(&float_type); double_type.kind_code = TSF_TK_DOUBLE; double_type.hash_code = TSF_TK_DOUBLE; init_basic_type(&double_type); bit_type.kind_code = TSF_TK_BIT; bit_type.hash_code = TSF_TK_BIT; init_basic_type(&bit_type); string_type.kind_code = TSF_TK_STRING; string_type.hash_code = TSF_TK_STRING; init_basic_type(&string_type); any_type.kind_code = TSF_TK_ANY; any_type.hash_code = TSF_TK_ANY; init_basic_type(&any_type); any_type.is_dynamic = tsf_true; } static tsf_once_t once_control = TSF_ONCE_INIT; static void check_init_types(void) { tsf_once(&once_control, init_types); } #define BIG_META_CODE_SWITCH(kind_code) \ switch (kind_code) { \ case TSF_TK_VOID: \ return &void_type; \ case TSF_TK_INT8: \ return &int8_type; \ case TSF_TK_UINT8: \ return &uint8_type; \ case TSF_TK_INT16: \ return &int16_type; \ case TSF_TK_UINT16: \ return &uint16_type; \ case TSF_TK_INT32: \ return &int32_type; \ case TSF_TK_UINT32: \ return &uint32_type; \ case TSF_TK_INT64: \ return &int64_type; \ case TSF_TK_UINT64: \ return &uint64_type; \ case TSF_TK_INTEGER: \ return &integer_type; \ case TSF_TK_LONG: \ return &long_type; \ case TSF_TK_FLOAT: \ return &float_type; \ case TSF_TK_DOUBLE: \ return &double_type; \ case TSF_TK_BIT: \ return &bit_type; \ case TSF_TK_STRING: \ return &string_type; \ case TSF_TK_ANY: \ return &any_type; \ default: \ break; \ } static inline tsf_bool_t is_singleton(tsf_type_kind_t kind_code) { switch (kind_code) { case TSF_TK_INT8: case TSF_TK_UINT8: case TSF_TK_INT16: case TSF_TK_UINT16: case TSF_TK_INT32: case TSF_TK_UINT32: case TSF_TK_INT64: case TSF_TK_UINT64: case TSF_TK_INTEGER: case TSF_TK_LONG: case TSF_TK_FLOAT: case TSF_TK_DOUBLE: case TSF_TK_BIT: case TSF_TK_VOID: case TSF_TK_STRING: case TSF_TK_ANY: return tsf_true; default: return tsf_false; } } tsf_type_t *tsf_type_create(tsf_type_kind_t kind_code) { tsf_type_t *ret; check_init_types(); if (kind_code == TSF_TK_ARRAY) { tsf_set_error(TSF_E_BAD_TYPE_KIND, "You cannot use TSF_TK_ARRAY with tsf_type_create(); " "to create an array type, use tsf_type_create_array() " "instead"); return NULL; } BIG_META_CODE_SWITCH(kind_code) ret = malloc(sizeof(tsf_type_t)); if (ret == NULL) { tsf_set_errno("Could not malloc tsf_type_t object."); return NULL; } tsf_atomic_count_init(&ret->ref_count, 1); ret->comment = NULL; ret->name = NULL; ret->version = 0; ret->kind_code = kind_code; ret->is_dynamic = tsf_false; switch (kind_code) { case TSF_TK_STRUCT: ret->u.s.tt = tsf_type_table_create(); if (ret->u.s.tt == NULL) { free(ret); return NULL; } ret->u.s.has_size = tsf_false; ret->u.s.constructor = NULL; ret->u.s.pre_destructor = NULL; ret->u.s.destructor = NULL; break; case TSF_TK_CHOICE: ret->u.h.tt = tsf_type_table_create(); if (ret->u.h.tt == NULL) { free(ret); return NULL; } ret->u.h.has_mapping = tsf_false; ret->u.h.choice_as_full_word = tsf_false; break; default: tsf_set_error(TSF_E_BAD_TYPE_KIND, "'" fui8 "' is not a valid kind type for " "tsf_type_create()", kind_code); free(ret); return NULL; } tsf_type_recompute_hash(ret); ret->byte_size = 0; ret->memo_master = NULL; increment_type_balance(); /* printf("created a type at %p\n",ret); */ return ret; } tsf_type_t *tsf_type_create_array(tsf_type_t *ele_type) { tsf_type_t *ret; if (ele_type == NULL) { return NULL; } ret = malloc(sizeof(tsf_type_t)); if (ret == NULL) { tsf_set_errno("Could not malloc tsf_type_t"); tsf_type_destroy(ele_type); return NULL; } tsf_atomic_count_init(&ret->ref_count, 1); ret->comment = NULL; ret->name = NULL; ret->version = 0; ret->kind_code = TSF_TK_ARRAY; ret->is_dynamic = ele_type->is_dynamic; ret->u.a.element_type = ele_type; tsf_type_recompute_hash(ret); ret->byte_size = 0; ret->memo_master = NULL; increment_type_balance(); /* printf("created an array type at %p\n",ret); */ return ret; } typedef tsf_bool_t (*append_func_t)(tsf_type_t **type, const char *name, tsf_type_t *ele_type); static tsf_type_t *create_somethingv(tsf_type_kind_t kind_code, append_func_t append, const char *first_name, va_list lst) { tsf_type_t *result; const char *name; tsf_type_t *type; result=tsf_type_create(kind_code); if (result==NULL) { return NULL; } name=first_name; while (name!=NULL) { type=va_arg(lst,tsf_type_t*); if (!append(&result, name, type)) { while (va_arg(lst,const char*)!=NULL) { tsf_type_destroy(va_arg(lst,tsf_type_t*)); } tsf_type_destroy(result); return NULL; } name=va_arg(lst,const char*); } return result; } tsf_type_t *tsf_type_create_structv(const char *first_name, va_list lst) { return create_somethingv(TSF_TK_STRUCT, tsf_struct_type_append, first_name, lst); } tsf_type_t *tsf_type_create_struct(const char *first_name, ...) { va_list lst; tsf_type_t *result; va_start(lst,first_name); result=tsf_type_create_structv(first_name,lst); va_end(lst); return result; } tsf_type_t *tsf_type_create_choicev(const char *first_name, va_list lst) { return create_somethingv(TSF_TK_CHOICE, tsf_choice_type_append, first_name, lst); } tsf_type_t *tsf_type_create_choice(const char *first_name, ...) { va_list lst; tsf_type_t *result; va_start(lst,first_name); result=tsf_type_create_choicev(first_name,lst); va_end(lst); return result; } static tsf_bool_t type_kind_verify(tsf_type_kind_t code, tsf_limits_t *limits, tsf_error_t error) { if (tsf_limits_is_type_kind_allowed(limits,code)) { return tsf_true; } tsf_set_error(error, "%s type is not allowed", tsf_type_kind_tsf_name(code)); return tsf_false; } tsf_type_t *tsf_type_read_rec(tsf_reader_t reader, void *arg, tsf_limits_t *limits, uint32_t depth) { tsf_type_t *ret; uint8_t kind_code; uint8_t comment_mask; if (depth==tsf_limits_depth_limit(limits)) { tsf_set_error(TSF_E_PARSE_ERROR, "Type depth limit exceeded while parsing"); return NULL; } if (!reader(arg,&kind_code,1)) { return NULL; } if (!type_kind_verify(kind_code,limits,TSF_E_PARSE_ERROR)) { return NULL; } BIG_META_CODE_SWITCH(kind_code) ret = malloc(sizeof(tsf_type_t)); if (ret==NULL) { tsf_set_errno("Could not malloc tsf_type_t object."); return NULL; } tsf_atomic_count_init(&ret->ref_count, 1); ret->comment = NULL; ret->name = NULL; ret->version = 0; ret->kind_code = kind_code; ret->memo_master = NULL; switch (ret->kind_code) { case TSF_TK_ARRAY: ret->u.a.element_type = tsf_type_read_rec(reader, arg, limits, depth + 1); if (ret->u.a.element_type == NULL) { free(ret); return NULL; } ret->is_dynamic = ret->u.a.element_type->is_dynamic; break; case TSF_TK_STRUCT: ret->u.s.tt = tsf_type_table_read("Struct", reader, arg, limits, tsf_limits_max_struct_size(limits), depth + 1); if (ret->u.s.tt == NULL) { free(ret); return NULL; } ret->is_dynamic = tsf_type_table_contains_dynamic(ret->u.s.tt); ret->u.s.has_size = tsf_false; ret->u.s.constructor = NULL; ret->u.s.pre_destructor = NULL; ret->u.s.destructor = NULL; break; case TSF_TK_CHOICE: ret->u.h.tt = tsf_type_table_read("Choice", reader, arg, limits, tsf_limits_max_choice_size(limits), depth + 1); if (ret->u.h.tt == NULL) { free(ret); return NULL; } ret->is_dynamic = tsf_type_table_contains_dynamic(ret->u.h.tt); ret->u.h.has_mapping = tsf_false; if (tsf_choice_type_get_num_elements(ret) >= 256) ret->u.h.choice_as_full_word = tsf_true; else ret->u.h.choice_as_full_word = tsf_false; break; default: tsf_set_error(TSF_E_PARSE_ERROR, "'" fui8 "' is not a valid kind type in " "tsf_type_read()", ret->kind_code); free(ret); return NULL; } increment_type_balance(); if (!reader(arg, &comment_mask, 1)) { tsf_type_destroy(ret); return NULL; } if ((comment_mask & 1)&& !tsf_limits_allow_comments(limits)) { tsf_set_error(TSF_E_PARSE_ERROR, "Encountered a commented type while parsing, but " "comments are not allowed"); tsf_type_destroy(ret); return NULL; } if ((comment_mask & 1)) { ret->comment = tsf_read_string(reader, arg, TSF_MAX_COMMENT_SIZE); if (ret->comment == NULL) { tsf_type_destroy(ret); return NULL; } } if ((comment_mask & 2)) { ret->name = tsf_read_string(reader, arg, TSF_MAX_NAME_SIZE); if (ret->name == NULL) { tsf_type_destroy(ret); return NULL; } } if ((comment_mask & 4)) { if (!reader(arg, &ret->version, sizeof(ret->version))) { tsf_type_destroy(ret); return NULL; } ret->version = ntohl(ret->version); } if ((comment_mask & 8)) { if (ret->kind_code == TSF_TK_CHOICE) { ret->u.h.choice_as_full_word = tsf_false; } } tsf_type_recompute_hash(ret); ret->byte_size = 0; /* printf("read a type at %p\n",ret); */ return ret; } tsf_type_t *tsf_type_read(tsf_reader_t reader, void *arg, tsf_limits_t *limits) { tsf_size_aware_rdr_t size_aware; tsf_type_t *result; check_init_types(); size_aware.reader=reader; size_aware.arg=arg; size_aware.limit=tsf_limits_max_type_size(limits); size_aware.name="type"; result=tsf_type_read_rec(tsf_size_aware_reader, &size_aware, limits, 0); if (result!=NULL) { result->byte_size= tsf_limits_max_type_size(limits) -size_aware.limit; } return result; } tsf_bool_t tsf_type_write(tsf_type_t *type, tsf_writer_t writer, void *arg) { uint8_t comment_mask; tsf_assert(tsf_atomic_count_value(&type->ref_count) > 0); if (!writer(arg,&type->kind_code,1)) { return tsf_false; } switch (type->kind_code) { case TSF_TK_ARRAY: if (!tsf_type_write(type->u.a.element_type,writer,arg)) { return tsf_false; } break; case TSF_TK_STRUCT: if (!tsf_type_table_write(type->u.s.tt, writer, arg)) { return tsf_false; } break; case TSF_TK_CHOICE: if (!tsf_type_table_write(type->u.h.tt, writer, arg)) { return tsf_false; } break; default: break; } if (tsf_type_kind_is_commentable(type->kind_code)) { comment_mask = 0; if (type->comment != NULL) { comment_mask |= 1; } if (type->name != NULL) { comment_mask |= 2; } if (type->version != 0) { comment_mask |= 4; } if (type->kind_code == TSF_TK_CHOICE && tsf_choice_type_get_num_elements(type) >= 256) { comment_mask |= 8; } if (!writer(arg, &comment_mask, 1)) { return tsf_false; } if (type->comment != NULL) { if (!tsf_write_string(writer, arg, type->comment)) { return tsf_false; } } if (type->name != NULL) { if (!tsf_write_string(writer, arg, type->name)) { return tsf_false; } } if (type->version != 0) { uint32_t version = htonl(type->version); if (!writer(arg, &version, sizeof(version))) { return tsf_false; } } } return tsf_true; } tsf_type_t *tsf_type_dup(tsf_type_t *type) { if (type == NULL) { return NULL; } BIG_META_CODE_SWITCH(type->kind_code) tsf_atomic_count_xadd(&type->ref_count, 1); increment_type_balance(); /* printf("dupped a type at %p\n",type); */ return type; } tsf_type_t *tsf_type_clone(tsf_type_t *type) { tsf_type_t *ret; if (type==NULL) { return NULL; } BIG_META_CODE_SWITCH(type->kind_code) if (type->kind_code==TSF_TK_ARRAY) { ret=tsf_type_create_array(tsf_type_clone(type->u.a.element_type)); } else { ret=tsf_type_create(type->kind_code); } if (ret==NULL) { return NULL; } if (tsf_type_get_comment(type)!=NULL) { ret->comment=strdup(tsf_type_get_comment(type)); if (ret->comment==NULL) { tsf_set_errno("Could not allocate comment"); tsf_type_destroy(ret); return NULL; } } if (tsf_type_get_name(type)!=NULL) { ret->name=strdup(tsf_type_get_name(type)); if (ret->name==NULL) { tsf_set_errno("Could not allocate name"); tsf_type_destroy(ret); return NULL; } } ret->version=type->version; if (type->kind_code==TSF_TK_STRUCT) { if (!tsf_type_table_copy(ret->u.s.tt,type->u.s.tt)) { tsf_type_destroy(ret); return NULL; } ret->u.s.has_size=type->u.s.has_size; ret->u.s.size=type->u.s.size; } else if (type->kind_code==TSF_TK_CHOICE) { if (!tsf_type_table_copy(ret->u.h.tt,type->u.h.tt)) { tsf_type_destroy(ret); return NULL; } ret->u.h.has_mapping=type->u.h.has_mapping; ret->u.h.in_place=type->u.h.in_place; ret->u.h.data_offset=type->u.h.data_offset; ret->u.h.value_offset=type->u.h.value_offset; ret->u.h.size=type->u.h.size; } ret->is_dynamic=type->is_dynamic; tsf_type_recompute_hash(ret); ret->byte_size=type->byte_size; return ret; } tsf_type_t *tsf_type_own_begin(tsf_type_t *type) { if (tsf_atomic_count_value(&type->ref_count) == 1) { return type; } return tsf_type_clone(type); } void tsf_type_own_commit(tsf_type_t **dst, tsf_type_t *ret) { if (*dst == ret) { return; } tsf_type_destroy(*dst); *dst = ret; } void tsf_type_own_abort(tsf_type_t *dst, tsf_type_t *ret) { if (dst == ret) { return; } tsf_type_destroy(ret); } void tsf_type_destroy(tsf_type_t *type) { /* printf("tsf_type: destroy: %p\n", type); */ if (is_singleton(type->kind_code)) return; if (tsf_atomic_count_xsub(&type->ref_count, 1) > 1) { /* printf("didn't destroy a type at %p\n",type); */ return; } /* printf("tsf_type: really destroy: %p\n", type); */ if (type->memo_master != NULL) { if (type->memo_master == type) { /* need to remove myself from the memo table. */ tsf_type_t *to_delete = type; tsf_mutex_lock(&memo_lock); tsf_st_delete(memo_table, (char**)&to_delete, NULL); tsf_mutex_unlock(&memo_lock); } else { /* relinquish ownership of the memo master. */ /* printf("Destroying memo master: %p\n", type->memo_master); */ tsf_type_destroy(type->memo_master); } } switch (type->kind_code) { case TSF_TK_ARRAY: tsf_type_destroy(type->u.a.element_type); break; case TSF_TK_STRUCT: tsf_type_table_destroy(type->u.s.tt); break; case TSF_TK_CHOICE: tsf_type_table_destroy(type->u.h.tt); default: break; } tsf_atomic_count_destroy(&type->ref_count); if (type->comment!=NULL) { free(type->comment); } if (type->name!=NULL) { free(type->name); } free(type); decrement_type_balance(); /* printf("destroyed a type at %p\n",type); */ } static tsf_bool_t type_verify_rec(tsf_type_t *type, tsf_limits_t *limits, uint32_t depth) { uint32_t i; if (depth==tsf_limits_depth_limit(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Type exceeds depth limit"); return tsf_false; } if (!type_kind_verify(type->kind_code,limits,TSF_E_NOT_ALLOWED)) { return tsf_false; } if (type->comment!=NULL && !tsf_limits_allow_comments(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Encountered comments, but commented are not allowed"); return tsf_false; } switch (type->kind_code) { case TSF_TK_ARRAY: if (!type_verify_rec(type->u.a.element_type, limits, depth+1)) { return tsf_false; } break; case TSF_TK_STRUCT: if (tsf_struct_type_get_num_elements(type)> tsf_limits_max_struct_size(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Structure type size limit exceeded"); return tsf_false; } for (i=0; icomment!=NULL && !tsf_limits_allow_comments(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Encountered comments, but commented are " "not allowed"); return tsf_false; } if (!type_verify_rec(tsf_struct_type_get_element(type,i)->type, limits, depth+1)) { return tsf_false; } } break; case TSF_TK_CHOICE: if (tsf_choice_type_get_num_elements(type)> tsf_limits_max_choice_size(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Choice type size limit exceeded"); return tsf_false; } for (i=0; itype, limits, depth+1)) { return tsf_false; } } break; default: break; } return tsf_true; } uint32_t tsf_type_get_byte_size(tsf_type_t *type) { uint32_t size=type->byte_size; if (size==0) { tsf_bool_t result=tsf_type_write(type, tsf_size_calc_writer, &size); tsf_assert(result); type->byte_size=size; } return size; } tsf_bool_t tsf_type_verify(tsf_type_t *type, tsf_limits_t *limits) { if (limits==NULL) { return tsf_true; } if (tsf_type_get_byte_size(type)> tsf_limits_max_type_size(limits)) { tsf_set_error(TSF_E_NOT_ALLOWED, "Type is larger than type size limit"); return tsf_false; } /* verify all else */ return type_verify_rec(type,limits,0); } int tsf_type_get_hash(tsf_type_t *type) { return type->hash_code; } void tsf_type_recompute_hash(tsf_type_t *type) { type->hash_code = type->kind_code; switch (type->kind_code) { case TSF_TK_ARRAY: type->hash_code += tsf_type_get_hash(type->u.a.element_type); break; case TSF_TK_STRUCT: type->hash_code += tsf_type_table_get_hash(type->u.s.tt); break; case TSF_TK_CHOICE: type->hash_code += tsf_type_table_get_hash(type->u.h.tt) + type->u.h.choice_as_full_word; break; default: break; } } tsf_bool_t tsf_type_compare(tsf_type_t *a, tsf_type_t *b) { if (a == b) { return tsf_true; } if (a->kind_code != b->kind_code) { return tsf_false; } switch (a->kind_code) { case TSF_TK_ARRAY: if (!tsf_type_compare(a->u.a.element_type, b->u.a.element_type)) { return tsf_false; } break; case TSF_TK_STRUCT: if (!tsf_type_table_compare(a->u.s.tt, b->u.s.tt)) { return tsf_false; } break; case TSF_TK_CHOICE: if (!tsf_type_table_compare(a->u.h.tt, b->u.h.tt)) { return tsf_false; } if (a->u.h.choice_as_full_word != b->u.h.choice_as_full_word) { return tsf_false; } break; default: break; } return tsf_true; } tsf_type_t *tsf_type_memo(tsf_type_t *type) { tsf_type_t *result; result = type->memo_master; if (result) { return result; } tsf_mutex_lock(&memo_lock); if (tsf_st_lookup(memo_table, (char*)type, (void**)&result)) { type->memo_master = tsf_type_dup(result); } else { if (tsf_st_add_direct(memo_table, (char*)type, (void*)type) < 0) { tsf_set_errno("Could not memoize type"); result = NULL; } else { type->memo_master = type; result = type; } } tsf_mutex_unlock(&memo_lock); return result; } tsf_bool_t tsf_type_is_dynamic(tsf_type_t *type) { return type->is_dynamic; } tsf_bool_t tsf_type_instanceof(tsf_type_t *one, tsf_type_t *two) { if (two->kind_code==TSF_TK_VOID) { return tsf_true; } if (tsf_type_kind_is_primitive(one->kind_code) || one->kind_code==TSF_TK_BIT) { return tsf_type_kind_is_primitive(two->kind_code) || two->kind_code==TSF_TK_BIT; } if (one->kind_code!=two->kind_code) { return tsf_false; } switch (one->kind_code) { case TSF_TK_ARRAY: return tsf_type_instanceof(one->u.a.element_type, two->u.a.element_type); case TSF_TK_STRUCT: { uint32_t i; for (i=0;ioptional) { continue; } if (n2==NULL || n2->optional) { return tsf_false; } if (!tsf_type_instanceof( tsf_named_type_get_type(n2), tsf_named_type_get_type(n))) { return tsf_false; } } } break; case TSF_TK_CHOICE: { uint32_t i; for (i=0;ikind_code; } uint32_t tsf_type_get_static_size(tsf_type_t *type) { switch (type->kind_code) { case TSF_TK_ARRAY: return UINT32_MAX; case TSF_TK_STRUCT: { uint32_t i, num_bits = 0, ret = 0; for (i = 0; i < tsf_struct_type_get_num_elements(type); ++i) { uint32_t tmp; if (tsf_struct_type_get_element(type,i)->type->kind_code == TSF_TK_BIT) { ++num_bits; continue; } tmp = tsf_type_get_static_size( tsf_struct_type_get_element(type, i)->type); if (tmp == UINT32_MAX) { return UINT32_MAX; } ret += tmp; } ret += ((num_bits + 7) >> 3); return ret; } case TSF_TK_CHOICE: if (tsf_choice_type_has_non_void(type)) { return UINT32_MAX; } else { if (tsf_choice_type_get_num_elements(type) >= 256) { if (type->u.h.choice_as_full_word) { return 4; } else { return UINT32_MAX; } } else { return 1; } } break; case TSF_TK_BIT: return 1; case TSF_TK_VOID: return 0; case TSF_TK_STRING: return UINT32_MAX; case TSF_TK_ANY: return UINT32_MAX; case TSF_TK_INTEGER: case TSF_TK_LONG: return UINT32_MAX; default: return tsf_primitive_type_kind_size_of(type->kind_code); } } const char *tsf_type_get_comment(tsf_type_t *type) { return type->comment; } tsf_bool_t tsf_type_has_comment(tsf_type_t *type) { return type->comment != NULL; } tsf_bool_t tsf_type_set_comment(tsf_type_t **type, const char *comment) { char *comment_copy; tsf_type_t *ret; if (!tsf_type_kind_is_commentable((*type)->kind_code)) { tsf_set_error(TSF_E_BAD_TYPE_KIND, "The given type is not commentable"); return tsf_false; } if (strlen(comment) + 1 > TSF_MAX_COMMENT_SIZE) { tsf_set_error(TSF_E_STR_OVERFLOW, "String '%s' is too long to be used as a type comment", comment); return tsf_false; } comment_copy = strdup(comment); if (comment_copy == NULL) { tsf_set_errno("Could not stdup comment"); return tsf_false; } ret = tsf_type_own_begin(*type); if (ret == NULL) { free(comment_copy); return tsf_false; } if (ret->comment != NULL) { free(ret->comment); } ret->comment = comment_copy; ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } const char *tsf_type_get_name(tsf_type_t *type) { return type->name; } tsf_bool_t tsf_type_has_name(tsf_type_t *type) { return type->name != NULL; } tsf_bool_t tsf_type_set_name(tsf_type_t **type, const char *name) { char *name_copy; tsf_type_t *ret; if (!tsf_type_kind_is_commentable((*type)->kind_code)) { tsf_set_error(TSF_E_BAD_TYPE_KIND, "The given type is not commentable"); return tsf_false; } if (strlen(name) + 1 > TSF_MAX_NAME_SIZE) { tsf_set_error(TSF_E_STR_OVERFLOW, "String '%s' is too long to be used as a type name", name); return tsf_false; } name_copy = strdup(name); if (name_copy == NULL) { tsf_set_errno("Could not strdup name"); return tsf_false; } ret = tsf_type_own_begin(*type); if (ret == NULL) { free(name_copy); return tsf_false; } if (ret->name != NULL) { free(ret->name); } ret->name = name_copy; ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_type_has_version(tsf_type_t *type) { return type->version != 0; } uint32_t tsf_type_get_raw_version(tsf_type_t *type) { return type->version; } uint16_t tsf_type_get_major_version(tsf_type_t *type) { return (type->version >> 16) & 0xffff; } uint8_t tsf_type_get_minor_version(tsf_type_t *type) { return (type->version >> 8) & 0xff; } uint8_t tsf_type_get_patch_version(tsf_type_t *type) { return (type->version >> 0) & 0xff; } tsf_bool_t tsf_type_set_raw_version(tsf_type_t **type, uint32_t version) { tsf_type_t *ret; if (!tsf_type_kind_is_commentable((*type)->kind_code)) { tsf_set_error(TSF_E_BAD_TYPE_KIND, "The given type is not commentable"); return tsf_false; } ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } ret->version = version; ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_type_set_version(tsf_type_t **type, uint16_t major, uint8_t minor, uint8_t patch) { return tsf_type_set_raw_version(type, ((((uint32_t)major) & 0xffff) << 16) | ((((uint32_t)minor) & 0xff) << 8) | ((((uint32_t)patch) & 0xff) << 0)); } tsf_type_t *tsf_array_type_get_element_type(tsf_type_t *type) { return type->u.a.element_type; } tsf_bool_t tsf_struct_type_append(tsf_type_t **type, const char *name, tsf_type_t *ele_type) { tsf_type_t *ret = tsf_type_own_begin(*type); if (ret == NULL) { if (ele_type!=NULL) { tsf_type_destroy(ele_type); } return tsf_false; } if (!tsf_type_table_append(ret->u.s.tt,name,ele_type)) { tsf_type_own_abort(*type, ret); return tsf_false; } if (ele_type->is_dynamic) { ret->is_dynamic=tsf_true; } /* depending on the invariant that the hash code of the type table is * just the sum of the hash codes of the named types. */ ret->hash_code += tsf_named_type_get_hash( tsf_type_table_find_by_index(ret->u.s.tt, tsf_type_table_get_num_elements(ret->u.s.tt)-1)); ret->byte_size=0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_struct_type_remove(tsf_type_t **type, const char *name) { tsf_type_t *ret; tsf_named_type_t *named_type; int hash_code; ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } named_type = tsf_type_table_find_by_name(ret->u.s.tt, name); if (named_type == NULL) { tsf_type_own_abort(*type, ret); return tsf_false; } /* depending on the invariant that the hash code of the type table * is just the sum of the hash codes of the named types. */ hash_code = tsf_named_type_get_hash(named_type); if (!tsf_type_table_remove(ret->u.s.tt, name)) { tsf_type_own_abort(*type, ret); return tsf_false; } ret->hash_code -= hash_code; ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_struct_type_set_comment(tsf_type_t **type, const char *name, const char *comment) { tsf_type_t *ret; tsf_named_type_t *n; ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } n = tsf_struct_type_find_node(ret,name); if (n == NULL) { tsf_type_own_abort(*type, ret); return tsf_false; } if (!tsf_named_type_set_comment(n, comment)) { tsf_type_own_abort(*type, ret); return tsf_false; } ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_struct_type_set_optional(tsf_type_t **type, const char *name, tsf_bool_t optional) { tsf_type_t *ret; tsf_named_type_t *n; ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } n = tsf_struct_type_find_node(ret, name); if (n == NULL) { tsf_type_own_abort(*type, ret); return tsf_false; } tsf_named_type_set_optional(n, optional); ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_type_t *tsf_struct_type_find(tsf_type_t *type, const char *name) { tsf_named_type_t *n = tsf_struct_type_find_node(type, name); if (n == NULL) { return NULL; } return n->type; } tsf_named_type_t *tsf_struct_type_find_node(tsf_type_t *type, const char *name) { return tsf_type_table_find_by_name(type->u.s.tt, name); } uint32_t tsf_struct_type_get_num_elements(tsf_type_t *type) { return tsf_type_table_get_num_elements(type->u.s.tt); } tsf_named_type_t *tsf_struct_type_get_element(tsf_type_t *type, uint32_t index) { return tsf_type_table_find_by_index(type->u.s.tt, index); } const char *tsf_struct_type_get_element_name(tsf_type_t *type, uint32_t index) { return tsf_named_type_get_name( tsf_struct_type_get_element(type, index)); } tsf_type_t *tsf_struct_type_get_element_type(tsf_type_t *type, uint32_t index) { return tsf_named_type_get_type( tsf_struct_type_get_element(type, index)); } tsf_bool_t tsf_choice_type_append(tsf_type_t **type, const char *name, tsf_type_t *ele_type) { tsf_type_t *ret; if (!strcmp(name, "unknown")) { tsf_set_error(TSF_E_NAME_COLLISION, "The name 'unknown' is reserved in choice types"); if (ele_type != NULL) { tsf_type_destroy(ele_type); } return tsf_false; } ret = tsf_type_own_begin(*type); if (ret == NULL) { if (ele_type != NULL) { tsf_type_destroy(ele_type); } return tsf_false; } if (!tsf_type_table_append(ret->u.h.tt, name, ele_type)) { tsf_type_own_abort(*type, ret); return tsf_false; } if (ele_type->is_dynamic) { ret->is_dynamic = tsf_true; } /* depending on the invariant that the hash code of the type table is * just the sum of the hash codes of the named types. */ ret->hash_code += tsf_named_type_get_hash( tsf_type_table_find_by_index(ret->u.h.tt, tsf_type_table_get_num_elements(ret->u.h.tt) - 1)); ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_choice_type_remove(tsf_type_t **type, const char *name) { tsf_type_t *ret; tsf_named_type_t *named_type; int hash_code; ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } named_type = tsf_type_table_find_by_name(ret->u.h.tt, name); if (named_type == NULL) { tsf_type_own_abort(*type, ret); return tsf_false; } /* depending on the invariant that the hash code of the type table * is just the sum of the hash codes of the named types. */ hash_code = tsf_named_type_get_hash(named_type); if (!tsf_type_table_remove(ret->u.h.tt, name)) { tsf_type_own_abort(*type, ret); return tsf_false; } ret->hash_code -= hash_code; ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_bool_t tsf_choice_type_set_comment(tsf_type_t **type, const char *name, const char *comment) { tsf_type_t *ret; tsf_named_type_t *n; ret = tsf_type_own_begin(*type); if (ret == NULL) { return tsf_false; } n = tsf_choice_type_find_node(ret,name); if (n == NULL) { tsf_type_own_abort(*type, ret); return tsf_false; } if (!tsf_named_type_set_comment(n, comment)) { tsf_type_own_abort(*type, ret); return tsf_false; } ret->byte_size = 0; tsf_type_own_commit(type, ret); return tsf_true; } tsf_type_t *tsf_choice_type_find(tsf_type_t *type,const char *name) { tsf_named_type_t *n=tsf_choice_type_find_node(type,name); if (n==NULL) { return NULL; } return n->type; } tsf_named_type_t *tsf_choice_type_find_node(tsf_type_t *type, const char *name) { return tsf_type_table_find_by_name(type->u.h.tt, name); } uint32_t tsf_choice_type_get_num_elements(tsf_type_t *type) { return tsf_type_table_get_num_elements(type->u.h.tt); } tsf_named_type_t *tsf_choice_type_get_element(tsf_type_t *type, uint32_t index) { return tsf_type_table_find_by_index(type->u.h.tt, index); } const char *tsf_choice_type_get_element_name(tsf_type_t *type, uint32_t index) { return tsf_named_type_get_name( tsf_choice_type_get_element(type, index)); } tsf_type_t *tsf_choice_type_get_element_type(tsf_type_t *type, uint32_t index) { return tsf_named_type_get_type( tsf_choice_type_get_element(type, index)); } tsf_bool_t tsf_choice_type_has_non_void(tsf_type_t *type) { uint32_t i; for (i=0;iu.h.tt);++i) { if (tsf_type_table_find_by_index(type->u.h.tt,i)-> type->kind_code!=TSF_TK_VOID) { return tsf_true; } } return tsf_false; } tsf_bool_t tsf_choice_type_uses_same_indices(tsf_type_t *a, tsf_type_t *b) { uint32_t i; if (tsf_choice_type_get_num_elements(a)> tsf_choice_type_get_num_elements(b)) { return tsf_false; } for (i=0;i