diff --git a/src/c/libstl/stla_read.c b/src/c/libstl/stla_read.c index 1c067b8..cec2739 100644 --- a/src/c/libstl/stla_read.c +++ b/src/c/libstl/stla_read.c @@ -7,7 +7,8 @@ #include #include -typedef struct foug_stream_fwd_iterator +/* foug_stream_fwd_iterator */ +struct foug_stream_fwd_iterator { foug_stream_t* stream; char* buffer; @@ -16,15 +17,61 @@ typedef struct foug_stream_fwd_iterator void* cookie; void (*stream_read_hook)(struct foug_stream_fwd_iterator* it); -} foug_stream_fwd_iterator_t; +}; +typedef struct foug_stream_fwd_iterator + foug_stream_fwd_iterator_t; -typedef struct foug_stream_fwd_iterator_stla_cookie +/* foug_string_buffer */ +struct foug_string_buffer +{ + char* data; + size_t max_len; + size_t len; +}; +typedef struct foug_string_buffer + foug_string_buffer_t; + +/* foug_stream_fwd_iterator_stla_cookie */ +struct foug_stream_fwd_iterator_stla_cookie { foug_task_control_t* task_control; size_t stream_data_size; size_t stream_offset; foug_bool_t is_stop_requested; -} foug_stream_fwd_iterator_stla_cookie_t; +}; +typedef struct foug_stream_fwd_iterator_stla_cookie + foug_stream_fwd_iterator_stla_cookie_t; + +/* foug_stla_token */ +enum foug_stla_token +{ + ENDFACET_token, + ENDLOOP_token, + ENDSOLID_token, + FACET_token, + FLOAT_token, + ID_token, + LOOP_token, + NORMAL_token, + OUTER_token, + SOLID_token, + VERTEX_token, + unknown_token +}; +typedef enum foug_stla_token foug_stla_token_t; + +/* foug_stla_parse_data */ +struct foug_stla_parse_data +{ + foug_stla_token_t token; + foug_bool_t error; + foug_stream_fwd_iterator_t stream_iterator; + foug_stream_fwd_iterator_stla_cookie_t stream_iterator_cookie; + foug_string_buffer_t string_buffer; + foug_stla_geom_input_t* geom; +}; +typedef struct foug_stla_parse_data + foug_stla_parse_data_t; static void foug_stream_fwd_iterator_stla_read_hook(foug_stream_fwd_iterator_t* it) { @@ -61,6 +108,7 @@ static char* next_char(foug_stream_fwd_iterator_t* it) if (foug_stream_error(it->stream) != 0 || foug_stream_at_end(it->stream)) return NULL; + /* Read next chunk of data */ char_count_read = foug_stream_read(it->stream, it->buffer, sizeof(char), it->buffer_size); if (foug_stream_error(it->stream) != 0) { @@ -76,7 +124,7 @@ static char* next_char(foug_stream_fwd_iterator_t* it) } } -void foug_stream_fwd_iterator_init(foug_stream_fwd_iterator_t* it) +static void foug_stream_fwd_iterator_init(foug_stream_fwd_iterator_t* it) { it->buffer_offset = it->buffer_size; /* This will cause the first call to foug_stream_read() */ next_char(it); @@ -89,33 +137,6 @@ static void skip_spaces(foug_stream_fwd_iterator_t* it) curr_char = next_char(it); } -static int eat_token(foug_stream_fwd_iterator_t* it, const char* token) -{ - uint32_t i; - const char* stream_curr_char; - - if (token == NULL || current_char(it) == NULL) - return -1; - - skip_spaces(it); - stream_curr_char = current_char(it); - - for (i = 0; token[i] != 0 && stream_curr_char != NULL; ++i) { - if (token[i] != *stream_curr_char) - return -2; - stream_curr_char = next_char(it); - } - - return token[i] != 0 ? -3 : 0; -} - -typedef struct foug_string_buffer -{ - char* data; - size_t max_len; - size_t len; -} foug_string_buffer_t; - static int eat_string(foug_stream_fwd_iterator_t* it, foug_string_buffer_t* str_buffer) { size_t i; @@ -150,83 +171,288 @@ static int eat_string(foug_stream_fwd_iterator_t* it, foug_string_buffer_t* str_ return -3; } -static int read_real32(foug_stream_fwd_iterator_t* it, - foug_string_buffer_t* str_buffer, - foug_real32_t* value_ptr) +static int get_real32(const char* str, foug_real32_t* value_ptr) { char* conv_end_ptr; /* for strtod() */ - if (eat_string(it, str_buffer) != 0) + *value_ptr = (foug_real32_t)strtod(str, &conv_end_ptr); + if (conv_end_ptr == str || errno == ERANGE) return -1; - *value_ptr = (foug_real32_t)strtod(str_buffer->data, &conv_end_ptr); - if (conv_end_ptr == str_buffer->data || errno == ERANGE) - return -2; return 0; } -static int read_coords(foug_stream_fwd_iterator_t* it, - foug_string_buffer_t* str_buffer, - foug_stl_coords_t* coords_ptr) +foug_bool_t parsing_can_continue(const foug_stla_parse_data_t* data) { - if (read_real32(it, str_buffer, &coords_ptr->x) != 0) - return -1; - if (read_real32(it, str_buffer, &coords_ptr->y) != 0) - return -1; - if (read_real32(it, str_buffer, &coords_ptr->z) != 0) - return -1; - return 0; + if (data->error || data->stream_iterator_cookie.is_stop_requested) + return 0; + return 1; } -static int eat_facet(foug_stream_fwd_iterator_t* it, - foug_string_buffer_t* str_buffer, - foug_stl_triangle_t* triangle) +static const char* current_token_as_identifier(const foug_stla_parse_data_t* data) { - /* Try to eat "facet" */ - if (eat_string(it, str_buffer) != 0) - return -1; - if (strcmp(str_buffer->data, "facet") != 0) /* Maybe "endsolid" */ - return 1; + return data->token == ID_token ? data->string_buffer.data : ""; +} - /* Read normal */ - if (eat_token(it, "normal") != 0) - return -2; - if (read_coords(it, str_buffer, &triangle->normal) != 0) - return -3; +static int get_current_token_as_real32(const foug_stla_parse_data_t* data, foug_real32_t* value) +{ + if (data->token == FLOAT_token) + return get_real32(data->string_buffer.data, value); + return -3; +} - /* Try to eat "outer loop" */ - if (eat_token(it, "outer") != 0) - return -2; - if (eat_token(it, "loop") != 0) - return -2; +static void parsing_error(foug_stla_parse_data_t* data) +{ + data->error = 1; + data->token = unknown_token; +} - /* Read vertex 1 */ - if (eat_token(it, "vertex") != 0) - return -2; - if (read_coords(it, str_buffer, &triangle->v1) != 0) - return -3; +static void parsing_advance(foug_stla_parse_data_t* data) +{ + const char* str = data->string_buffer.data; - /* Read vertex 2 */ - if (eat_token(it, "vertex") != 0) - return -2; - if (read_coords(it, str_buffer, &triangle->v2) != 0) - return -3; + if (!parsing_can_continue(data)) + return; - /* Read vertex 3 */ - if (eat_token(it, "vertex") != 0) - return -2; - if (read_coords(it, str_buffer, &triangle->v3) != 0) - return -3; + data->token = unknown_token; + if (eat_string(&data->stream_iterator, &data->string_buffer) == 0) { + const size_t str_len = data->string_buffer.len; - /* Try to eat "endloop" */ - if (eat_token(it, "endloop") != 0) - return -2; + if (str_len >= 7 && strncmp(str, "end", 3) == 0) { /* Might be "end..." token */ + switch (str[3]) { + case 'f': + if (strcmp(str + 4, "acet") == 0) + data->token = ENDFACET_token; + break; + case 'l': + if (strcmp(str + 4, "oop") == 0) + data->token = ENDLOOP_token; + break; + case 's': + if (strcmp(str + 4, "olid") == 0) + data->token = ENDSOLID_token; + break; + default: + data->token = ID_token; + } /* end switch */ + } + else if (str_len >= 4) { + switch (str[0]) { + case 'f': + if (strcmp(str + 1, "acet") == 0) + data->token = FACET_token; + break; + case 'l': + if (strcmp(str + 1, "oop") == 0) + data->token = LOOP_token; + break; + case 'n': + if (strcmp(str + 1, "ormal") == 0) + data->token = NORMAL_token; + break; + case 'o': + if (strcmp(str + 1, "uter") == 0) + data->token = OUTER_token; + break; + case 's': + if (strcmp(str + 1, "olid") == 0) + data->token = SOLID_token; + break; + case 'v': + if (strcmp(str + 1, "ertex") == 0) + data->token = VERTEX_token; + break; + default: + data->token = unknown_token; + } + } - /* Try to eat "endfacet" */ - if (eat_token(it, "endfacet") != 0) - return -2; + if (data->token == unknown_token) { + if (str[0] == '+' || str[0] == '-' || isdigit(str[0])) /* Try to guess if it's a float */ + data->token = FLOAT_token; + else + data->token = ID_token; + } + } + else { + data->token = unknown_token; + parsing_error(data); + } +} - return 0; +static void parsing_eat_token(foug_stla_token_t token, foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + if (data->token == token) + parsing_advance(data); + else + parsing_error(data); +} + +static void parse_solidname_beg(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case ENDSOLID_token: + case FACET_token: + case ID_token: + break; + default: + parsing_error(data); + } +} + +static void parse_solidname_end(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case SOLID_token: + case ID_token: + break; + default: + parsing_error(data); + } +} + +static void parse_beginsolid(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case SOLID_token: { + parsing_eat_token(SOLID_token, data); + parse_solidname_beg(data); + if (parsing_can_continue(data) && data->geom != NULL && data->geom->begin_solid_func != NULL) + data->geom->begin_solid_func(data->geom, current_token_as_identifier(data)); + if (data->token == ID_token) + parsing_eat_token(ID_token, data); + break; + } + default: + parsing_error(data); + } /* end switch */ +} + +static void parse_endsolid(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case ENDSOLID_token: { + parsing_eat_token(ENDSOLID_token, data); + parse_solidname_end(data); + if (parsing_can_continue(data) && data->geom != NULL && data->geom->end_solid_func != NULL) + data->geom->end_solid_func(data->geom, current_token_as_identifier(data)); + if (data->token == ID_token) + parsing_eat_token(ID_token, data); + break; + } + default: + parsing_error(data); + } /* end switch */ +} + +static void parse_xyz_coords(foug_stla_parse_data_t* data, foug_stl_coords_t* coords) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case FLOAT_token: { + if (get_current_token_as_real32(data, &coords->x) != 0) + parsing_error(data); + parsing_eat_token(FLOAT_token, data); + if (get_current_token_as_real32(data, &coords->y) != 0) + parsing_error(data); + parsing_eat_token(FLOAT_token, data); + if (get_current_token_as_real32(data, &coords->z) != 0) + parsing_error(data); + parsing_eat_token(FLOAT_token, data); + break; + } + default: + break; + } /* end switch */ +} + +static void parse_facets(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case ENDSOLID_token: + break; + case FACET_token: { + foug_stl_triangle_t facet; + + parsing_eat_token(FACET_token, data); + parsing_eat_token(NORMAL_token, data); + parse_xyz_coords(data, &facet.normal); + + parsing_eat_token(OUTER_token, data); + parsing_eat_token(LOOP_token, data); + + parsing_eat_token(VERTEX_token, data); + parse_xyz_coords(data, &facet.v1); + parsing_eat_token(VERTEX_token, data); + parse_xyz_coords(data, &facet.v2); + parsing_eat_token(VERTEX_token, data); + parse_xyz_coords(data, &facet.v3); + + parsing_eat_token(ENDLOOP_token, data); + parsing_eat_token(ENDFACET_token, data); + + if (parsing_can_continue(data) + && data->geom != NULL && data->geom->process_next_triangle_func != NULL) + { + data->geom->process_next_triangle_func(data->geom, &facet); + } + + parse_facets(data); + break; + } + default: + parsing_error(data); + } /* end switch */ +} + +static void parse_solid(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case SOLID_token: + parse_beginsolid(data); + parse_facets(data); + parse_endsolid(data); + break; + default: + parsing_error(data); + } +} + +static void parse_contents(foug_stla_parse_data_t* data) +{ + if (!parsing_can_continue(data)) + return; + + switch (data->token) { + case SOLID_token: + parse_solid(data); + break; + default: + parsing_error(data); + } } #define FOUG_STLA_READ_STRING_BUFFER_LEN 512 @@ -236,9 +462,7 @@ int foug_stla_read(foug_stla_geom_input_t* geom, size_t data_size_hint) { char fixed_buffer[FOUG_STLA_READ_STRING_BUFFER_LEN]; - foug_string_buffer_t string_buffer; - foug_stream_fwd_iterator_t it; - foug_stream_fwd_iterator_stla_cookie_t stla_cookie; + foug_stla_parse_data_t parse_data; if (trsf == NULL) return FOUG_DATAX_NULL_TRANSFER_ERROR; @@ -247,58 +471,33 @@ int foug_stla_read(foug_stla_geom_input_t* geom, if (trsf->buffer_size == 0) return FOUG_DATAX_INVALID_BUFFER_SIZE_ERROR; - stla_cookie.task_control = &trsf->task_control; - stla_cookie.stream_data_size = data_size_hint; - stla_cookie.stream_offset = 0; - stla_cookie.is_stop_requested = 0; + parse_data.token = unknown_token; + parse_data.error = 0; - it.stream = &(trsf->stream); - it.buffer = trsf->buffer; - it.buffer_offset = 0; - it.buffer_size = trsf->buffer_size; - it.cookie = &stla_cookie; - it.stream_read_hook = foug_stream_fwd_iterator_stla_read_hook; - foug_stream_fwd_iterator_init(&it); + parse_data.stream_iterator_cookie.task_control = &trsf->task_control; + parse_data.stream_iterator_cookie.stream_data_size = data_size_hint; + parse_data.stream_iterator_cookie.stream_offset = 0; + parse_data.stream_iterator_cookie.is_stop_requested = 0; - string_buffer.data = fixed_buffer; - string_buffer.max_len = FOUG_STLA_READ_STRING_BUFFER_LEN; - string_buffer.len = 0; + parse_data.stream_iterator.stream = &(trsf->stream); + parse_data.stream_iterator.buffer = trsf->buffer; + parse_data.stream_iterator.buffer_offset = 0; + parse_data.stream_iterator.buffer_size = trsf->buffer_size; + parse_data.stream_iterator.cookie = &parse_data.stream_iterator_cookie; + parse_data.stream_iterator.stream_read_hook = foug_stream_fwd_iterator_stla_read_hook; + foug_stream_fwd_iterator_init(&parse_data.stream_iterator); - /* Eat solids */ - while (eat_token(&it, "solid") == 0 && !stla_cookie.is_stop_requested) { - int eat_facet_result; - foug_stl_triangle_t triangle; + parse_data.string_buffer.data = fixed_buffer; + parse_data.string_buffer.max_len = FOUG_STLA_READ_STRING_BUFFER_LEN; + parse_data.string_buffer.len = 0; - /* Try to eat solid's name */ - if (eat_string(&it, &string_buffer) != 0) - return FOUG_STLA_READ_PARSE_ERROR; - if (geom != NULL && geom->begin_solid_func != NULL) - geom->begin_solid_func(geom, string_buffer.data); + parse_data.geom = geom; - /* Try to eat facets */ - while ((eat_facet_result = eat_facet(&it, &string_buffer, &triangle)) >= 0 - && !stla_cookie.is_stop_requested) - { - if (eat_facet_result == 0) { - if (geom != NULL && geom->process_next_triangle_func != NULL) - geom->process_next_triangle_func(geom, &triangle); - } - else { - break; /* Ate "endsolid" */ - } - } /* end while */ + parsing_advance(&parse_data); + parse_contents(&parse_data); - /* Handle "endsolid" */ - if (eat_facet_result > 0) { - if (eat_string(&it, &string_buffer) != 0) - return FOUG_STLA_READ_PARSE_ERROR; - if (geom != NULL && geom->end_solid_func != NULL) - geom->end_solid_func(geom, string_buffer.data); - } - else { - return FOUG_STLA_READ_PARSE_ERROR; - } - } - - return !stla_cookie.is_stop_requested ? FOUG_DATAX_NO_ERROR : FOUG_DATAX_TASK_STOPPED_ERROR; + if (parse_data.error) + return FOUG_STLA_READ_PARSE_ERROR; + return parse_data.stream_iterator_cookie.is_stop_requested ? FOUG_DATAX_TASK_STOPPED_ERROR : + FOUG_DATAX_NO_ERROR; }