From: Kevin Day Date: Sat, 1 May 2021 16:23:56 +0000 (-0500) Subject: Update: Redesign Basic List loading logic to load all files into a single buffer. X-Git-Tag: 0.5.4~76 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=8fa5f44a49058d27a2ae9d7dbf46fd9ca23c2278;p=fll Update: Redesign Basic List loading logic to load all files into a single buffer. Upon further use and review I believe that it is better to treat all input sources as a single buffer. This allows for all of the parameters to work closer to what feels like normal logic. If I want to get the total lines for all listed files, then I should get that. If I want to get the total lines for each listed file, then I can call this program once for each file to get that. I am working on Basic List first but this will be repeated for all of the other FSS read projects as well (likely in a single commit). One of the downsides of this is that it exposes a current design limitation where the max buffer size is more likely to be reached. Future work will most likely address this in some manner. --- diff --git a/level_3/fss_basic_read/c/fss_basic_read.c b/level_3/fss_basic_read/c/fss_basic_read.c index 3c5b107..61a4c0d 100644 --- a/level_3/fss_basic_read/c/fss_basic_read.c +++ b/level_3/fss_basic_read/c/fss_basic_read.c @@ -353,17 +353,15 @@ extern "C" { } } + f_file_t file = f_file_t_initialize; fss_basic_read_depths_t depths = fss_basic_read_depths_t_initialize; - f_fss_delimits_t delimits = f_fss_delimits_t_initialize; - f_array_length_t original_size = data->quantity.total; - if (F_status_is_error_not(status)) { - status = fss_basic_read_main_preprocess_depth(arguments, *data, &depths); + status = fss_basic_read_depth_process(arguments, *data, &depths); if (F_status_is_error(status)) { - fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_main_preprocess_depth", F_true); + fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_depth_process", F_true); } } @@ -389,93 +387,98 @@ extern "C" { status = F_status_set_error(F_parameter); } - if (F_status_is_error_not(status) && data->process_pipe) { - f_file_t file = f_file_t_initialize; + // Provide a range designating where within the buffer a particular file exists, using a statically allocated array. @fixme make this a structure with + fss_basic_read_file_t files_array[data->remaining.used + 1]; + fss_basic_read_files_t files = fss_basic_read_files_t_initialize; + + if (F_status_is_error_not(status)) { + files.array = files_array; + files.size += data->remaining.used; + for (f_array_length_t i = 0; i < files.used; ++i) { + f_macro_string_range_t_clear(files.array[i].range); + } // for + } + + if (F_status_is_error_not(status) && data->process_pipe) { file.id = f_type_descriptor_input; + file.stream = f_type_input; + + files.array[0].name = 0; + files.array[0].range.start = 0; - status = f_file_read(file, &data->buffer); + status = f_file_stream_read(file, 1, &data->buffer); if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read", F_true, "-", "read", fll_error_file_type_pipe); + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_read", F_true, "-", "read", fll_error_file_type_pipe); } - else { - status = fss_basic_read_main_process_file(arguments, data, "-", depths, &delimits); + else if (data->buffer.used) { + files.array[0].range.stop = data->buffer.used - 1; + + // This standard is newline sensitive, when appending files to the buffer if the file lacks a final newline then this could break the format for files appended thereafter. + // Guarantee that a newline exists at the end of the buffer. + status = f_string_append_assure(f_string_eol_s, 1, &data->buffer); if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "fss_basic_read_main_process_file", F_true, "-", "read", fll_error_file_type_pipe); + fll_error_file_print(data->error, F_status_set_fine(status), "f_string_append_assure", F_true, "-", "read", fll_error_file_type_pipe); } } - - // Clear buffers before continuing. - f_macro_fss_contents_t_delete_simple(data->contents); - f_macro_fss_objects_t_delete_simple(data->objects); - f_macro_string_dynamic_t_delete_simple(data->buffer); + else { + files.array[0].range.start = 1; + } } if (F_status_is_error_not(status) && data->remaining.used > 0) { for (f_array_length_t i = 0; i < data->remaining.used; i++) { - f_file_t file = f_file_t_initialize; - status = f_file_open(arguments.argv[data->remaining.array[i]], 0, &file); + files.array[files.used].range.start = data->buffer.used; + file.stream = 0; + file.id = -1; - data->quantity.total = original_size; + status = f_file_stream_open(arguments.argv[data->remaining.array[i]], 0, &file); if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_open", F_true, arguments.argv[data->remaining.array[i]], "open", fll_error_file_type_file); - break; - } - - if (!data->quantity.total) { - status = f_file_size_by_id(file.id, &data->quantity.total); - - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_size_by_id", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file); - - f_file_stream_close(F_true, &file); - break; - } + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.argv[data->remaining.array[i]], "open", fll_error_file_type_file); - // Skip past empty files. - if (!data->quantity.total) { - if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) { - fprintf(data->output.stream, "0%c", f_string_eol_s[0]); - } - - f_file_stream_close(F_true, &file); - continue; - } + f_file_stream_close(F_true, &file); + break; } - status = f_file_read_until(file, data->quantity.total, &data->buffer); + status = f_file_stream_read(file, 1, &data->buffer); f_file_stream_close(F_true, &file); if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_until", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file); + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_read", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file); + break; } + else if (data->buffer.used > files.array[files.used].range.start) { + files.array[files.used].name = arguments.argv[data->remaining.array[i]]; + files.array[files.used++].range.stop = data->buffer.used - 1; - status = fss_basic_read_main_process_file(arguments, data, arguments.argv[data->remaining.array[i]], depths, &delimits); + // This standard is newline sensitive, when appending files to the buffer if the file lacks a final newline then this could break the format for files appended thereafter. + // Guarantee that a newline exists at the end of the buffer. + status = f_string_append_assure(f_string_eol_s, 1, &data->buffer); - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "fss_basic_read_main_process_file", F_true, arguments.argv[data->remaining.array[i]], "read", fll_error_file_type_file); - break; + if (F_status_is_error(status)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_string_append_assure", F_true, "-", "read", fll_error_file_type_pipe); + } + } + else { + files.array[files.used].range.start = 1; } - - // Clear buffers before repeating the loop. - f_macro_fss_contents_t_delete_simple(data->contents); - f_macro_fss_objects_t_delete_simple(data->objects); - f_macro_string_dynamic_t_delete_simple(data->buffer); } // for + } - if (F_status_is_error(status)) { - f_macro_fss_contents_t_delete_simple(data->contents); - f_macro_fss_objects_t_delete_simple(data->objects); - f_macro_string_dynamic_t_delete_simple(data->buffer); - } + if (F_status_is_error_not(status)) { + status = fss_basic_read_process(arguments, files, depths, data, &delimits); } + f_macro_fss_contents_t_delete_simple(data->contents); + f_macro_fss_objects_t_delete_simple(data->objects); + f_macro_string_dynamic_t_delete_simple(data->buffer); + fss_basic_read_macro_depths_t_delete_simple(depths); f_macro_fss_delimits_t_delete_simple(delimits); } diff --git a/level_3/fss_basic_read/c/fss_basic_read.h b/level_3/fss_basic_read/c/fss_basic_read.h index bea720e..c04aa8b 100644 --- a/level_3/fss_basic_read/c/fss_basic_read.h +++ b/level_3/fss_basic_read/c/fss_basic_read.h @@ -178,7 +178,6 @@ extern "C" { f_string_dynamic_t buffer; f_fss_objects_t objects; f_fss_contents_t contents; - f_string_quantity_t quantity; uint8_t delimit_mode; f_array_length_t delimit_depth; @@ -196,7 +195,6 @@ extern "C" { f_string_dynamic_t_initialize, \ f_fss_objects_t_initialize, \ f_fss_contents_t_initialize, \ - f_string_quantity_t_initialize, \ fss_basic_read_delimit_mode_all, \ 0, \ f_color_context_t_initialize, \ diff --git a/level_3/fss_basic_read/c/private-fss_basic_read.c b/level_3/fss_basic_read/c/private-fss_basic_read.c index 6df27ba..4fed779 100644 --- a/level_3/fss_basic_read/c/private-fss_basic_read.c +++ b/level_3/fss_basic_read/c/private-fss_basic_read.c @@ -5,8 +5,9 @@ extern "C" { #endif -#ifndef _di_fss_basic_read_main_preprocess_depth_ - f_status_t fss_basic_read_main_preprocess_depth(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) { +#ifndef _di_fss_basic_read_depth_process_ + f_status_t fss_basic_read_depth_process(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) { + f_status_t status = F_none; { @@ -20,6 +21,7 @@ extern "C" { if (F_status_is_error(status)) { f_color_print(data.error.to.stream, data.context.set.error, "%sUnable to allocate memory.%c", fll_error_print_error, f_string_eol_s[0]); + return status; } @@ -31,6 +33,7 @@ extern "C" { f_array_length_t position_name = 0; for (f_array_length_t i = 0; i < depths->used; i++) { + depths->array[i].depth = 0; depths->array[i].index_at = 0; depths->array[i].index_name = 0; @@ -73,6 +76,7 @@ extern "C" { if (F_status_is_error(status)) { fll_error_parameter_integer_print(data.error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_at, arguments.argv[depths->array[i].index_at]); + return status; } } // for @@ -167,20 +171,105 @@ extern "C" { return F_none; } -#endif // _di_fss_basic_read_main_preprocess_depth_ +#endif // _di_fss_basic_read_depth_process_ + +#ifndef _di_fss_basic_read_file_identify_ + f_string_t fss_basic_read_file_identify(const f_array_length_t at, const fss_basic_read_files_t files) { + + for (f_array_length_t i = 0; i < files.used; ++i) { + + if (at >= files.array[i].range.start && at <= files.array[i].range.stop) { + return files.array[i].name; + } + } // for + + return ""; + } +#endif // _di_fss_basic_read_file_identify_ + +#ifndef _di_fss_basic_read_load_number_ + f_status_t fss_basic_read_load_number(const f_console_arguments_t arguments, const fss_basic_read_data_t data, const f_array_length_t parameter, const f_string_t name, f_number_unsigned_t *number) { + + if (data.parameters[parameter].result == f_console_result_additional) { + const f_array_length_t index = data.parameters[parameter].values.array[data.parameters[parameter].values.used - 1]; + const f_string_range_t range = f_macro_string_range_t_initialize(strnlen(arguments.argv[index], f_console_parameter_size)); + + const f_status_t status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, number); + + if (F_status_is_error(status)) { + fll_error_parameter_integer_print(data.error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, name, arguments.argv[index]); + + return status; + } + } + + return F_none; + } +#endif // _di_fss_basic_read_load_number_ + +#ifndef _di_fss_basic_read_print_object_end_ + void fss_basic_read_print_object_end(const fss_basic_read_data_t data) { + + if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) { + fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_start); + } + else { + fprintf(data.output.stream, "%c", f_fss_space); + } + } +#endif // _di_fss_basic_read_print_object_end_ + +#ifndef _di_fss_basic_read_print_set_end_ + void fss_basic_read_print_set_end(const fss_basic_read_data_t data) { + + if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) { + fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_end); + } + else { + fprintf(data.output.stream, "%c", f_fss_eol); + } + } +#endif // _di_fss_basic_read_print_set_end_ + +#ifndef _di_fss_basic_read_process_ + f_status_t fss_basic_read_process(const f_console_arguments_t arguments, const fss_basic_read_files_t files, const fss_basic_read_depths_t depths, fss_basic_read_data_t *data, f_fss_delimits_t *delimits) { -#ifndef _di_fss_basic_read_main_process_file_ - f_status_t fss_basic_read_main_process_file(const f_console_arguments_t arguments, fss_basic_read_data_t *data, const f_string_t filename, const fss_basic_read_depths_t depths, f_fss_delimits_t *delimits) { f_status_t status = F_none; const f_array_lengths_t except_none = f_array_lengths_t_initialize; bool delimited = F_true; + bool include_empty = F_false; + f_number_unsigned_t select = 0; + f_number_unsigned_t line = 0; - // for this standard, delimits would always be applied, except for when delimit_depth is greater than 0. if (data->delimit_mode == fss_basic_read_delimit_mode_none || (data->delimit_depth && (data->delimit_mode == fss_basic_read_delimit_mode_depth || data->delimit_mode == fss_basic_read_delimit_mode_depth_greater))) { delimited = F_false; } + if (data->parameters[fss_basic_read_parameter_empty].result == f_console_result_found) { + include_empty = F_true; + } + + status = fss_basic_read_load_number(arguments, *data, fss_basic_read_parameter_select, fss_basic_read_long_select, &select); + + if (F_status_is_error_not(status)) { + status = fss_basic_read_load_number(arguments, *data, fss_basic_read_parameter_line, fss_basic_read_long_line, &line); + } + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fss_basic_read_load_setting_number", F_true); + + return status; + } + + if (data->parameters[fss_basic_read_parameter_select].result == f_console_result_additional) { + + // This standard does not support multiple content groups. + if (select > 0) { + return F_none; + } + } + { f_string_range_t input = f_macro_string_range_t_initialize(data->buffer.used); @@ -189,8 +278,9 @@ extern "C" { status = fll_fss_basic_read(data->buffer, &input, &data->objects, &data->contents, 0, delimits, 0); if (F_status_is_error(status)) { - // @todo: detect and replace fll_error_file_type_file with fll_error_file_type_pipe as appropriate. - fll_error_file_print(data->error, F_status_set_fine(status), "fll_fss_basic_read", F_true, filename, "process", fll_error_file_type_file); + const f_string_t file_name = fss_basic_read_file_identify(input.start, files); + + fll_error_file_print(data->error, F_status_set_fine(status), "fll_fss_basic_read", F_true, file_name ? file_name : "-", "process", fll_error_file_type_file); } else if (status == F_data_not_stop || status == F_data_not_eos) { f_macro_fss_contents_t_delete_simple(data->contents); @@ -199,6 +289,7 @@ extern "C" { if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) { fprintf(data->output.stream, "0%c", f_string_eol_s[0]); + return F_none; } @@ -206,43 +297,6 @@ extern "C" { } if (F_status_is_error(status)) { - f_macro_fss_contents_t_delete_simple(data->contents); - f_macro_fss_objects_t_delete_simple(data->objects); - f_macro_string_dynamic_t_delete_simple(data->buffer); - - return status; - } - } - - f_number_unsigned_t select = 0; - - if (data->parameters[fss_basic_read_parameter_select].result == f_console_result_additional) { - const f_array_length_t index = data->parameters[fss_basic_read_parameter_select].values.array[data->parameters[fss_basic_read_parameter_select].values.used - 1]; - const f_string_range_t range = f_macro_string_range_t_initialize(strlen(arguments.argv[index])); - - status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, &select); - - if (F_status_is_error(status)) { - fll_error_parameter_integer_print(data->error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_select, arguments.argv[index]); - return status; - } - - // This standard does not support multiple content groups. - if (select > 0) { - return F_none; - } - } - - f_array_length_t line = 0; - - if (data->parameters[fss_basic_read_parameter_line].result == f_console_result_additional) { - const f_array_length_t index = data->parameters[fss_basic_read_parameter_line].values.array[data->parameters[fss_basic_read_parameter_line].values.used - 1]; - const f_string_range_t range = f_macro_string_range_t_initialize(strlen(arguments.argv[index])); - - status = fl_conversion_string_to_number_unsigned(arguments.argv[index], range, &line); - - if (F_status_is_error(status)) { - fll_error_parameter_integer_print(data->error, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, fss_basic_read_long_line, arguments.argv[index]); return status; } } @@ -275,12 +329,6 @@ extern "C" { memset(names, 1, sizeof(bool) * data->objects.used); } - bool include_empty = 0; - - if (data->parameters[fss_basic_read_parameter_empty].result == f_console_result_found) { - include_empty = 1; - } - if (data->parameters[fss_basic_read_parameter_object].result == f_console_result_found) { if (data->parameters[fss_basic_read_parameter_total].result == f_console_result_found) { if (depths.array[0].index_at > 0) { @@ -484,31 +532,7 @@ extern "C" { return F_none; } -#endif // _di_fss_basic_read_main_process_file_ - -#ifndef _di_fss_basic_read_print_object_end_ - void fss_basic_read_print_object_end(const fss_basic_read_data_t data) { - - if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) { - fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_start); - } - else { - fprintf(data.output.stream, "%c", f_fss_space); - } - } -#endif // _di_fss_basic_read_print_object_end_ - -#ifndef _di_fss_basic_read_print_set_end_ - void fss_basic_read_print_set_end(const fss_basic_read_data_t data) { - - if (data.parameters[fss_basic_read_parameter_pipe].result == f_console_result_found) { - fprintf(data.output.stream, "%c", fss_basic_read_pipe_content_end); - } - else { - fprintf(data.output.stream, "%c", f_fss_eol); - } - } -#endif // _di_fss_basic_read_print_set_end_ +#endif // _di_fss_basic_read_process_ #ifdef __cplusplus } // extern "C" diff --git a/level_3/fss_basic_read/c/private-fss_basic_read.h b/level_3/fss_basic_read/c/private-fss_basic_read.h index c6f1696..5198bac 100644 --- a/level_3/fss_basic_read/c/private-fss_basic_read.h +++ b/level_3/fss_basic_read/c/private-fss_basic_read.h @@ -123,7 +123,47 @@ extern "C" { #endif // _di_fss_basic_read_depths_t_ /** - * Pre-process the parameters, parsing out and handling the depth and depth related parameters. + * A structure for designating where within the buffer a particular file exists, using a statically allocated array. + * + * name: The name of the file representing the range. Set string to NULL to represent the STDIN pipe. + */ +#ifndef _di_fss_basic_read_file_t_ + typedef struct { + f_string_t name; + f_string_range_t range; + } fss_basic_read_file_t; + + #define fss_basic_read_file_t_initialize \ + { \ + f_string_t_initialize, \ + f_string_range_t_initialize, \ + } +#endif // _di_fss_basic_read_file_t_ + +/** + * An array of depth parameters. + * + * This is intended to be defined and used statically allocated and as such no dynamic allocation or dynamic deallocation methods are provided. + * + * The STDIN pipe is reserved for index 0 and as such size and used must be initialized to 1. + * + * array: The array of depths. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ +#ifndef _di_fss_basic_read_files_t_ + typedef struct { + fss_basic_read_file_t *array; + + f_array_length_t size; + f_array_length_t used; + } fss_basic_read_files_t; + + #define fss_basic_read_files_t_initialize { 0, 1, 1 } +#endif // _di_fss_basic_read_files_t_ + +/** + * Process the parameters, parsing out and handling the depth and depth related parameters. * * Will handle depth-sensitive parameter conflicts, such as --name being used with --at (which is not allowed). * @@ -139,34 +179,50 @@ extern "C" { * * Status codes (with error bit) are returned on any problem. */ -#ifndef _di_fss_basic_read_main_preprocess_depth_ - extern f_status_t fss_basic_read_main_preprocess_depth(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) f_attribute_visibility_internal; -#endif // _di_fss_basic_read_main_preprocess_depth_ +#ifndef _di_fss_basic_read_depth_process_ + extern f_status_t fss_basic_read_depth_process(const f_console_arguments_t arguments, const fss_basic_read_data_t data, fss_basic_read_depths_t *depths) f_attribute_visibility_internal; +#endif // _di_fss_basic_read_depth_process_ + +/** + * Get the name of the file the given position represents within the buffer. + * + * @param at + * The position within the buffer. + * @param files + * The representation of files and their respective ranges within the buffer. + * + * @return + * A string with the name when found. + * NULL is returned if the range represents the STDIN pipe. + * + * On failure to identify, an empty string is returned. + */ +#ifndef _di_fss_basic_read_file_identify_ + extern f_string_t fss_basic_read_file_identify(const f_array_length_t at, const fss_basic_read_files_t files) f_attribute_visibility_internal; +#endif // _di_fss_basic_read_file_identify_ /** - * Process a given file. + * Load a given number parameter. * * @param arguments * The console arguments passed to the program. * @param data * The program specific data. - * @param file_name - * The name of the file being processed. - * @param depths - * The processed depth parameters. - * @param delimits - * An array of delimits detected during processing. + * @param parameter + * An ID representing the parameter. + * @param name + * The parameter name to print on error. + * @param number + * The location to store the loaded number. * * @return * F_none on success. * * Status codes (with error bit) are returned on any problem. - * - * @see fss_basic_read_main_preprocess_depth() */ -#ifndef _di_fss_basic_read_main_process_file_ - extern f_status_t fss_basic_read_main_process_file(const f_console_arguments_t arguments, fss_basic_read_data_t *data, const f_string_t file_name, const fss_basic_read_depths_t depths, f_fss_delimits_t *delimits) f_attribute_visibility_internal; -#endif // _di_fss_basic_read_main_process_file_ +#ifndef _di_fss_basic_read_load_number_ + extern f_status_t fss_basic_read_load_number(const f_console_arguments_t arguments, const fss_basic_read_data_t data, const f_array_length_t parameter, const f_string_t name, f_number_unsigned_t *number) f_attribute_visibility_internal; +#endif // _di_fss_basic_read_load_number_ /** * Print the end of an object (which is essentially the start of a content). @@ -188,6 +244,31 @@ extern "C" { extern void fss_basic_read_print_set_end(const fss_basic_read_data_t data) f_attribute_visibility_internal; #endif // _di_fss_basic_read_print_set_end_ +/** + * Perform the basic read processing on the buffer. + * + * @param arguments + * The console arguments passed to the program. + * @param files + * An array representing the ranges in which a given file exists in the buffer. + * @param depths + * The processed depth parameters. + * @param data + * The program specific data. + * @param delimits + * An array of delimits detected during processing. + * + * @return + * F_none on success. + * + * Status codes (with error bit) are returned on any problem. + * + * @see fss_basic_read_load_setting() + */ +#ifndef _di_fss_basic_read_process_ + extern f_status_t fss_basic_read_process(const f_console_arguments_t arguments, const fss_basic_read_files_t files, const fss_basic_read_depths_t depths, fss_basic_read_data_t *data, f_fss_delimits_t *delimits) f_attribute_visibility_internal; +#endif // _di_fss_basic_read_process_ + #ifdef __cplusplus } // extern "C" #endif