blob: dd8a17a1378093553b7da5863e329f015e61a28e [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005, 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 <string.h>
tsf_type_table_t *tsf_type_table_create() {
tsf_type_table_t *ret = malloc(sizeof(tsf_type_table_t));
if (ret == NULL) {
tsf_set_errno("Could not malloc tsf_type_table_t");
return NULL;
}
ret->elements = NULL;
ret->num_elements = 0;
ret->ele_table = tsf_st_init_strtable();
if (ret->ele_table == NULL) {
tsf_set_errno("Could not tsf_st_init_strtable()");
free(ret);
return NULL;
}
return ret;
}
tsf_bool_t tsf_type_table_copy(tsf_type_table_t *dest,
tsf_type_table_t *src) {
uint32_t i;
for (i = 0; i < tsf_type_table_get_num_elements(src); ++i) {
tsf_named_type_t *n = tsf_type_table_find_by_index(src, i), *n2;
tsf_type_t *new_type = tsf_type_dup(n->type);
if (new_type == NULL) {
return tsf_false;
}
if (!tsf_type_table_append(dest, n->name, new_type)) {
return tsf_false;
}
/* copy the optional attribute, offset info manually */
n2 = tsf_type_table_find_by_index(dest,
tsf_type_table_get_num_elements(dest) - 1);
n2->optional = n->optional;
if (n->has_offset) {
n2->has_offset = tsf_true;
n2->offset = n->offset;
}
/* copy the comment manually */
if (n->comment != NULL) {
n2->comment = strdup(n->comment);
if (n2->comment == NULL) {
tsf_set_errno("Could not allocate comment");
return tsf_false;
}
}
}
return tsf_true;
}
void tsf_type_table_destroy(tsf_type_table_t *tt) {
uint32_t i;
for (i = 0; i < tt->num_elements; ++i) {
tsf_named_type_destroy(tt->elements[i]);
}
if (tt->elements != NULL) {
free(tt->elements);
}
tsf_st_free_table(tt->ele_table);
free(tt);
}
tsf_type_table_t *tsf_type_table_read(const char *name,
tsf_reader_t reader,
void *arg,
tsf_limits_t *limits,
uint32_t size_limit,
uint32_t depth) {
tsf_type_table_t *ret = tsf_type_table_create();
uint32_t i, num;
if (ret == NULL) {
return NULL;
}
if (!reader(arg, &num, sizeof(num))) {
tsf_type_table_destroy(ret);
return NULL;
}
num = ntohl(num);
if (num > size_limit) {
tsf_set_error(TSF_E_PARSE_ERROR,
"%s size exceeds limit while parsing", name);
tsf_type_table_destroy(ret);
return NULL;
}
for (i = 0; i < num; ++i) {
uint8_t ui8_tmp;
char *str;
/* parse the string */
str = tsf_read_string(reader, arg, TSF_STR_SIZE);
if (str == NULL) {
tsf_type_table_destroy(ret);
return NULL;
}
/* now do other things */
if (!tsf_type_table_append(ret,
str,
tsf_type_read_rec(reader,
arg,
limits,
depth))) {
tsf_type_table_destroy(ret);
free(str);
return NULL;
}
free(str);
if (!reader(arg, &ui8_tmp, 1)) {
tsf_type_table_destroy(ret);
return NULL;
}
if (ui8_tmp & ~3) {
tsf_set_error(TSF_E_PARSE_ERROR,
"Flags field contains bits I don't recognize: " fui8,
ui8_tmp);
tsf_type_table_destroy(ret);
return NULL;
}
ret->elements[ret->num_elements - 1]->optional =
(ui8_tmp & 2 ? tsf_true : tsf_false);
if (ui8_tmp & 1) {
/* parse a comment string. it can be arbitrarily long, so
this parsing code is somewhat of a pain in the ass to
write. */
if (!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_table_destroy(ret);
return NULL;
}
str = tsf_read_string(reader, arg, TSF_MAX_COMMENT_SIZE);
if (str == NULL) {
tsf_type_table_destroy(ret);
return NULL;
}
ret->elements[ret->num_elements - 1]->comment = str;
}
}
return ret;
}
tsf_bool_t tsf_type_table_write(tsf_type_table_t *tt,
tsf_writer_t writer,
void *arg) {
uint32_t ui32_tmp = htonl(tt->num_elements);
uint8_t ui8_tmp;
uint32_t i;
if (!writer(arg, &ui32_tmp, sizeof(ui32_tmp))) {
return tsf_false;
}
for (i = 0; i < tt->num_elements; ++i) {
tsf_named_type_t *n = tt->elements[i];
if (!tsf_write_string(writer, arg, tsf_named_type_get_name(n))) {
return tsf_false;
}
if (!tsf_type_write(tsf_named_type_get_type(n),
writer, arg)) {
return tsf_false;
}
ui8_tmp = 0;
if (n->comment != NULL) {
ui8_tmp |= 1;
}
if (n->optional) {
ui8_tmp |= 2;
}
if (!writer(arg, &ui8_tmp, 1)) {
return tsf_false;
}
if (n->comment != NULL) {
if (!tsf_write_string(writer, arg, n->comment)) {
return tsf_false;
}
}
}
return tsf_true;
}
int tsf_type_table_get_hash(tsf_type_table_t *tt) {
int ret = 0;
uint32_t i;
for (i = 0; i < tt->num_elements; ++i) {
ret += tsf_named_type_get_hash(tt->elements[i]);
}
return ret;
}
tsf_bool_t tsf_type_table_contains_dynamic(tsf_type_table_t *tt) {
uint32_t i;
for (i = 0; i < tt->num_elements; ++i) {
if (tsf_type_is_dynamic(tt->elements[i]->type)) {
return tsf_true;
}
}
return tsf_false;
}
tsf_bool_t tsf_type_table_compare(tsf_type_table_t *a,
tsf_type_table_t *b) {
uint32_t i;
if (a->num_elements != b->num_elements) {
return tsf_false;
}
for (i = 0; i < a->num_elements; ++i) {
if (strcmp(a->elements[i]->name,
b->elements[i]->name)) {
return tsf_false;
}
if (a->elements[i]->optional
!= b->elements[i]->optional) {
return tsf_false;
}
if (!tsf_type_compare(a->elements[i]->type,
b->elements[i]->type)) {
return tsf_false;
}
}
return tsf_true;
}
tsf_bool_t tsf_type_table_append(tsf_type_table_t *tt,
const char *name,
tsf_type_t *ele_type) {
tsf_named_type_t *n;
if (ele_type == NULL) {
return tsf_false;
}
if (tsf_st_lookup(tt->ele_table, (char*)name, NULL)) {
tsf_set_error(TSF_E_NAME_COLLISION,
"Name '%s' is already used by another element",
name);
tsf_type_destroy(ele_type);
return tsf_false;
}
if (tt->elements == NULL) {
tt->elements = malloc(sizeof(tsf_named_type_t*));
if (tt->elements == NULL) {
tsf_set_errno("Could not malloc tsf_named_type_t*");
tsf_type_destroy(ele_type);
return tsf_false;
}
} else {
tsf_named_type_t **new_array = realloc(tt->elements,
sizeof(tsf_named_type_t*) *
(tt->num_elements + 1));
if (new_array==NULL) {
tsf_set_errno("Could not really array of tsf_named_type_t*");
tsf_type_destroy(ele_type);
return tsf_false;
}
tt->elements = new_array;
}
n = tsf_named_type_create(name,
ele_type,
tt->num_elements);
if (n == NULL) {
return tsf_false;
}
if (tsf_st_insert(tt->ele_table, n->name, n) < 0) {
tsf_set_errno("Failed to tsf_st_insert()");
tsf_type_destroy(ele_type);
free(n);
return tsf_false;
}
tt->elements[tt->num_elements++] = n;
return tsf_true;
}
tsf_bool_t tsf_type_table_remove(tsf_type_table_t *tt,
const char *name) {
char *to_delete;
void *old_value;
tsf_named_type_t *n;
to_delete = (char*)name;
old_value = NULL;
tsf_st_delete(tt->ele_table, &to_delete, &old_value);
if (old_value == NULL) {
tsf_set_error(TSF_E_ELE_NOT_FOUND, "Could not find \'%s\'", name);
return tsf_false;
}
n = (tsf_named_type_t*)old_value;
tt->elements[n->index] = tt->elements[--tt->num_elements];
tsf_named_type_destroy(n);
return tsf_true;
}
tsf_named_type_t *tsf_type_table_find_by_name(tsf_type_table_t *tt,
const char *name) {
tsf_named_type_t *n;
if (!tsf_st_lookup(tt->ele_table, (char*)name, (void**)&n)) {
tsf_set_error(TSF_E_ELE_NOT_FOUND, "Could not find \'%s\'", name);
return tsf_false;
}
return n;
}
tsf_named_type_t *tsf_type_table_find_by_index(tsf_type_table_t *tt,
uint32_t index) {
return tt->elements[index];
}
uint32_t tsf_type_table_get_num_elements(tsf_type_table_t *tt) {
return tt->num_elements;
}