From 9b7017867a16e46ed4ffcecacd60e78cdb0805c6 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sat, 5 Aug 2023 00:16:18 -0500 Subject: [PATCH] Feature: Add f_time project, adding functions for safely handling struct timespec. This adds f_time_spec_millisecond() and f_time_spec_nanosecond() for safely handling overflow and underflow when using standard FLL f_number_unsigned_t numbers. Unit tests are added. A comment in the controller prrogram is added to designate that the controller-specific functions should be updated to call these functions. --- build/disable/about.txt | 4 +- build/level_0/settings | 2 + build/monolithic/settings | 2 + build/scripts/bootstrap-example.sh | 2 +- build/scripts/test.sh | 2 +- build/stand_alone/fake.config.h | 19 ++- level_0/f_time/c/time.c | 86 ++++++++++++++ level_0/f_time/c/time.h | 86 ++++++++++++++ level_0/f_time/data/build/defines | 1 + level_0/f_time/data/build/dependencies | 4 + level_0/f_time/data/build/dependencies-tests | 3 + level_0/f_time/data/build/fakefile | 12 ++ level_0/f_time/data/build/functions | 3 + level_0/f_time/data/build/macros | 3 + level_0/f_time/data/build/settings | 71 +++++++++++ level_0/f_time/data/build/settings-tests | 57 +++++++++ level_0/f_time/data/build/structures | 3 + level_0/f_time/data/build/testfile | 63 ++++++++++ .../tests/unit/c/test-time-spec_millisecond.c | 131 +++++++++++++++++++++ .../tests/unit/c/test-time-spec_millisecond.h | 41 +++++++ .../tests/unit/c/test-time-spec_nanosecond.c | 131 +++++++++++++++++++++ .../tests/unit/c/test-time-spec_nanosecond.h | 41 +++++++ level_0/f_time/tests/unit/c/test-time.c | 41 +++++++ level_0/f_time/tests/unit/c/test-time.h | 72 +++++++++++ level_0/f_type/c/type.h | 7 +- .../controller/c/controller/private-controller.c | 1 + 26 files changed, 878 insertions(+), 10 deletions(-) create mode 100644 level_0/f_time/c/time.c create mode 100644 level_0/f_time/c/time.h create mode 100644 level_0/f_time/data/build/defines create mode 100644 level_0/f_time/data/build/dependencies create mode 100644 level_0/f_time/data/build/dependencies-tests create mode 100644 level_0/f_time/data/build/fakefile create mode 100644 level_0/f_time/data/build/functions create mode 100644 level_0/f_time/data/build/macros create mode 100644 level_0/f_time/data/build/settings create mode 100644 level_0/f_time/data/build/settings-tests create mode 100644 level_0/f_time/data/build/structures create mode 100644 level_0/f_time/data/build/testfile create mode 100644 level_0/f_time/tests/unit/c/test-time-spec_millisecond.c create mode 100644 level_0/f_time/tests/unit/c/test-time-spec_millisecond.h create mode 100644 level_0/f_time/tests/unit/c/test-time-spec_nanosecond.c create mode 100644 level_0/f_time/tests/unit/c/test-time-spec_nanosecond.h create mode 100644 level_0/f_time/tests/unit/c/test-time.c create mode 100644 level_0/f_time/tests/unit/c/test-time.h diff --git a/build/disable/about.txt b/build/disable/about.txt index 79cd64e..da3828d 100644 --- a/build/disable/about.txt +++ b/build/disable/about.txt @@ -33,11 +33,11 @@ Consider the following example using the fake dependencies for the stand alone b # echo $(cat level_3/fake/data/build/dependencies) Which prints: - # fss-0000 f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_parse f_path f_pipe f_print f_rip f_signal f_thread fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fll_error fll_execute fll_file fll_fss fll_print fll_program + # fss-0000 f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_parse f_path f_pipe f_print f_rip f_signal f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fll_error fll_execute fll_file fll_fss fll_print fll_program From this list, build the level_0: -# for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_parse f_path f_pipe f_print f_rip f_signal f_thread ; do if [[ -f build/disable/level_0/$i.h ]] ; then echo >> build/stand_alone/fake.config.h && cat build/disable/level_0/$i.h >> build/stand_alone/fake.config.h ; fi ; done +# for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_parse f_path f_pipe f_print f_rip f_signal f_thread f_time ; do if [[ -f build/disable/level_0/$i.h ]] ; then echo >> build/stand_alone/fake.config.h && cat build/disable/level_0/$i.h >> build/stand_alone/fake.config.h ; fi ; done From the list, build the level_1: diff --git a/build/level_0/settings b/build/level_0/settings index 38effe0..fad50c3 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -75,6 +75,7 @@ build_sources_library string/quantity.c string/quantitys.c string/quantityss.c build_sources_library string/range.c string/ranges.c string/rangess.c build_sources_library string/static.c string/statics.c string/staticss.c build_sources_library string/triple.c string/triples.c string/tripless.c +build_sources_library time.c build_sources_library type_array/cell.c type_array/file.c type_array/fll_id.c type_array/int8.c type_array/int16.c type_array/int32.c type_array/int64.c type_array/int128.c type_array/number_unsigned.c type_array/poll.c type_array/state.c type_array/status.c type_array/uint8.c type_array/uint16.c type_array/uint32.c type_array/uint64.c type_array/uint128.c build_sources_library type_array/private-cell.c type_array/private-file.c type_array/private-fll_id.c type_array/private-int8.c type_array/private-int16.c type_array/private-int32.c type_array/private-int64.c type_array/private-int128.c type_array/private-number_unsigned.c type_array/private-poll.c type_array/private-state.c type_array/private-status.c type_array/private-uint8.c type_array/private-uint16.c type_array/private-uint32.c type_array/private-uint64.c type_array/private-uint128.c build_sources_library utf.c private-utf.c private-utf_alphabetic.c private-utf_combining.c private-utf_control.c private-utf_digit.c private-utf_emoji.c private-utf_numeric.c private-utf_phonetic.c private-utf_private.c private-utf_punctuation.c private-utf_subscript.c private-utf_superscript.c private-utf_symbol.c private-utf_unassigned.c private-utf_valid.c private-utf_whitespace.c private-utf_wide.c private-utf_word.c private-utf_zero_width.c @@ -128,6 +129,7 @@ build_sources_headers string/quantity.h string/quantitys.h string/quantityss.h build_sources_headers string/range.h string/ranges.h string/rangess.h build_sources_headers string/static.h string/statics.h string/staticss.h build_sources_headers string/triple.h string/triples.h string/tripless.h +build_sources_headers time.h build_sources_headers type.h type_file.h build_sources_headers type_array.h type_array_file.h type_array/common.h type_array/cell.h type_array/file.h type_array/fll_id.h type_array/int8.h type_array/int16.h type_array/int32.h type_array/int64.h type_array/int128.h type_array/number_unsigned.h type_array/poll.h type_array/state.h type_array/status.h type_array/uint8.h type_array/uint16.h type_array/uint32.h type_array/uint64.h type_array/uint128.h build_sources_headers utf.h utf/common.h utf/convert.h utf/dynamic.h utf/dynamics.h utf/dynamicss.h utf/is.h utf/is_character.h utf/map.h utf/maps.h utf/mapss.h utf/map_multi.h utf/map_multis.h utf/map_multiss.h utf/static.h utf/statics.h utf/staticss.h utf/string.h utf/triple.h utf/triples.h utf/tripless.h diff --git a/build/monolithic/settings b/build/monolithic/settings index 6a50e28..cdf3334 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -75,6 +75,7 @@ build_sources_library level_0/string/quantity.c level_0/string/quantitys.c level build_sources_library level_0/string/range.c level_0/string/ranges.c level_0/string/rangess.c build_sources_library level_0/string/static.c level_0/string/statics.c level_0/string/staticss.c build_sources_library level_0/string/triple.c level_0/string/triples.c level_0/string/tripless.c +build_sources_library level_0/time.c build_sources_library level_0/type_array/cell.c level_0/type_array/file.c level_0/type_array/fll_id.c level_0/type_array/int8.c level_0/type_array/int16.c level_0/type_array/int32.c level_0/type_array/int64.c level_0/type_array/int128.c level_0/type_array/number_unsigned.c level_0/type_array/poll.c level_0/type_array/state.c level_0/type_array/status.c level_0/type_array/uint8.c level_0/type_array/uint16.c level_0/type_array/uint32.c level_0/type_array/uint64.c level_0/type_array/uint128.c build_sources_library level_0/type_array/private-cell.c level_0/type_array/private-file.c level_0/type_array/private-fll_id.c level_0/type_array/private-int8.c level_0/type_array/private-int16.c level_0/type_array/private-int32.c level_0/type_array/private-int64.c level_0/type_array/private-int128.c level_0/type_array/private-number_unsigned.c level_0/type_array/private-poll.c level_0/type_array/private-state.c level_0/type_array/private-status.c level_0/type_array/private-uint8.c level_0/type_array/private-uint16.c level_0/type_array/private-uint32.c level_0/type_array/private-uint64.c level_0/type_array/private-uint128.c build_sources_library level_0/utf.c level_0/private-utf.c level_0/private-utf_alphabetic.c level_0/private-utf_combining.c level_0/private-utf_control.c level_0/private-utf_digit.c level_0/private-utf_emoji.c level_0/private-utf_numeric.c level_0/private-utf_phonetic.c level_0/private-utf_private.c level_0/private-utf_punctuation.c level_0/private-utf_subscript.c level_0/private-utf_superscript.c level_0/private-utf_symbol.c level_0/private-utf_unassigned.c level_0/private-utf_valid.c level_0/private-utf_whitespace.c level_0/private-utf_wide.c level_0/private-utf_word.c level_0/private-utf_zero_width.c @@ -149,6 +150,7 @@ build_sources_headers level_0/string/quantity.h level_0/string/quantitys.h level build_sources_headers level_0/string/range.h level_0/string/ranges.h level_0/string/rangess.h build_sources_headers level_0/string/static.h level_0/string/statics.h level_0/string/staticss.h build_sources_headers level_0/string/triple.h level_0/string/triples.h level_0/string/tripless.h +build_sources_headers level_0/time.h build_sources_headers level_0/type.h level_0/type_file.h build_sources_headers level_0/type_array.h level_0/type_array_file.h level_0/type_array/common.h level_0/type_array/cell.h level_0/type_array/file.h level_0/type_array/fll_id.h level_0/type_array/int8.h level_0/type_array/int16.h level_0/type_array/int32.h level_0/type_array/int64.h level_0/type_array/int128.h level_0/type_array/number_unsigned.h level_0/type_array/poll.h level_0/type_array/state.h level_0/type_array/status.h level_0/type_array/uint8.h level_0/type_array/uint16.h level_0/type_array/uint32.h level_0/type_array/uint64.h level_0/type_array/uint128.h build_sources_headers level_0/utf.h level_0/utf/common.h level_0/utf/convert.h level_0/utf/dynamic.h level_0/utf/dynamics.h level_0/utf/dynamicss.h level_0/utf/is.h level_0/utf/is_character.h level_0/utf/map.h level_0/utf/maps.h level_0/utf/mapss.h level_0/utf/map_multi.h level_0/utf/map_multis.h level_0/utf/map_multiss.h level_0/utf/static.h level_0/utf/statics.h level_0/utf/staticss.h level_0/utf/string.h level_0/utf/triple.h level_0/utf/triples.h level_0/utf/tripless.h diff --git a/build/scripts/bootstrap-example.sh b/build/scripts/bootstrap-example.sh index a09f7e6..d2b36e0 100644 --- a/build/scripts/bootstrap-example.sh +++ b/build/scripts/bootstrap-example.sh @@ -130,7 +130,7 @@ if [[ ${1} == "individual" ]] ; then ${shell_command} build/scripts/package.sh ${verbose} ${color} rebuild -i if [[ ${?} -eq 0 ]] ; then - for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_status_string f_serialize f_signal f_socket f_thread fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program ; do + for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_status_string f_serialize f_signal f_socket f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program ; do echo && echo "Processing ${i}." && cd package/individual/${i}-${version}/ && diff --git a/build/scripts/test.sh b/build/scripts/test.sh index 870078d..98d6925 100644 --- a/build/scripts/test.sh +++ b/build/scripts/test.sh @@ -71,7 +71,7 @@ test_main() { local verbose= local verbose_common= - local projects="f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_serialize f_signal f_socket f_status_string f_thread fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program" + local projects="f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_serialize f_signal f_socket f_status_string f_thread f_time fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print fl_status_string fl_utf_file fll_control_group fll_error fll_execute fll_file fll_fss fll_fss_status_string fll_iki fll_print fll_program" local projects_no_tests="f_type" local programs="fss_read" diff --git a/build/stand_alone/fake.config.h b/build/stand_alone/fake.config.h index cfa89b6..65d6553 100644 --- a/build/stand_alone/fake.config.h +++ b/build/stand_alone/fake.config.h @@ -2,11 +2,18 @@ // To assist in building this file use (may need to omit --defined-only): "nm --defined-only -f p programs/shared/fake | grep -oP '^(f|fl|fll)_[^\s]+'". // To get the list of all possible functions, use something like: "grep -horP '\b_di_f_\w*\b' level_0/f_color/". // -// Example: echo > /tmp/all.txt -// for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_signal f_socket f_thread ; do grep -horP '\b_di_f_\w*\b' level_0/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_0/$i/c >> /tmp/all.txt ; done -// for i in fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print ; do grep -horP '\b_di_fl_\w*\b' level_1/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_1/$i/c >> /tmp/all.txt ; done -// for i in fll_error fll_execute fll_file fll_fss fll_print fll_program ; do grep -horP '\b_di_fll_\w*\b' level_2/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_2/$i/c >> /tmp/all.txt ; done -// sort /tmp/all.txt | uniq | sed -e 's|^_|#define &|g' > /tmp/sorted.txt +// Note that some special defines may need to be manually added, such as: +// #define _di_private_inline_f_print_to_error_ +// #define _di_private_inline_private_f_print_to_error_ +// +// Example: +// echo > /tmp/all.txt +// for i in f_type f_status f_memory f_type_array f_string f_utf f_account f_capability f_color f_compare f_console f_control_group f_conversion f_directory f_environment f_execute f_file f_fss f_iki f_limit f_network f_parse f_path f_pipe f_print f_rip f_signal f_socket f_thread f_time ; do grep -horP '\b_di_f_\w*\b' level_0/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_0/$i/c >> /tmp/all.txt ; done +// for i in fl_control_group fl_conversion fl_directory fl_environment fl_execute fl_fss fl_iki fl_path fl_print ; do grep -horP '\b_di_fl_\w*\b' level_1/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_1/$i/c >> /tmp/all.txt ; done +// for i in fll_error fll_execute fll_file fll_fss fll_print fll_program ; do grep -horP '\b_di_fll_\w*\b' level_2/$i/c >> /tmp/all.txt ; grep -horP '\b_di_macro_\w*\b' level_2/$i/c >> /tmp/all.txt ; done +// sort /tmp/all.txt | uniq | sed -e 's|^_|#define &|g' > /tmp/sorted.txt +// echo "#define _di_private_inline_f_print_to_error_" >> /tmp/sorted.txt +// echo "#define _di_private_inline_private_f_print_to_error_" >> /tmp/sorted.txt #define _di_f_account_by_id_ #define _di_f_account_by_name_ @@ -1997,6 +2004,8 @@ #define _di_f_thread_spin_t_ #define _di_f_thread_spin_unlock_ #define _di_f_thread_unlock_ +#define _di_f_time_spec_millisecond_ +#define _di_f_time_spec_nanosecond_ #define _di_f_time_spec_t_ #define _di_f_time_t_ //#define _di_f_type_d_ diff --git a/level_0/f_time/c/time.c b/level_0/f_time/c/time.c new file mode 100644 index 0000000..04bd748 --- /dev/null +++ b/level_0/f_time/c/time.c @@ -0,0 +1,86 @@ +#include "time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_f_time_spec_millisecond_ + extern f_status_t f_time_spec_millisecond(const f_number_unsigned_t second, const f_number_unsigned_t millisecond, struct timespec * const time) { + #ifndef _di_level_0_parameter_checking_ + if (!time) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (((time_t) second) < 0) { + if ((long) millisecond < 0) { + time->tv_sec = 0; + time->tv_nsec = 0; + } + else { + time->tv_sec = (long) millisecond > 1000 ? ((long) millisecond) / 1000 : 0; + time->tv_nsec = (time->tv_sec ? ((long) millisecond) % 1000 : ((long) millisecond)) * 1000; + } + + return F_status_set_error(F_number_underflow); + } + + if ((long) millisecond < 0) { + time->tv_sec = (time_t) second; + time->tv_nsec = 0; + + return F_status_set_error(F_number_underflow); + } + + if (millisecond) { + time->tv_sec = ((long) millisecond) / 1000; + time->tv_nsec = (time->tv_sec ? ((long) millisecond) % 1000 : ((long) millisecond)) * 1000; + } + else { + time->tv_sec = 0; + time->tv_nsec = 0; + } + + time->tv_sec += (time_t) second; + + return time->tv_sec < (time_t) second ? F_status_set_error(F_number_overflow) : F_none; + } +#endif // _di_f_time_spec_millisecond_ + +#ifndef _di_f_time_spec_nanosecond_ + extern f_status_t f_time_spec_nanosecond(const f_number_unsigned_t second, const f_number_unsigned_t nanosecond, struct timespec * const time) { + #ifndef _di_level_0_parameter_checking_ + if (!time) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (((time_t) second) < 0) { + time->tv_sec = 0; + time->tv_nsec = ((long) nanosecond) < 0 ? 0 : (long) nanosecond; + + return F_status_set_error(F_number_underflow); + } + + time->tv_sec = (time_t) second; + + if ((long) nanosecond < 0) { + time->tv_nsec = 0; + + return F_status_set_error(F_number_underflow); + } + + time->tv_nsec = (long) nanosecond; + + // If tv_nsec is 1 second or greater, then increment second. + if (time->tv_nsec >= 1000000000) { + ++(time->tv_sec); + + time->tv_nsec -= 1000000000; + + if (time->tv_sec < (time_t) second) return F_status_set_error(F_number_overflow); + } + + return F_none; + } +#endif // _di_f_time_spec_nanosecond_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_time/c/time.h b/level_0/f_time/c/time.h new file mode 100644 index 0000000..b87f304 --- /dev/null +++ b/level_0/f_time/c/time.h @@ -0,0 +1,86 @@ +/** + * FLL - Level 0 + * + * Project: Time + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Provides time related functionality. + */ +#ifndef _F_time_h +#define _F_time_h + +// Libc includes. +#include + +// FLL-0 includes. +#include +#include + +// FLL-0 time includes. + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create a timespec representing the given seconds and milliseconds. + * + * This is intended to work with "struct timespec" and is not intended to work with "f_time_spec_t". + * The "struct timespec" may have different lengths and so this function provides overflow and underflow protection. + * The "f_time_spec_t" should not need this as "f_time_spec_t" is intended to always be uint64_t. + * + * @param second + * The number of seconds. + * @param millisecond + * The number of milliseconds. + * @param time + * The time spec representing the given seconds and milliseconds. + * + * This is still updated on F_number_overflow, and the result of the overflow is still assigned to seconds. + * This is still updated on F_number_underflow, but the underflow value is set to 0. + * + * @return + * F_none on success. + * + * F_number_overflow (with error bit) if the summation of seconds and milliseconds results in an overflow when cast to appropriate integer type. + * F_number_underflow (with error bit) if the seconds or milliseconds is less than 0 when cast to appropriate integer type. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_time_spec_millisecond_ + extern f_status_t f_time_spec_millisecond(const f_number_unsigned_t second, const f_number_unsigned_t millisecond, struct timespec * const time); +#endif // _di_f_time_spec_millisecond_ + +/** + * Create a timespec representing the given seconds and nanoseconds. + * + * This is intended to work with "struct timespec" and is not intended to work with "f_time_spec_t". + * The "struct timespec" may have different lengths and so this function provides overflow and underflow protection. + * The "f_time_spec_t" should not need this as "f_time_spec_t" is intended to always be uint64_t. + * + * @param second + * The number of seconds. + * @param nanosecond + * The number of nanoseconds. + * @param time + * The time spec representing the given seconds and nanoseconds. + * + * This is still updated on F_number_overflow, and the result of the overflow is still assigned to second. + * This is still updated on F_number_underflow, but the underflow value is set to 0. + * + * @return + * F_none on success. + * + * F_number_overflow (with error bit) if the summation of seconds and nanoseconds results in an overflow when cast to appropriate integer type. + * F_number_underflow (with error bit) if the second or nanoseconds is less than 0 when cast to appropriate integer type. + * F_parameter (with error bit) if a parameter is invalid. + */ +#ifndef _di_f_time_spec_nanosecond_ + extern f_status_t f_time_spec_nanosecond(const f_number_unsigned_t second, const f_number_unsigned_t nanosecond, struct timespec * const time); +#endif // _di_f_time_spec_nanosecond_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_time_h diff --git a/level_0/f_time/data/build/defines b/level_0/f_time/data/build/defines new file mode 100644 index 0000000..4f13080 --- /dev/null +++ b/level_0/f_time/data/build/defines @@ -0,0 +1 @@ +# fss-0000 diff --git a/level_0/f_time/data/build/dependencies b/level_0/f_time/data/build/dependencies new file mode 100644 index 0000000..d9c4b77 --- /dev/null +++ b/level_0/f_time/data/build/dependencies @@ -0,0 +1,4 @@ +# fss-0000 + +f_type +f_status diff --git a/level_0/f_time/data/build/dependencies-tests b/level_0/f_time/data/build/dependencies-tests new file mode 100644 index 0000000..dea3179 --- /dev/null +++ b/level_0/f_time/data/build/dependencies-tests @@ -0,0 +1,3 @@ +# fss-0001 + +cmocka 1.* diff --git a/level_0/f_time/data/build/fakefile b/level_0/f_time/data/build/fakefile new file mode 100644 index 0000000..90a7134 --- /dev/null +++ b/level_0/f_time/data/build/fakefile @@ -0,0 +1,12 @@ +# fss-0005 iki-0002 + +settings: + fail exit + modes individual individual_thread level monolithic clang test fanalyzer thread threadless + + environment PATH LD_LIBRARY_PATH + environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +main: + + build diff --git a/level_0/f_time/data/build/functions b/level_0/f_time/data/build/functions new file mode 100644 index 0000000..67f0d67 --- /dev/null +++ b/level_0/f_time/data/build/functions @@ -0,0 +1,3 @@ +# fss-0005 + +main: diff --git a/level_0/f_time/data/build/macros b/level_0/f_time/data/build/macros new file mode 100644 index 0000000..67f0d67 --- /dev/null +++ b/level_0/f_time/data/build/macros @@ -0,0 +1,3 @@ +# fss-0005 + +main: diff --git a/level_0/f_time/data/build/settings b/level_0/f_time/data/build/settings new file mode 100644 index 0000000..6779d2c --- /dev/null +++ b/level_0/f_time/data/build/settings @@ -0,0 +1,71 @@ +# fss-0001 +# +# Modes: +# - individual: Compile using per project (individual) libraries, does not handle thread or threadless cases. +# - individual_thread: This is required when compiling in individual mode with "thread" mode. +# - level: Compile using per level libraries. +# - monolithic: Compile using per monolithic libraries. +# - clang: Use clang rather than the default, which is generally gcc. +# - test: Compile for a test, such as unit testing. +# - fanalyzer: Compile using GCC's -fanalyzer compile time option. +# - thread: Compile with thread support. +# - threadless: Compile without thread support. +# + +build_name f_time + +version_major 0 +version_minor 7 +version_micro 0 +version_file micro +version_target minor + +modes individual individual_thread level monolithic clang test fanalyzer thread threadless +modes_default individual individual_thread thread + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc +build_libraries-individual + +build_sources_library time.c + +build_sources_headers time.h + +build_script yes +build_shared yes +build_static no + +path_headers fll/level_0 +path_library_script script +path_library_shared shared +path_library_static static +path_object_script script +path_object_shared shared +path_object_static static +path_program_script script +path_program_shared shared +path_program_static static + +has_path_standard yes +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +environment PATH LD_LIBRARY_PATH +environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses +flags-clang -Wno-logical-op-parentheses +flags-test -O0 -fstack-protector -Wall +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ + +flags_library -fPIC +flags_object -fPIC +flags_program -fPIE diff --git a/level_0/f_time/data/build/settings-tests b/level_0/f_time/data/build/settings-tests new file mode 100644 index 0000000..3f1ad4f --- /dev/null +++ b/level_0/f_time/data/build/settings-tests @@ -0,0 +1,57 @@ +# fss-0001 +# +# Builds a program that is links to the generated library and is executed to perform tests. +# +# Memory leaks in the test program can be checked for by running valgrind with this executable. +# + +build_name test-f_time + +version_major 0 +version_minor 7 +version_micro 0 +version_file major +version_target major + +modes individual clang test coverage +modes_default individual test + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcmocka +build_libraries-individual -lf_time + +build_sources_program test-time-spec_millisecond.c test-time-spec_nanosecond.c +build_sources_program test-time.c + +build_script no +build_shared yes +build_static no + +path_headers tests/unit/c +path_sources tests/unit/c + +has_path_standard no +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +environment PATH LD_LIBRARY_PATH +environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + +defines -Ibuild/includes +defines_static -Lbuild/libraries/static +defines_shared -Lbuild/libraries/shared + +flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses +flags-clang -Wno-logical-op-parentheses +flags-test -fstack-protector -Wall +flags-coverage -O0 --coverage -fprofile-abs-path -fprofile-dir=build/coverage/ + +flags_program -fPIE diff --git a/level_0/f_time/data/build/structures b/level_0/f_time/data/build/structures new file mode 100644 index 0000000..67f0d67 --- /dev/null +++ b/level_0/f_time/data/build/structures @@ -0,0 +1,3 @@ +# fss-0005 + +main: diff --git a/level_0/f_time/data/build/testfile b/level_0/f_time/data/build/testfile new file mode 100644 index 0000000..dd9b6d8 --- /dev/null +++ b/level_0/f_time/data/build/testfile @@ -0,0 +1,63 @@ +# fss-0005 iki-0002 + +settings: + load_build yes + fail exit + + environment PATH LD_LIBRARY_PATH + environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH + environment CMOCKA_XML_FILE CMOCKA_MESSAGE_OUTPUT CMOCKA_TEST_ABORT + + # Cmocka is not fully thread-safe, set this to "1" to have cmocka call abort() on a test failure. + #CMOCKA_TEST_ABORT 1 + + # One of: STDOUT, SUBUNIT, TAP, or XML. + #define CMOCKA_MESSAGE_OUTPUT STDOUT + + # When in "XML" output mode, output to this file rather than stdout. + #define CMOCKA_XML_FILE ./out.xml + +main: + build settings individual test + build settings-tests individual test + + operate build_path + operate ld_library_path + + if exist parameter:"build_path"programs/shared/test-f_time + shell parameter:"build_path"programs/shared/test-f_time + + if exist parameter:"build_path"programs/static/test-f_time + shell parameter:"build_path"programs/static/test-f_time + + if not exist parameter:"build_path"programs/shared/test-f_time + and not exist parameter:"build_path"programs/static/test-f_time + operate not_created + +not_created: + print + print 'context:"error"Failed to test due to being unable to find either a shared or static test binary to perform tests. context:"reset"' + + exit failure + +build_path: + parameter build_path build/ + + if parameter build:value + parameter build_path parameter:"build:value" + +ld_library_path: + if define LD_LIBRARY_PATH + and parameter work:value + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:parameter:"work:value"libraries/shared:define:"LD_LIBRARY_PATH"' + + else + if define LD_LIBRARY_PATH + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:define:"LD_LIBRARY_PATH"' + + else + if parameter work:value + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared:parameter:"work:value"libraries/shared' + + else + define LD_LIBRARY_PATH 'parameter:"build_path"libraries/shared' diff --git a/level_0/f_time/tests/unit/c/test-time-spec_millisecond.c b/level_0/f_time/tests/unit/c/test-time-spec_millisecond.c new file mode 100644 index 0000000..47b0029 --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-spec_millisecond.c @@ -0,0 +1,131 @@ +#include "test-time.h" +#include "test-time-spec_millisecond.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_time_spec_millisecond__number_overflow(void **state) { + + f_number_unsigned_t second = 0; + + // If is signed, then... + if (((time_t) (-1)) < 0) { + second = (((f_number_unsigned_t) ((time_t) -1)) >> 1); + } + else { + second = (f_number_unsigned_t) ((time_t) (-1)); + } + + struct timespec time = { 0 }; + + { + const f_status_t status = f_time_spec_millisecond(second, 1000, &time); + + assert_int_equal(status, F_status_set_error(F_number_overflow)); + } + + { + const f_status_t status = f_time_spec_millisecond(second, 1000, &time); + + assert_int_equal(status, F_status_set_error(F_number_overflow)); + } +} + +void test__f_time_spec_millisecond__number_underflow(void **state) { + + struct timespec time = { .tv_sec = -1, .tv_nsec = -1 }; + + // If this system has uses unsigned, then this test is not needed. + if (time.tv_sec >= 0 && time.tv_nsec >= 0) return; + + if (time.tv_sec < 0) { + const f_status_t status = f_time_spec_millisecond(-1, 0, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + if (time.tv_sec < 0) { + const f_status_t status = f_time_spec_millisecond(-1, 1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + time.tv_nsec = -1; + + if (time.tv_nsec < 0) { + const f_status_t status = f_time_spec_millisecond(0, -1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + if (time.tv_nsec < 0) { + const f_status_t status = f_time_spec_millisecond(1, -1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } +} + +void test__f_time_spec_millisecond__parameter_checking(void **state) { + + { + const f_status_t status = f_time_spec_millisecond(0, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_time_spec_millisecond__works(void **state) { + + struct timespec time = { 0 }; + + { + const f_number_unsigned_t seconds[] = { + 1000, + 1000, + 0, + 0, + }; + + const f_number_unsigned_t milliseconds[] = { + 100, + 0, + 100, + 0, + }; + + for (uint8_t i = 0; i < 4; ++i) { + + const f_status_t status = f_time_spec_millisecond(seconds[i], milliseconds[i], &time); + + assert_int_equal(status, F_none); + assert_int_equal(time.tv_sec, seconds[i]); + assert_int_equal(time.tv_nsec, milliseconds[i] * 1000); + } // for + } + + { + const f_number_unsigned_t seconds[] = { + 1, + 2, + }; + + const f_number_unsigned_t milliseconds[] = { + 1000, + 1001, + }; + + for (uint8_t i = 0; i < 2; ++i) { + + const f_status_t status = f_time_spec_millisecond(seconds[i], milliseconds[i], &time); + + assert_int_equal(status, F_none); + assert_int_equal(time.tv_sec, seconds[i] + 1); + assert_int_equal(time.tv_nsec, (milliseconds[i] - 1000) * 1000); + } // for + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_time/tests/unit/c/test-time-spec_millisecond.h b/level_0/f_time/tests/unit/c/test-time-spec_millisecond.h new file mode 100644 index 0000000..b91483d --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-spec_millisecond.h @@ -0,0 +1,41 @@ +/** + * FLL - Level 0 + * + * Project: Time + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Test the time project. + */ +#ifndef _TEST__F_time_spec_millisecond_h +#define _TEST__F_time_spec_millisecond_h + +/** + * Test that function fails due to an overflow. + * + * @see f_time_spec_millisecond() + */ +extern void test__f_time_spec_millisecond__number_overflow(void **state); + +/** + * Test that function fails due to an underflow. + * + * @see f_time_spec_millisecond() + */ +extern void test__f_time_spec_millisecond__number_underflow(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_time_spec_millisecond() + */ +extern void test__f_time_spec_millisecond__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_time_spec_millisecond() + */ +extern void test__f_time_spec_millisecond__works(void **state); + +#endif // _TEST__F_time_spec_millisecond_h diff --git a/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.c b/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.c new file mode 100644 index 0000000..0de029b --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.c @@ -0,0 +1,131 @@ +#include "test-time.h" +#include "test-time-spec_nanosecond.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_time_spec_nanosecond__number_overflow(void **state) { + + f_number_unsigned_t second = 0; + + // If is signed, then... + if (((time_t) (-1)) < 0) { + second = (((f_number_unsigned_t) ((time_t) -1)) >> 1); + } + else { + second = (f_number_unsigned_t) ((time_t) (-1)); + } + + struct timespec time = { 0 }; + + { + const f_status_t status = f_time_spec_nanosecond(second, 1000000000, &time); + + assert_int_equal(status, F_status_set_error(F_number_overflow)); + } + + { + const f_status_t status = f_time_spec_nanosecond(second, 1000000000, &time); + + assert_int_equal(status, F_status_set_error(F_number_overflow)); + } +} + +void test__f_time_spec_nanosecond__number_underflow(void **state) { + + struct timespec time = { .tv_sec = -1, .tv_nsec = -1 }; + + // If this system has uses unsigned, then this test is not needed. + if (time.tv_sec >= 0 && time.tv_nsec >= 0) return; + + if (time.tv_sec < 0) { + const f_status_t status = f_time_spec_nanosecond(-1, 0, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + if (time.tv_sec < 0) { + const f_status_t status = f_time_spec_nanosecond(-1, 1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + time.tv_nsec = -1; + + if (time.tv_nsec < 0) { + const f_status_t status = f_time_spec_nanosecond(0, -1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } + + if (time.tv_nsec < 0) { + const f_status_t status = f_time_spec_nanosecond(1, -1, &time); + + assert_int_equal(status, F_status_set_error(F_number_underflow)); + } +} + +void test__f_time_spec_nanosecond__parameter_checking(void **state) { + + { + const f_status_t status = f_time_spec_nanosecond(0, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_time_spec_nanosecond__works(void **state) { + + struct timespec time = { 0 }; + + { + const f_number_unsigned_t seconds[] = { + 1000, + 1000, + 0, + 0, + }; + + const f_number_unsigned_t nanoseconds[] = { + 100, + 0, + 100, + 0, + }; + + for (uint8_t i = 0; i < 4; ++i) { + + const f_status_t status = f_time_spec_nanosecond(seconds[i], nanoseconds[i], &time); + + assert_int_equal(status, F_none); + assert_int_equal(time.tv_sec, seconds[i]); + assert_int_equal(time.tv_nsec, nanoseconds[i]); + } // for + } + + { + const f_number_unsigned_t seconds[] = { + 1, + 2, + }; + + const f_number_unsigned_t nanoseconds[] = { + 1000000000, + 1000000001, + }; + + for (uint8_t i = 0; i < 2; ++i) { + + const f_status_t status = f_time_spec_nanosecond(seconds[i], nanoseconds[i], &time); + + assert_int_equal(status, F_none); + assert_int_equal(time.tv_sec, seconds[i] + 1); + assert_int_equal(time.tv_nsec, (nanoseconds[i] - 1000000000)); + } // for + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.h b/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.h new file mode 100644 index 0000000..e13bd27 --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-spec_nanosecond.h @@ -0,0 +1,41 @@ +/** + * FLL - Level 0 + * + * Project: Time + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Test the time project. + */ +#ifndef _TEST__F_time_spec_nanosecond_h +#define _TEST__F_time_spec_nanosecond_h + +/** + * Test that function fails due to an overflow. + * + * @see f_time_spec_nanosecond() + */ +extern void test__f_time_spec_nanosecond__number_overflow(void **state); + +/** + * Test that function fails due to an underflow. + * + * @see f_time_spec_nanosecond() + */ +extern void test__f_time_spec_nanosecond__number_underflow(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_time_spec_nanosecond() + */ +extern void test__f_time_spec_nanosecond__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_time_spec_nanosecond() + */ +extern void test__f_time_spec_nanosecond__works(void **state); + +#endif // _TEST__F_time_spec_nanosecond_h diff --git a/level_0/f_time/tests/unit/c/test-time.c b/level_0/f_time/tests/unit/c/test-time.c new file mode 100644 index 0000000..7a1439d --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time.c @@ -0,0 +1,41 @@ +#include "test-time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int setup(void **state) { + + return 0; +} + +int setdown(void **state) { + + errno = 0; + + return 0; +} + +int main(void) { + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test__f_time_spec_millisecond__number_overflow), + cmocka_unit_test(test__f_time_spec_millisecond__number_underflow), + cmocka_unit_test(test__f_time_spec_millisecond__works), + + cmocka_unit_test(test__f_time_spec_nanosecond__number_overflow), + cmocka_unit_test(test__f_time_spec_nanosecond__number_underflow), + cmocka_unit_test(test__f_time_spec_nanosecond__works), + + #ifndef _di_level_0_parameter_checking_ + cmocka_unit_test(test__f_time_spec_millisecond__parameter_checking), + cmocka_unit_test(test__f_time_spec_nanosecond__parameter_checking), + #endif // _di_level_0_parameter_checking_ + }; + + return cmocka_run_group_tests(tests, setup, setdown); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_time/tests/unit/c/test-time.h b/level_0/f_time/tests/unit/c/test-time.h new file mode 100644 index 0000000..fede1ca --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time.h @@ -0,0 +1,72 @@ +/** + * FLL - Level 0 + * + * Project: Time + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Test the time project. + */ +#ifndef _TEST__F_time_h +#define _TEST__F_time_h + +// Libc includes. +#include +#include +#include +#include + +// cmocka includes. +#include + +// FLL-0 includes. +#include + +// Mock includes. + +// Test includes. +#include "test-time-spec_millisecond.h" +#include "test-time-spec_nanosecond.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform any setup operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setup(void **state); + +/** + * Peform any setdown operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setdown(void **state); + +/** + * Run all tests. + * + * @return + * The final result of the tests. + * + * @see cmocka_run_group_tests() + * @see cmocka_unit_test() + */ +extern int main(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _TEST__F_time_h diff --git a/level_0/f_type/c/type.h b/level_0/f_type/c/type.h index 2a79dc4..13e94b4 100644 --- a/level_0/f_type/c/type.h +++ b/level_0/f_type/c/type.h @@ -491,6 +491,8 @@ extern "C" { * 1 (Earth) second = 1000000000 Time or 1 GT (GigaTime). * * Consequentially, 1 day in units of Time is easily represented as 86.4 TT (TeraTime). + * + * This is not time_t, which is a different structure that may not be uint64_t and is often signed. */ #ifndef _di_f_time_t_ typedef uint64_t f_time_t; @@ -499,7 +501,10 @@ extern "C" { #endif // _di_f_time_t_ /** - * A non-kernel dependent version of "struct timespec". + * A non-kernel dependent alternative version of "struct timespec". + * + * This is may not compatible with struct timespec because seconds is time_t and that may not be a uint64_t. + * In many cases of "struct timespec", seconds is signed because time_t is signed. * * Properties: * - seconds: The total number of seconds. diff --git a/level_3/controller/c/controller/private-controller.c b/level_3/controller/c/controller/private-controller.c index 84efa55..4e36c4c 100644 --- a/level_3/controller/c/controller/private-controller.c +++ b/level_3/controller/c/controller/private-controller.c @@ -732,6 +732,7 @@ extern "C" { #ifndef _di_controller_time_milliseconds_ struct timespec controller_time_milliseconds(const f_number_unsigned_t milliseconds) { + // @todo update all of these type of functions to use the newer f_time_spec_milliseconds() and f_time_spec_nanoseconds() as appropriate. struct timespec time; time.tv_sec = milliseconds > 1000 ? milliseconds / 1000 : 0; -- 1.8.3.1