blob: 7723ea73cb3497f1dfbac379f479873b7d93c125 [file] [log] [blame]
/*
* 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 <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_PTHREAD
#include <pthread.h>
#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;
i<tsf_struct_type_get_num_elements(type);
++i) {
if (tsf_struct_type_get_element(type,i)->comment!=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;
i<tsf_choice_type_get_num_elements(type);
++i) {
if (!type_verify_rec(tsf_choice_type_get_element(type,i)->type,
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;i<tsf_struct_type_get_num_elements(two);++i) {
tsf_named_type_t *n=tsf_struct_type_get_element(two,i);
tsf_named_type_t *n2=
tsf_struct_type_find_node(one,
tsf_named_type_get_name(n));
if (n->optional) {
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;i<tsf_choice_type_get_num_elements(one);++i) {
tsf_named_type_t *n=tsf_choice_type_get_element(one,i);
tsf_named_type_t *n2=
tsf_choice_type_find_node(two,
tsf_named_type_get_name(n));
if (n2==NULL) {
continue;
}
if (!tsf_type_instanceof(
tsf_named_type_get_type(n),
tsf_named_type_get_type(n2))) {
return tsf_false;
}
}
}
default:
break;
}
return tsf_true;
}
tsf_type_kind_t tsf_type_get_kind_code(tsf_type_t *type) {
return type->kind_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;i<tsf_type_table_get_num_elements(type->u.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<tsf_choice_type_get_num_elements(a);++i) {
tsf_named_type_t *n=tsf_choice_type_get_element(a,i);
tsf_named_type_t *n2=tsf_choice_type_get_element(b,i);
if (strcmp(tsf_named_type_get_name(n),
tsf_named_type_get_name(n2))) {
return tsf_false;
}
}
return tsf_true;
}