From 6efe473c112bea8e8702181f74ce92cd4083f72f Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 25 May 2026 17:27:14 +0100 Subject: [PATCH] fx: implement updated interfaces in builtin classes --- fx/include/fx/iterator.h | 35 +-- fx/include/fx/macros.h | 11 + fx/include/fx/object.h | 12 +- fx/include/fx/type.h | 3 + fx/iterator.c | 14 +- fx/object.c | 20 +- fx/printf.c | 619 ++++++++++++++++++++++++++------------ fx/string.c | 187 +++++++++--- fx/test/unicode-strings.c | 4 +- fx/test/values.c | 21 +- fx/type.c | 40 +++ fx/type.h | 9 + fx/uuid.c | 9 +- 13 files changed, 691 insertions(+), 293 deletions(-) diff --git a/fx/include/fx/iterator.h b/fx/include/fx/iterator.h index 020620b..2c9170a 100644 --- a/fx/include/fx/iterator.h +++ b/fx/include/fx/iterator.h @@ -4,31 +4,21 @@ #include #include #include +#include #include FX_DECLS_BEGIN; -#define fx_foreach(type, var, iterator) \ - for (type var = (type)fx_iterator_get_value(iterator).v_int; \ +#define fx_foreach(var, iterator) \ + for (fx_value var = fx_iterator_get_value(iterator); \ FX_OK(fx_iterator_get_status(iterator)); \ fx_iterator_move_next(iterator), \ - var = (type)fx_iterator_get_value(iterator).v_int) + var = fx_iterator_get_value(iterator)) #define fx_foreach_ptr(type, var, iterator) \ for (type *var = (type *)fx_iterator_get_value(iterator).v_ptr; \ FX_OK(fx_iterator_get_status(iterator)); \ fx_iterator_move_next(iterator), \ var = (type *)fx_iterator_get_value(iterator).v_ptr) -#define fx_foreach_c(type, var, iterator) \ - for (type var = (type)fx_iterator_get_cvalue(iterator).v_int; \ - FX_OK(fx_iterator_get_status(iterator)); \ - fx_iterator_move_next(iterator), \ - var = (type)fx_iterator_get_cvalue(iterator).v_int) -#define fx_foreach_cptr(type, var, iterator) \ - for (const type *var \ - = (const type *)fx_iterator_get_cvalue(iterator).v_cptr; \ - FX_OK(fx_iterator_get_status(iterator)); \ - fx_iterator_move_next(iterator), \ - var = (const type *)fx_iterator_get_cvalue(iterator).v_cptr) #define FX_ITERATOR_VALUE_INT(v) ((fx_iterator_value) {.v_int = (v)}) #define FX_ITERATOR_VALUE_PTR(v) ((fx_iterator_value) {.v_ptr = (v)}) @@ -39,12 +29,6 @@ FX_DECLS_BEGIN; #define FX_TYPE_ITERATOR (fx_iterator_get_type()) #define FX_TYPE_ITERABLE (fx_iterable_get_type()) -typedef union fx_iterator_value { - uintptr_t v_int; - void *v_ptr; - const void *v_cptr; -} fx_iterator_value; - __FX_DECLARE_TYPE(fx_iterator); FX_DECLARE_TYPE(fx_iterable); @@ -52,8 +36,7 @@ FX_DECLARE_TYPE(fx_iterable); FX_TYPE_CLASS_DECLARATION_BEGIN(fx_iterator) fx_status (*it_move_next)(const fx_iterator *); fx_status (*it_erase)(fx_iterator *); - fx_iterator_value (*it_get_value)(fx_iterator *); - const fx_iterator_value (*it_get_cvalue)(const fx_iterator *); + fx_value (*it_get_value)(const fx_iterator *); FX_TYPE_CLASS_DECLARATION_END(fx_iterator) FX_TYPE_CLASS_DECLARATION_BEGIN(fx_iterable) @@ -77,13 +60,11 @@ FX_API fx_iterator *fx_iterator_begin(fx_iterable *it); FX_API const fx_iterator *fx_iterator_cbegin(const fx_iterable *it); FX_API fx_status fx_iterator_get_status(const fx_iterator *it); -FX_API fx_status fx_iterator_set_status( - const fx_iterator *it, - fx_status status); +FX_API fx_status +fx_iterator_set_status(const fx_iterator *it, fx_status status); FX_API fx_status fx_iterator_move_next(const fx_iterator *it); -FX_API fx_iterator_value fx_iterator_get_value(fx_iterator *it); -FX_API const fx_iterator_value fx_iterator_get_cvalue(const fx_iterator *it); +FX_API fx_value fx_iterator_get_value(const fx_iterator *it); FX_API fx_status fx_iterator_erase(fx_iterator *it); static inline bool fx_iterator_is_valid(const fx_iterator *it) { diff --git a/fx/include/fx/macros.h b/fx/include/fx/macros.h index 6b27a53..29f4163 100644 --- a/fx/include/fx/macros.h +++ b/fx/include/fx/macros.h @@ -67,6 +67,16 @@ fx_type_add_function(ty, func); \ } \ } while (0) +#define FX_TYPE_PROPERTY(name, get, set) \ + do { \ + if (ty) { \ + fx_property *prop = fx_property_create( \ + name, \ + get, \ + set); \ + fx_type_add_property(ty, prop); \ + } \ + } while (0) #define FX_TYPE_VTABLE_INTERFACE_BEGIN(interface_name, interface_id) \ interface_name##_class *__FX_IFACE_I1(iface, __LINE__) \ @@ -256,6 +266,7 @@ } \ self = fx_assembly_create(); #define FX_ASSEMBLY_END(id) \ + fx_assembly_register(self); \ return self; \ } \ const fx_assembly *__fx_assembly_get(void) \ diff --git a/fx/include/fx/object.h b/fx/include/fx/object.h index cbbc69c..703ed0e 100644 --- a/fx/include/fx/object.h +++ b/fx/include/fx/object.h @@ -14,9 +14,14 @@ #define FX_RV(p) (fx_object_make_rvalue(p)) typedef FX_TYPE_FWDREF(fx_object) fx_object; +struct fx_value; typedef struct _fx_object_class { - void (*to_string)(const fx_object *, FX_TYPE_FWDREF(fx_stream) *); + fx_status (*to_string)( + const struct fx_value *, + FX_TYPE_FWDREF(fx_stream) *, + const char *); + fx_status (*hash)(const struct fx_value *, uint64_t *); } fx_object_class; FX_API fx_type_id fx_object_get_type(void); @@ -37,9 +42,10 @@ FX_API void fx_object_unref(fx_object *p); FX_API fx_object *fx_object_make_rvalue(fx_object *p); FX_API fx_object *fx_object_create(fx_type_id type); -FX_API void fx_object_to_string( +FX_API fx_status fx_object_to_string( const fx_object *p, - FX_TYPE_FWDREF(fx_stream) * out); + FX_TYPE_FWDREF(fx_stream) * out, + const char *format); FX_API bool fx_object_is_type(const fx_object *p, fx_type_id type); #endif diff --git a/fx/include/fx/type.h b/fx/include/fx/type.h index 54346ad..c280e66 100644 --- a/fx/include/fx/type.h +++ b/fx/include/fx/type.h @@ -64,6 +64,7 @@ typedef struct fx_type_info { struct fx_bst ty_components; struct fx_queue ty_class_hierarchy; struct fx_namemap ty_functions; + struct fx_namemap ty_properties; size_t ty_instance_size, ty_class_size; } fx_type_info; @@ -93,5 +94,7 @@ static inline int fx_type_id_compare(fx_type_id a, fx_type_id b) FX_API fx_result fx_type_register(fx_type_info *info); FX_API fx_result fx_type_add_function(fx_type_info *info, struct _fx_object *func); +FX_API fx_result +fx_type_add_property(fx_type_info *info, struct _fx_object *prop); #endif diff --git a/fx/iterator.c b/fx/iterator.c index 5199271..0f40aea 100644 --- a/fx/iterator.c +++ b/fx/iterator.c @@ -74,26 +74,16 @@ enum fx_status fx_iterator_move_next(const fx_iterator *it) return status; } -fx_iterator_value fx_iterator_get_value(fx_iterator *it) +fx_value fx_iterator_get_value(const fx_iterator *it) { FX_CLASS_DISPATCH_VIRTUAL_0( fx_iterator, FX_TYPE_ITERATOR, - FX_ITERATOR_VALUE_NULL, + FX_VALUE_EMPTY, it_get_value, it); } -const fx_iterator_value fx_iterator_get_cvalue(const fx_iterator *it) -{ - FX_CLASS_DISPATCH_VIRTUAL_0( - fx_iterator, - FX_TYPE_ITERATOR, - FX_ITERATOR_VALUE_NULL, - it_get_cvalue, - it); -} - fx_status fx_iterator_erase(fx_iterator *it) { enum fx_status status = FX_ERR_NOT_SUPPORTED; diff --git a/fx/object.c b/fx/object.c index 33db6db..e9507dd 100644 --- a/fx/object.c +++ b/fx/object.c @@ -85,15 +85,19 @@ struct _fx_object *fx_object_create(fx_type_id type) return out; } -void fx_object_to_string(const struct _fx_object *p, fx_stream *out) +fx_status fx_object_to_string( + const struct _fx_object *p, + fx_stream *out, + const char *format) { - FX_CLASS_DISPATCH_VIRTUAL_V( - fx_object, - FX_TYPE_OBJECT, - to_string, - p, - out); - fx_stream_write_fmt(out, NULL, "<%s@%p>", p->obj_type->ty_name, p); + fx_value value = FX_VALUE_OBJECT(p); + fx_object_class *iface = fx_object_get_interface(p, FX_TYPE_OBJECT); + if (iface && iface->to_string) { + return iface->to_string(&value, out, format); + } + + fx_stream_write_fmt(out, NULL, "%s", p->obj_type->ty_name); + return FX_SUCCESS; } bool fx_object_is_type(const struct _fx_object *p, fx_type_id type) diff --git a/fx/printf.c b/fx/printf.c index cf36096..b5bf8a0 100644 --- a/fx/printf.c +++ b/fx/printf.c @@ -81,12 +81,14 @@ #define PRINTF_DECIMAL_BUFFER_SIZE 32 #endif -// Support for the decimal notation floating point conversion specifiers (%f, %F) +// Support for the decimal notation floating point conversion specifiers (%f, +// %F) #ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS #define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 #endif -// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) +// Support for the exponential notation floating point conversion specifiers +// (%e, %g, %E, %G) #ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS #define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 #endif @@ -96,7 +98,8 @@ #define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 #endif -// Default precision for the floating point conversion specifiers (the C standard sets this at 6) +// Default precision for the floating point conversion specifiers (the C +// standard sets this at 6) #ifndef PRINTF_DEFAULT_FLOAT_PRECISION #define PRINTF_DEFAULT_FLOAT_PRECISION 6 #endif @@ -143,12 +146,14 @@ /////////////////////////////////////////////////////////////////////////////// -// The following will convert the number-of-digits into an exponential-notation literal +// The following will convert the number-of-digits into an exponential-notation +// literal #define PRINTF_CONCATENATE(s1, s2) s1##s2 #define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) #define PRINTF_FLOAT_NOTATION_THRESHOLD \ ((floating_point_t)PRINTF_EXPAND_THEN_CONCATENATE( \ - 1e, PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) + 1e, \ + PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) // internal flag definitions #define FLAGS_ZEROPAD (1U << 0U) @@ -323,18 +328,21 @@ static inline int get_exp2(floating_point_with_bit_access x) // with a non-trivial representation: An unsigned offset from some // negative value (with the extremal offset values reserved for special // use). - return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS) & FP_TYPE_EXPONENT_MASK) + return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS) + & FP_TYPE_EXPONENT_MASK) - FP_TYPE_BASE_EXPONENT; } #define PRINTF_ABS(_x) ((_x) > 0 ? (_x) : -(_x)) -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || + // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) // Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid // and well-defined, but if you're not careful you can easily trigger undefined // behavior with -LONG_MIN or -LLONG_MIN #define ABS_FOR_PRINTING(_x) \ - ((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x))) + ((printf_unsigned_value_t)((_x) > 0 ? (_x) \ + : -((printf_signed_value_t)_x))) // wrapper (used as buffer) for output function type // @@ -368,8 +376,9 @@ static inline void putchar_via_gadget(output_gadget_t *gadget, char c) // No check for c == '\0' . gadget->function(c, gadget->extra_function_arg); } else { - // it must be the case that gadget->buffer != NULL , due to the constraint - // on output_gadget_t ; and note we're relying on write_pos being non-negative. + // it must be the case that gadget->buffer != NULL , due to the + // constraint on output_gadget_t ; and note we're relying on + // write_pos being non-negative. gadget->buffer[write_pos] = c; } } @@ -423,7 +432,8 @@ static inline output_gadget_t buffer_gadget(char *buffer, size_t buffer_size) } static inline output_gadget_t function_gadget( - void (*function)(char, void *), void *extra_arg) + void (*function)(char, void *), + void *extra_arg) { output_gadget_t result = discarding_gadget(); result.function = function; @@ -438,7 +448,8 @@ static inline output_gadget_t extern_putchar_gadget(void) } // internal secure strlen -// @return The length of the string (excluding the terminating 0) limited by 'maxsize' +// @return The length of the string (excluding the terminating 0) limited by +// 'maxsize' // @note strlen uses size_t, but wes only use this function with printf_size_t // variables - hence the signature. static inline printf_size_t strnlen_s_(const char *str, printf_size_t maxsize) @@ -468,8 +479,11 @@ static printf_size_t atou_(const char **str) // output the specified string in reverse, taking care of any zero-padding static void out_rev_( - output_gadget_t *output, const char *buf, printf_size_t len, - printf_size_t width, printf_flags_t flags) + output_gadget_t *output, + const char *buf, + printf_size_t len, + printf_size_t width, + printf_flags_t flags) { const printf_size_t start_pos = output->pos; @@ -493,11 +507,17 @@ static void out_rev_( } } -// Invoked by print_integer after the actual number has been printed, performing necessary -// work on the number's prefix (as the number is initially printed in reverse order) +// Invoked by print_integer after the actual number has been printed, performing +// necessary work on the number's prefix (as the number is initially printed in +// reverse order) static void print_integer_finalization( - output_gadget_t *output, char *buf, printf_size_t len, bool negative, - numeric_base_t base, printf_size_t precision, printf_size_t width, + output_gadget_t *output, + char *buf, + printf_size_t len, + bool negative, + numeric_base_t base, + printf_size_t precision, + printf_size_t width, printf_flags_t flags) { printf_size_t unpadded_len = len; @@ -506,7 +526,8 @@ static void print_integer_finalization( { if (!(flags & FLAGS_LEFT)) { if (width && (flags & FLAGS_ZEROPAD) - && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + && (negative + || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { width--; } while ((flags & FLAGS_ZEROPAD) && (len < width) @@ -515,12 +536,14 @@ static void print_integer_finalization( } } - while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + while ((len < precision) + && (len < PRINTF_INTEGER_BUFFER_SIZE)) { buf[len++] = '0'; } if (base == BASE_OCTAL && (len > unpadded_len)) { - // Since we've written some zeros, we've satisfied the alternative format leading space requirement + // Since we've written some zeros, we've satisfied the + // alternative format leading space requirement flags &= ~FLAGS_HASH; } } @@ -560,7 +583,8 @@ static void print_integer_finalization( if (negative) { buf[len++] = '-'; } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists + buf[len++] = '+'; // ignore the space if the '+' + // exists } else if (flags & FLAGS_SPACE) { buf[len++] = ' '; } @@ -571,8 +595,12 @@ static void print_integer_finalization( // An internal itoa-like function static void print_integer( - output_gadget_t *output, printf_unsigned_value_t value, bool negative, - numeric_base_t base, printf_size_t precision, printf_size_t width, + output_gadget_t *output, + printf_unsigned_value_t value, + bool negative, + numeric_base_t base, + printf_size_t precision, + printf_size_t width, printf_flags_t flags) { char buf[PRINTF_INTEGER_BUFFER_SIZE]; @@ -595,17 +623,25 @@ static void print_integer( } else { do { const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit - : (flags & FLAGS_UPPERCASE - ? 'A' - : 'a') - + digit - 10); + buf[len++] = (char)(digit < 10 + ? '0' + digit + : (flags & FLAGS_UPPERCASE + ? 'A' + : 'a') + + digit - 10); value /= base; } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); } print_integer_finalization( - output, buf, len, negative, base, precision, width, flags); + output, + buf, + len, + negative, + base, + precision, + width, + flags); } #if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) @@ -620,7 +656,8 @@ struct floating_point_components { bool is_negative; }; -static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] +static const floating_point_t + powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] = {1e00, 1e01, 1e02, @@ -649,24 +686,27 @@ static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + // for avoiding buffer overruns and such #define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1) -// Break up a floating-point number - which is known to be a finite non-negative number - -// into its base-10 parts: integral - before the decimal point, and fractional - after it. -// Taken the precision into account, but does not change it even internally. +// Break up a floating-point number - which is known to be a finite non-negative +// number - into its base-10 parts: integral - before the decimal point, and +// fractional - after it. Taken the precision into account, but does not change +// it even internally. static struct floating_point_components get_components( - floating_point_t number, printf_size_t precision) + floating_point_t number, + printf_size_t precision) { struct floating_point_components number_; number_.is_negative = get_sign_bit(number); floating_point_t abs_number = (number_.is_negative) ? -number : number; number_.integral = (int_fast64_t)abs_number; - floating_point_t scaled_remainder - = (abs_number - (floating_point_t)number_.integral) - * powers_of_10[precision]; + floating_point_t scaled_remainder = (abs_number + - (floating_point_t) + number_.integral) + * powers_of_10[precision]; number_.fractional = (int_fast64_t) scaled_remainder; // for precision == 0U, this will be 0 - floating_point_t remainder - = scaled_remainder - (floating_point_t)number_.fractional; + floating_point_t remainder = scaled_remainder + - (floating_point_t)number_.fractional; const floating_point_t one_half = (floating_point_t)0.5; if (remainder > one_half) { @@ -697,18 +737,21 @@ static struct floating_point_components get_components( #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS struct scaling_factor { floating_point_t raw_factor; - bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it + bool multiply; // if true, need to multiply by raw_factor; otherwise + // need to divide by it }; static floating_point_t apply_scaling( - floating_point_t num, struct scaling_factor normalization) + floating_point_t num, + struct scaling_factor normalization) { return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; } static floating_point_t unapply_scaling( - floating_point_t normalized, struct scaling_factor normalization) + floating_point_t normalized, + struct scaling_factor normalization) { #ifdef __GNUC__ // accounting for a static analysis bug in GCC 6.x and earlier @@ -727,7 +770,8 @@ static floating_point_t unapply_scaling( } static struct scaling_factor update_normalization( - struct scaling_factor sf, floating_point_t extra_multiplicative_factor) + struct scaling_factor sf, + floating_point_t extra_multiplicative_factor) { struct scaling_factor result; if (sf.multiply) { @@ -735,26 +779,29 @@ static struct scaling_factor update_normalization( result.raw_factor = sf.raw_factor * extra_multiplicative_factor; } else { int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); - int extra_factor_exp2 - = get_exp2(get_bit_access(extra_multiplicative_factor)); + int extra_factor_exp2 = get_exp2( + get_bit_access(extra_multiplicative_factor)); // Divide the larger-exponent raw raw_factor by the smaller if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { result.multiply = false; - result.raw_factor - = sf.raw_factor / extra_multiplicative_factor; + result.raw_factor = sf.raw_factor + / extra_multiplicative_factor; } else { result.multiply = true; - result.raw_factor - = extra_multiplicative_factor / sf.raw_factor; + result.raw_factor = extra_multiplicative_factor + / sf.raw_factor; } } return result; } static struct floating_point_components get_normalized_components( - bool negative, printf_size_t precision, floating_point_t non_normalized, - struct scaling_factor normalization, int floored_exp10) + bool negative, + printf_size_t precision, + floating_point_t non_normalized, + struct scaling_factor normalization, + int floored_exp10) { struct floating_point_components components; components.is_negative = negative; @@ -763,39 +810,47 @@ static struct floating_point_components get_normalized_components( bool close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= FP_TYPE_MAX_10_EXP - 1); if (close_to_representation_extremum) { - // We can't have a normalization factor which also accounts for the precision, i.e. moves - // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. - // So, we'll give up early on getting extra precision... + // We can't have a normalization factor which also accounts for + // the precision, i.e. moves some decimal digits into the + // mantissa, since it's unrepresentable, or nearly + // unrepresentable. So, we'll give up early on getting extra + // precision... return get_components(negative ? -scaled : scaled, precision); } components.integral = (int_fast64_t)scaled; - floating_point_t remainder - = non_normalized - - unapply_scaling( - (floating_point_t)components.integral, normalization); + floating_point_t remainder = non_normalized + - unapply_scaling( + (floating_point_t) + components.integral, + normalization); floating_point_t prec_power_of_10 = powers_of_10[precision]; - struct scaling_factor account_for_precision - = update_normalization(normalization, prec_power_of_10); - floating_point_t scaled_remainder - = apply_scaling(remainder, account_for_precision); + struct scaling_factor account_for_precision = update_normalization( + normalization, + prec_power_of_10); + floating_point_t scaled_remainder = apply_scaling( + remainder, + account_for_precision); floating_point_t rounding_threshold = 0.5; components.fractional = (int_fast64_t) - scaled_remainder; // when precision == 0, the assigned value should be 0 - scaled_remainder - -= (floating_point_t)components - .fractional; // when precision == 0, this will not change scaled_remainder + scaled_remainder; // when precision == 0, the assigned value + // should be 0 + scaled_remainder -= (floating_point_t)components + .fractional; // when precision == 0, this + // will not change + // scaled_remainder components.fractional += (scaled_remainder >= rounding_threshold); if (scaled_remainder == rounding_threshold) { - // banker's rounding: Round towards the even number (making the mean error 0) + // banker's rounding: Round towards the even number (making the + // mean error 0) components.fractional &= ~((int_fast64_t)0x1); } - // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), - // and must then be corrected into (1, 0). - // Note: for precision = 0, this will "translate" the rounding effect from - // the fractional part to the integral part where it should actually be - // felt (as prec_power_of_10 is 1) + // handle rollover, e.g. the case of 0.99 with precision 1 becoming + // (0,100), and must then be corrected into (1, 0). Note: for precision + // = 0, this will "translate" the rounding effect from the fractional + // part to the integral part where it should actually be felt (as + // prec_power_of_10 is 1) if ((floating_point_t)components.fractional >= prec_power_of_10) { components.fractional = 0; ++components.integral; @@ -805,9 +860,13 @@ static struct floating_point_components get_normalized_components( #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS static void print_broken_up_decimal( - struct floating_point_components number_, output_gadget_t *output, - printf_size_t precision, printf_size_t width, printf_flags_t flags, - char *buf, printf_size_t len) + struct floating_point_components number_, + output_gadget_t *output, + printf_size_t precision, + printf_size_t width, + printf_flags_t flags, + char *buf, + printf_size_t len) { if (precision != 0U) { // do fractional part, as an unsigned number @@ -833,14 +892,15 @@ static void print_broken_up_decimal( || (flags & FLAGS_HASH)) { while (len < PRINTF_DECIMAL_BUFFER_SIZE) { --count; - buf[len++] - = (char)('0' + number_.fractional % 10U); + buf[len++] = (char)('0' + + number_.fractional % 10U); if (!(number_.fractional /= 10U)) { break; } } // add extra 0s - while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) + && (count > 0U)) { buf[len++] = '0'; --count; } @@ -849,7 +909,8 @@ static void print_broken_up_decimal( } } } else { - if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { + if ((flags & FLAGS_HASH) + && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { buf[len++] = '.'; } } @@ -879,7 +940,8 @@ static void print_broken_up_decimal( if (number_.is_negative) { buf[len++] = '-'; } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists + buf[len++] = '+'; // ignore the space if the '+' + // exists } else if (flags & FLAGS_SPACE) { buf[len++] = ' '; } @@ -890,12 +952,25 @@ static void print_broken_up_decimal( // internal ftoa for fixed decimal floating point static void print_decimal_number( - output_gadget_t *output, floating_point_t number, printf_size_t precision, - printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) + output_gadget_t *output, + floating_point_t number, + printf_size_t precision, + printf_size_t width, + printf_flags_t flags, + char *buf, + printf_size_t len) { - struct floating_point_components value_ - = get_components(number, precision); - print_broken_up_decimal(value_, output, precision, width, flags, buf, len); + struct floating_point_components value_ = get_components( + number, + precision); + print_broken_up_decimal( + value_, + output, + precision, + width, + flags, + buf, + len); } #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS @@ -915,46 +990,69 @@ static int bastardized_floor(floating_point_t x) // positive number (not infinity or NaN, nor a sub-normal) static floating_point_t log10_of_positive(floating_point_t positive_number) { - // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + // The implementation follows David Gay + // (https://www.ampl.com/netlib/fp/dtoa.c). // - // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of - // our input number, and need only solve log_10(M) for M between 1 and 2 (as - // the base-2 mantissa is always 1-point-something). In that limited range, a - // Taylor series expansion of log10(x) should serve us well enough; and we'll - // take the mid-point, 1.5, as the point of expansion. + // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the + // components of our input number, and need only solve log_10(M) for M + // between 1 and 2 (as the base-2 mantissa is always 1-point-something). + // In that limited range, a Taylor series expansion of log10(x) should + // serve us well enough; and we'll take the mid-point, 1.5, as the point + // of expansion. floating_point_with_bit_access dwba = get_bit_access(positive_number); - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + // based on the algorithm by David Gay + // (https://www.ampl.com/netlib/fp/dtoa.c) int exp2 = get_exp2(dwba); // drop the exponent, so dwba.F comes into the range [1,2) dwba.U = (dwba.U - & (((printf_fp_uint_t)(1) << FP_TYPE_STORED_MANTISSA_BITS) - 1U)) + & (((printf_fp_uint_t)(1) << FP_TYPE_STORED_MANTISSA_BITS) + - 1U)) | ((printf_fp_uint_t)FP_TYPE_BASE_EXPONENT << FP_TYPE_STORED_MANTISSA_BITS); floating_point_t z = (dwba.F - (floating_point_t)1.5); return ( // Taylor expansion around 1.5: - (floating_point_t)0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) - + z * (floating_point_t)0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) + (floating_point_t)0.1760912590556812420 // Expansion term 0: + // ln(1.5) / ln(10) + + z * (floating_point_t)0.2895296546021678851 // Expansion + // term 1: (M + // - 1.5) * + // 2/3 / ln(10) #if PRINTF_LOG10_TAYLOR_TERMS > 2 - - z * z * (floating_point_t)0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) + - z * z * (floating_point_t)0.0965098848673892950 // Expansion + // term 2: + // (M + // - 1.5)^2 + // * 2/9 / + // ln(10) #if PRINTF_LOG10_TAYLOR_TERMS > 3 - + z - * z * z * (floating_point_t)0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) + + z * z * z + * (floating_point_t)0.0428932821632841311 // Expansion + // term 2: + // (M + // - 1.5)^3 + // * 8/81 + // / + // ln(10) #endif #endif // exact log_2 of the exponent x, with logarithm base change - + (floating_point_t)exp2 * (floating_point_t)0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + + (floating_point_t)exp2 + * (floating_point_t)0.30102999566398119521 // = exp2 + // * log_10(2) = exp2 * ln(2)/ln(10) ); } static floating_point_t pow10_of_int(int floored_exp10) { - // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + // A crude hack for avoiding undesired behavior with barely-normal or + // slightly-subnormal values. if (floored_exp10 == FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10) { return FP_TYPE_MAX_SUBNORMAL_POWER_OF_10; } - // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow + // Compute 10^(floored_exp10) but (try to) make sure that doesn't + // overflow floating_point_with_bit_access dwba; int exp2 = bastardized_floor( (floating_point_t)(floored_exp10 * 3.321928094887362 + 0.5)); @@ -965,17 +1063,24 @@ static floating_point_t pow10_of_int(int floored_exp10) dwba.U = ((printf_fp_uint_t)(exp2) + FP_TYPE_BASE_EXPONENT) << FP_TYPE_STORED_MANTISSA_BITS; // compute exp(z) using continued fractions, - // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + // see + // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); return dwba.F; } static void print_exponential_number( - output_gadget_t *output, floating_point_t number, printf_size_t precision, - printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) + output_gadget_t *output, + floating_point_t number, + printf_size_t precision, + printf_size_t width, + printf_flags_t flags, + char *buf, + printf_size_t len) { const bool negative = get_sign_bit(number); - // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + // This number will decrease gradually (by factors of 10) as we + // "extract" the exponent out of it floating_point_t abs_number = negative ? -number : number; int floored_exp10; @@ -984,9 +1089,10 @@ static void print_exponential_number( // Determine the decimal exponent if (abs_number == (floating_point_t)0.0) { - // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. - floored_exp10 - = 0; // ... and no need to set a normalization factor or check the powers table + // TODO: This is a special-case for 0.0 (and -0.0); but proper + // handling is required for denormals more generally. + floored_exp10 = 0; // ... and no need to set a normalization + // factor or check the powers table } else { floating_point_t exp10 = log10_of_positive(abs_number); floored_exp10 = bastardized_floor(exp10); @@ -999,10 +1105,10 @@ static void print_exponential_number( abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; - normalization.raw_factor - = abs_exp10_covered_by_powers_table - ? powers_of_10[PRINTF_ABS(floored_exp10)] - : p10; + normalization.raw_factor = abs_exp10_covered_by_powers_table + ? powers_of_10[PRINTF_ABS( + floored_exp10)] + : p10; } // We now begin accounting for the widths of the two parts of our @@ -1014,9 +1120,11 @@ static void print_exponential_number( bool fall_back_to_decimal_only_mode = false; if (flags & FLAGS_ADAPT_EXP) { - int required_significant_digits - = (precision == 0) ? 1 : (int)precision; - // Should we want to fall-back to "%f" mode, and only print the decimal part? + int required_significant_digits = (precision == 0) + ? 1 + : (int)precision; + // Should we want to fall-back to "%f" mode, and only print the + // decimal part? fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); @@ -1025,12 +1133,17 @@ static void print_exponential_number( // "%g" mode, "precision" is the number of _significant digits_, // and this is when we "translate" the precision value to an // actual number of decimal digits. - int precision_ - = fall_back_to_decimal_only_mode ? (int)precision - 1 - - floored_exp10 - : (int)precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point + int precision_ = fall_back_to_decimal_only_mode + ? (int)precision - 1 - floored_exp10 + : (int)precision + - 1; // the presence of the + // exponent ensures only + // one significant digit + // comes before the + // decimal point precision = (precision_ > 0 ? (unsigned)precision_ : 0U); - flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above + flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal + // respects our choice above } #ifdef __GNUC__ @@ -1052,10 +1165,14 @@ static void print_exponential_number( struct floating_point_components decimal_part_components = should_skip_normalization ? get_components( - negative ? -abs_number : abs_number, precision) + negative ? -abs_number : abs_number, + precision) : get_normalized_components( - negative, precision, abs_number, - normalization, floored_exp10); + negative, + precision, + abs_number, + normalization, + floored_exp10); // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which // effects the exponent and may require additional tweaking of the parts @@ -1063,13 +1180,17 @@ static void print_exponential_number( if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { - floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used + floored_exp10++; // Not strictly necessary, since + // floored_exp10 is no longer really + // used if (precision > 0U) { precision--; } - // ... and it should already be the case that decimal_part_components.fractional == 0 + // ... and it should already be the case that + // decimal_part_components.fractional == 0 } - // TODO: What about rollover strictly within the fractional part? + // TODO: What about rollover strictly within the fractional + // part? } else { if (decimal_part_components.integral >= 10) { floored_exp10++; @@ -1078,8 +1199,9 @@ static void print_exponential_number( } } - // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double - // is "307" (for 2^1023), so we set aside 4-5 characters overall + // the floored_exp10 format is "E%+03d" and largest possible + // floored_exp10 value for a 64-bit double is "307" (for 2^1023), so we + // set aside 4-5 characters overall printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U : 5U; @@ -1092,12 +1214,15 @@ static void print_exponential_number( // part's, so we'll use as many characters as we need: 0U : - // We're padding on the left; so the width constraint is the decimal part's - // problem. Well, can both the decimal part and the exponent part fit within our overall width? + // We're padding on the left; so the width constraint is + // the decimal part's problem. Well, can both the + // decimal part and the exponent part fit within our + // overall width? ((width > exp10_part_width) ? // Yes, so we limit our decimal part's width. - // (Note this is trivially valid even if we've fallen back to "%f" mode) + // (Note this is trivially valid even if + // we've fallen back to "%f" mode) width - exp10_part_width : // No; we just give up on any restriction on @@ -1107,17 +1232,31 @@ static void print_exponential_number( const printf_size_t printed_exponential_start_pos = output->pos; print_broken_up_decimal( - decimal_part_components, output, precision, decimal_part_width, - flags, buf, len); + decimal_part_components, + output, + precision, + decimal_part_width, + flags, + buf, + len); if (!fall_back_to_decimal_only_mode) { - putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + putchar_via_gadget( + output, + (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); print_integer( - output, ABS_FOR_PRINTING(floored_exp10), floored_exp10 < 0, - 10, 0, exp10_part_width - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + output, + ABS_FOR_PRINTING(floored_exp10), + floored_exp10 < 0, + 10, + 0, + exp10_part_width - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); if (flags & FLAGS_LEFT) { - // We need to right-pad with spaces to meet the width requirement - while (output->pos - printed_exponential_start_pos < width) { + // We need to right-pad with spaces to meet the width + // requirement + while (output->pos - printed_exponential_start_pos + < width) { putchar_via_gadget(output, ' '); } } @@ -1126,8 +1265,12 @@ static void print_exponential_number( #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS static void print_floating_point( - output_gadget_t *output, floating_point_t value, printf_size_t precision, - printf_size_t width, printf_flags_t flags, bool prefer_exponential) + output_gadget_t *output, + floating_point_t value, + printf_size_t precision, + printf_size_t width, + printf_flags_t flags, + bool prefer_exponential) { char buf[PRINTF_DECIMAL_BUFFER_SIZE]; printf_size_t len = 0U; @@ -1143,8 +1286,11 @@ static void print_floating_point( } if (value > FP_TYPE_MAX) { out_rev_( - output, (flags & FLAGS_PLUS) ? "fni+" : "fni", - (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + output, + (flags & FLAGS_PLUS) ? "fni+" : "fni", + (flags & FLAGS_PLUS) ? 4U : 3U, + width, + flags); return; } @@ -1157,7 +1303,13 @@ static void print_floating_point( // necessitating a more complicated implementation. #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS print_exponential_number( - output, value, precision, width, flags, buf, len); + output, + value, + precision, + width, + flags, + buf, + len); #endif return; } @@ -1167,24 +1319,39 @@ static void print_floating_point( precision = PRINTF_DEFAULT_FLOAT_PRECISION; } - // limit precision so that our integer holding the fractional part does not overflow + // limit precision so that our integer holding the fractional part does + // not overflow while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { - buf[len++] = '0'; // This respects the precision in terms of result length only + buf[len++] = '0'; // This respects the precision in terms of + // result length only precision--; } #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS if (prefer_exponential) print_exponential_number( - output, value, precision, width, flags, buf, len); + output, + value, + precision, + width, + flags, + buf, + len); else #endif print_decimal_number( - output, value, precision, width, flags, buf, len); + output, + value, + precision, + width, + flags, + buf, + len); } -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || + // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) // Advances the format pointer past the flags, and returns the parsed flags // due to the characters passed @@ -1220,7 +1387,9 @@ static printf_flags_t parse_flags(const char **format) } static inline void format_string_loop( - output_gadget_t *output, const char *format, va_list args) + output_gadget_t *output, + const char *format, + va_list args) { #if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER #define ADVANCE_IN_FORMAT_STRING(cptr_) \ @@ -1240,7 +1409,8 @@ static inline void format_string_loop( format++; continue; } - // We're parsing a format specifier: %[flags][width][.precision][length] + // We're parsing a format specifier: + // %[flags][width][.precision][length] ADVANCE_IN_FORMAT_STRING(format); printf_flags_t flags = parse_flags(&format); @@ -1382,7 +1552,9 @@ static inline void format_string_loop( base = BASE_BINARY; } else { base = BASE_DECIMAL; - flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation + flags &= ~FLAGS_HASH; // decimal integers have + // no alternative + // presentation } if (*format == 'X') { @@ -1396,39 +1568,62 @@ static inline void format_string_loop( } if (flags & FLAGS_SIGNED) { - // A signed specifier: d, i or possibly I + bit size if enabled + // A signed specifier: d, i or possibly I + bit + // size if enabled if (flags & FLAGS_LONG_LONG) { #if PRINTF_SUPPORT_LONG_LONG - const long long value - = va_arg(args, long long); + const long long value = va_arg( + args, + long long); print_integer( - output, ABS_FOR_PRINTING(value), - value < 0, base, precision, - width, flags); + output, + ABS_FOR_PRINTING(value), + value < 0, + base, + precision, + width, + flags); #endif } else if (flags & FLAGS_LONG) { const long value = va_arg(args, long); print_integer( - output, ABS_FOR_PRINTING(value), - value < 0, base, precision, - width, flags); + output, + ABS_FOR_PRINTING(value), + value < 0, + base, + precision, + width, + flags); } else { - // We never try to interpret the argument as something potentially-smaller than int, - // due to integer promotion rules: Even if the user passed a short int, short unsigned - // etc. - these will come in after promotion, as int's (or unsigned for the case of - // short unsigned when it has the same size as int) + // We never try to interpret the + // argument as something + // potentially-smaller than int, due to + // integer promotion rules: Even if the + // user passed a short int, short + // unsigned etc. - these will come in + // after promotion, as int's (or + // unsigned for the case of short + // unsigned when it has the same size as + // int) const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg( - args, int) + args, + int) : (flags & FLAGS_SHORT) - ? (short int)va_arg(args, int) + ? (short int)va_arg( + args, + int) : va_arg(args, int); print_integer( - output, ABS_FOR_PRINTING(value), - value < 0, base, precision, - width, flags); + output, + ABS_FOR_PRINTING(value), + value < 0, + base, + precision, + width, + flags); } } else { // An unsigned specifier: u, x, X, o, b @@ -1440,31 +1635,44 @@ static inline void format_string_loop( print_integer( output, (printf_unsigned_value_t)va_arg( - args, unsigned long long), - false, base, precision, width, + args, + unsigned long long), + false, + base, + precision, + width, flags); #endif } else if (flags & FLAGS_LONG) { print_integer( output, (printf_unsigned_value_t)va_arg( - args, unsigned long), - false, base, precision, width, + args, + unsigned long), + false, + base, + precision, + width, flags); } else { const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg( - args, unsigned int) + args, + unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg( - args, unsigned int) + args, + unsigned int) : va_arg(args, unsigned int); print_integer( output, (printf_unsigned_value_t)value, - false, base, precision, width, + false, + base, + precision, + width, flags); } } @@ -1475,12 +1683,18 @@ static inline void format_string_loop( case 'F': { floating_point_t value = (floating_point_t)(flags & FLAGS_LONG_DOUBLE - ? va_arg(args, long double) - : va_arg(args, double)); + ? va_arg(args, + long double) + : va_arg(args, + double)); if (*format == 'F') flags |= FLAGS_UPPERCASE; print_floating_point( - output, value, precision, width, flags, + output, + value, + precision, + width, + flags, PRINTF_PREFER_DECIMAL); format++; break; @@ -1493,14 +1707,20 @@ static inline void format_string_loop( case 'G': { floating_point_t value = (floating_point_t)(flags & FLAGS_LONG_DOUBLE - ? va_arg(args, long double) - : va_arg(args, double)); + ? va_arg(args, + long double) + : va_arg(args, + double)); if ((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; if ((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; print_floating_point( - output, value, precision, width, flags, + output, + value, + precision, + width, + flags, PRINTF_PREFER_EXPONENTIAL); format++; break; @@ -1532,8 +1752,10 @@ static inline void format_string_loop( out_rev_(output, ")llun(", 6, width, flags); } else { printf_size_t l = strnlen_s_( - p, precision ? precision - : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + p, + precision + ? precision + : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); // pre padding if (flags & FLAGS_PRECISION) { l = (l < precision ? l : precision); @@ -1545,7 +1767,8 @@ static inline void format_string_loop( } // string output while ((*p != 0) - && (!(flags & FLAGS_PRECISION) || precision)) { + && (!(flags & FLAGS_PRECISION) + || precision)) { putchar_via_gadget(output, *(p++)); --precision; } @@ -1569,8 +1792,12 @@ static inline void format_string_loop( ? out_rev_(output, ")lin(", 5, width, flags) : print_integer( output, - (printf_unsigned_value_t)value, false, - BASE_HEX, precision, width, flags); + (printf_unsigned_value_t)value, + false, + BASE_HEX, + precision, + width, + flags); format++; break; } @@ -1613,10 +1840,14 @@ static inline void format_string_loop( } // internal vsnprintf - used for implementing _all library functions -static int vsnprintf_impl(output_gadget_t *output, const char *format, va_list args) +static int vsnprintf_impl( + output_gadget_t *output, + const char *format, + va_list args) { - // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is - // possible to call this function with a non-zero pos value for some "remedial printing". + // Note: The library only calls vsnprintf_impl() with output->pos being + // 0. However, it is possible to call this function with a non-zero pos + // value for some "remedial printing". format_string_loop(output, format, args); // termination @@ -1627,8 +1858,10 @@ static int vsnprintf_impl(output_gadget_t *output, const char *format, va_list a } int z__fx_fctprintf( - void (*out)(char c, void *extra_arg), void *extra_arg, - const char *format, va_list arg) + void (*out)(char c, void *extra_arg), + void *extra_arg, + const char *format, + va_list arg) { if (out == NULL) { return 0; diff --git a/fx/string.c b/fx/string.c index 578b949..f8392d4 100644 --- a/fx/string.c +++ b/fx/string.c @@ -1,9 +1,18 @@ +#include "cstr.h" + #include +#include +#include #include +#include +#include #include +#include #include #include #include +#include +#include #include #include #include @@ -1157,23 +1166,6 @@ static fx_string *string_substr( return newstr; } -static uint64_t string_hash(const struct fx_string_p *str) -{ -#define FNV1_OFFSET_BASIS 0xcbf29ce484222325 -#define FNV1_PRIME 0x100000001b3 - uint64_t hash = FNV1_OFFSET_BASIS; - size_t i = 0; - - const char *s = string_ptr(str); - - for (i = 0; i < str->s_len; i++) { - hash ^= s[i]; - hash *= FNV1_PRIME; - } - - return hash; -} - /*** PUBLIC FUNCTIONS *********************************************************/ fx_string *fx_string_create_from_cstr(const char *s) @@ -1471,11 +1463,6 @@ fx_string *fx_string_get_substr(const fx_string *str, size_t start, size_t len) len); } -uint64_t fx_string_hash(const fx_string *str) -{ - FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_STRING, string_hash, str); -} - /*** PUBLIC ALIAS FUNCTIONS ***************************************************/ enum fx_status fx_string_append_c(fx_string *dest, char c) @@ -1547,13 +1534,54 @@ static void string_fini(fx_object *obj, void *priv) } } -static void string_to_string(const fx_object *obj, fx_stream *out) +static fx_status string_to_string( + const fx_value *obj, + fx_stream *out, + const char *format) { - struct fx_string_p *str = fx_object_get_private(obj, FX_TYPE_STRING); + struct fx_string_p *str = fx_object_get_private( + obj->v_object, + FX_TYPE_STRING); const char *s = string_ptr(str); for (size_t i = 0; i < str->s_len; i++) { fx_stream_write_char(out, s[i]); } + + return FX_SUCCESS; +} + +static fx_status string_hash(const fx_value *v, uint64_t *out_hash) +{ + struct fx_string_p *str = fx_object_get_private( + v->v_object, + FX_TYPE_STRING); + const char *s = string_ptr(str); + *out_hash = fx_hash_cstr(s); + return FX_SUCCESS; +} + +static i32 compare(const fx_value *left, const fx_value *right) +{ + const char *left_cstr, *right_cstr; + if (!FX_OK(fx_value_get_cstr(left, &left_cstr))) { + return -1; + } + if (!FX_OK(fx_value_get_cstr(right, &right_cstr))) { + return -1; + } + + return strcmp(left_cstr, right_cstr); +} + +static fx_status get_length( + const fx_value *str_v, + const fx_property *prop, + fx_value *out) +{ + fx_string *str = NULL; + fx_value_get_object(str_v, &str); + *out = FX_SIZE(fx_string_get_size(str, FX_STRLEN_CODEPOINTS)); + return FX_SUCCESS; } /*** ITERATOR FUNCTIONS *******************************************************/ @@ -1662,19 +1690,17 @@ static enum fx_status iterator_move_next(const fx_iterator *obj) } } -static fx_iterator_value chars_iterator_get_value( - struct fx_string_iterator_p *it) +static fx_value chars_iterator_get_value(struct fx_string_iterator_p *it) { - return FX_ITERATOR_VALUE_INT(it->char_value); + return FX_U32(it->char_value); } -static fx_iterator_value tokens_iterator_get_value( - struct fx_string_iterator_p *it) +static fx_value tokens_iterator_get_value(struct fx_string_iterator_p *it) { - return FX_ITERATOR_VALUE_CPTR(it->string_value); + return FX_CSTR(it->string_value); } -static fx_iterator_value iterator_get_value(fx_iterator *obj) +static fx_value iterator_get_value(const fx_iterator *obj) { struct fx_string_iterator_p *it = fx_object_get_private( obj, @@ -1686,32 +1712,65 @@ static fx_iterator_value iterator_get_value(fx_iterator *obj) case ITERATOR_MODE_TOKENS: return tokens_iterator_get_value(it); default: - return FX_ITERATOR_VALUE_NULL; + return FX_VALUE_EMPTY; } } -static const fx_iterator_value iterator_get_cvalue(const fx_iterator *obj) +static fx_status add(const fx_value *l, const fx_value *r, fx_value *out) { - struct fx_string_iterator_p *it = fx_object_get_private( - obj, - FX_TYPE_STRING_ITERATOR); - - switch (it->_m) { - case ITERATOR_MODE_CHARS: - return chars_iterator_get_value(it); - case ITERATOR_MODE_TOKENS: - return tokens_iterator_get_value(it); - default: - return FX_ITERATOR_VALUE_NULL; + fx_string *left = l->v_object; + fx_string *right = r->v_object; + fx_string *result = fx_string_create_from_cstr( + fx_string_get_cstr(left)); + if (!result) { + return FX_ERR_NO_MEMORY; } + + fx_string_append_cstr(result, fx_string_get_cstr(right)); + *out = FX_VALUE_OBJECT(result); + + return FX_SUCCESS; } +#define STRING_CONVERTER(fx_type, c_type) \ + static fx_status to_##fx_type(const fx_value *in, c_type *out) \ + { \ + const char *s = NULL; \ + fx_value_get_cstr(in, &s); \ + return cstr_to_##fx_type(s, out); \ + } + +STRING_CONVERTER(bool, bool) +STRING_CONVERTER(i16, i16) +STRING_CONVERTER(u16, u16) +STRING_CONVERTER(i32, i32) +STRING_CONVERTER(u32, u32) +STRING_CONVERTER(i64, i64) +STRING_CONVERTER(u64, u64) +STRING_CONVERTER(iptr, iptr) +STRING_CONVERTER(uptr, uptr) +STRING_CONVERTER(sbyte, sbyte) +STRING_CONVERTER(byte, byte) +STRING_CONVERTER(short, short) +STRING_CONVERTER(ushort, unsigned short) +STRING_CONVERTER(int, int) +STRING_CONVERTER(uint, unsigned int) +STRING_CONVERTER(long, long) +STRING_CONVERTER(ulong, unsigned long) +STRING_CONVERTER(longlong, long long) +STRING_CONVERTER(ulonglong, unsigned long long) +STRING_CONVERTER(size, size_t) +STRING_CONVERTER(float, float) +STRING_CONVERTER(double, double) +STRING_CONVERTER(cstr, const char *) + /*** CLASS DEFINITION *********************************************************/ // ---- fx_string DEFINITION FX_TYPE_CLASS_BEGIN(fx_string) FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) FX_INTERFACE_ENTRY(to_string) = string_to_string; + FX_INTERFACE_ENTRY(hash) = string_hash; FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterable, FX_TYPE_ITERABLE) @@ -1719,6 +1778,40 @@ FX_TYPE_CLASS_BEGIN(fx_string) FX_INTERFACE_ENTRY(it_cbegin) = iterator_cbegin; FX_TYPE_VTABLE_INTERFACE_END(fx_iterable, FX_TYPE_ITERABLE) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_operable, FX_TYPE_OPERABLE) + FX_INTERFACE_ENTRY(op_add) = add; + FX_TYPE_VTABLE_INTERFACE_END(fx_operable, FX_TYPE_OPERABLE) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_convertible, FX_TYPE_CONVERTIBLE) + FX_INTERFACE_ENTRY(c_to_bool) = to_bool; + FX_INTERFACE_ENTRY(c_to_bool) = to_bool; + FX_INTERFACE_ENTRY(c_to_i16) = to_i16; + FX_INTERFACE_ENTRY(c_to_u16) = to_u16; + FX_INTERFACE_ENTRY(c_to_u32) = to_u32; + FX_INTERFACE_ENTRY(c_to_i64) = to_i64; + FX_INTERFACE_ENTRY(c_to_u64) = to_u64; + FX_INTERFACE_ENTRY(c_to_iptr) = to_iptr; + FX_INTERFACE_ENTRY(c_to_uptr) = to_uptr; + FX_INTERFACE_ENTRY(c_to_sbyte) = to_sbyte; + FX_INTERFACE_ENTRY(c_to_byte) = to_byte; + FX_INTERFACE_ENTRY(c_to_short) = to_short; + FX_INTERFACE_ENTRY(c_to_ushort) = to_ushort; + FX_INTERFACE_ENTRY(c_to_int) = to_int; + FX_INTERFACE_ENTRY(c_to_uint) = to_uint; + FX_INTERFACE_ENTRY(c_to_long) = to_long; + FX_INTERFACE_ENTRY(c_to_ulong) = to_ulong; + FX_INTERFACE_ENTRY(c_to_longlong) = to_longlong; + FX_INTERFACE_ENTRY(c_to_ulonglong) = to_ulonglong; + FX_INTERFACE_ENTRY(c_to_size) = to_size; + FX_INTERFACE_ENTRY(c_to_float) = to_float; + FX_INTERFACE_ENTRY(c_to_double) = to_double; + FX_INTERFACE_ENTRY(c_to_cstr) = to_cstr; + FX_TYPE_VTABLE_INTERFACE_END(fx_convertible, FX_TYPE_CONVERTIBLE) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_comparable, FX_TYPE_COMPARABLE) + FX_INTERFACE_ENTRY(c_compare) = compare; + FX_TYPE_VTABLE_INTERFACE_END(fx_comparable, FX_TYPE_COMPARABLE) + FX_TYPE_CONSTRUCTOR("create", fx_string_create, 0, FX_TYPE_VOID); FX_TYPE_CONSTRUCTOR( "create_from_cstr", @@ -1731,12 +1824,17 @@ FX_TYPE_CLASS_BEGIN(fx_string) fx_string_get_cstr, 0, FX_TYPE_STRING); + + FX_TYPE_PROPERTY("length", get_length, NULL); FX_TYPE_CLASS_END(fx_string) FX_TYPE_DEFINITION_BEGIN(fx_string) FX_TYPE_ID(0x200194f6, 0x0327, 0x4a82, 0xb9c9, 0xb62ddd038c33); FX_TYPE_NAME("fx.string"); FX_TYPE_IMPLEMENTS(FX_TYPE_ITERABLE); + FX_TYPE_IMPLEMENTS(FX_TYPE_OPERABLE); + FX_TYPE_IMPLEMENTS(FX_TYPE_CONVERTIBLE); + FX_TYPE_IMPLEMENTS(FX_TYPE_COMPARABLE); FX_TYPE_CLASS(fx_string_class); FX_TYPE_INSTANCE_PRIVATE(struct fx_string_p); FX_TYPE_INSTANCE_INIT(string_init); @@ -1753,7 +1851,6 @@ FX_TYPE_CLASS_BEGIN(fx_string_iterator) FX_INTERFACE_ENTRY(it_move_next) = iterator_move_next; FX_INTERFACE_ENTRY(it_erase) = NULL; FX_INTERFACE_ENTRY(it_get_value) = iterator_get_value; - FX_INTERFACE_ENTRY(it_get_cvalue) = iterator_get_cvalue; FX_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR) FX_TYPE_CLASS_END(fx_string_iterator) diff --git a/fx/test/unicode-strings.c b/fx/test/unicode-strings.c index 99f6f4a..28b8648 100644 --- a/fx/test/unicode-strings.c +++ b/fx/test/unicode-strings.c @@ -17,8 +17,10 @@ int main(void) size_t nr_delims = sizeof delims / sizeof delims[0]; fx_iterator *it = fx_string_tokenise(str, delims, nr_delims, 0); - fx_foreach(const char *, tok, it) + fx_foreach(val, it) { + const char *tok = NULL; + fx_value_get_cstr(&val, &tok); printf("%s\n", tok); } fx_iterator_unref(it); diff --git a/fx/test/values.c b/fx/test/values.c index cae993a..602f73c 100644 --- a/fx/test/values.c +++ b/fx/test/values.c @@ -1,10 +1,27 @@ #include #include +#include #include int main(void) { - fx_value v = FX_I32(1024); - printf("%u\n", __fx_type_get_value_type(FX_TYPE_I64)); + fx_value x = FX_I32(1024); + fx_value y = FX_I16(512); + fx_value z = FX_VALUE_EMPTY; + + fx_type_id common_type = fx_value_get_common_type(&x, &y); + fx_value_change_type(&x, &x, common_type); + fx_value_change_type(&y, &y, common_type); + + fx_status status = fx_value_add(&x, &y, &z); + if (!FX_OK(status)) { + printf("add failed\n"); + return -1; + } + + u64 result = 0; + fx_value_get_u64(&z, &result); + printf("result: %" PRIu64 "\n", result); + return 0; } diff --git a/fx/type.c b/fx/type.c index bce3f36..bd6dac1 100644 --- a/fx/type.c +++ b/fx/type.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -362,6 +363,29 @@ fx_result fx_type_add_function(fx_type_info *info, struct _fx_object *func) return FX_RESULT_SUCCESS; } +fx_result fx_type_add_property(fx_type_info *info, struct _fx_object *prop) +{ + const char *name = fx_property_get_name(prop); + struct fx_namemap_entry *entry = fx_namemap_get( + &info->ty_functions, + name); + if (entry) { + return FX_RESULT_ERR(NAME_EXISTS); + } + + struct fx_type_property *ty_prop = malloc(sizeof *ty_prop); + if (!ty_prop) { + return FX_RESULT_ERR(NO_MEMORY); + } + + memset(ty_prop, 0x0, sizeof *ty_prop); + + ty_prop->p_prop = prop; + fx_namemap_put(&info->ty_properties, name, &ty_prop->p_entry); + + return FX_RESULT_SUCCESS; +} + struct fx_type_info *fx_type_info_get_by_id(const union fx_type_id *key) { return get_type(&type_idmap, key); @@ -392,3 +416,19 @@ fx_function *fx_type_info_get_function_by_name( f_entry); return func->f_func; } + +fx_function *fx_type_info_get_property_by_name( + const struct fx_type_info *ty, + const char *name) +{ + fx_namemap_entry *entry = fx_namemap_get(&ty->ty_properties, name); + if (!entry) { + return NULL; + } + + struct fx_type_property *prop = fx_unbox( + struct fx_type_property, + entry, + p_entry); + return prop->p_prop; +} diff --git a/fx/type.h b/fx/type.h index 93c2b4f..f13e67a 100644 --- a/fx/type.h +++ b/fx/type.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,11 @@ struct fx_type_function { fx_function *f_func; }; +struct fx_type_property { + struct fx_namemap_entry p_entry; + fx_property *p_prop; +}; + extern struct fx_type_registration *fx_type_get_registration(fx_type_id id); extern struct fx_type_component *fx_type_get_component( const fx_bst *tree, @@ -33,5 +39,8 @@ extern struct fx_type_info *fx_type_info_get_by_name(const char *name); extern fx_function *fx_type_info_get_function_by_name( const struct fx_type_info *ty, const char *name); +extern fx_property *fx_type_info_get_property_by_name( + const struct fx_type_info *ty, + const char *name); #endif diff --git a/fx/uuid.c b/fx/uuid.c index 3f2f1c2..7b2202c 100644 --- a/fx/uuid.c +++ b/fx/uuid.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -230,11 +231,15 @@ static void uuid_fini(fx_object *obj, void *priv) struct fx_uuid_p *uuid = priv; } -static void uuid_to_string(const fx_object *uuid, fx_stream *out) +static fx_status uuid_to_string( + const fx_value *uuid, + fx_stream *out, + const char *format) { char str[FX_UUID_STRING_MAX]; - fx_uuid_to_cstr(uuid, str); + fx_uuid_to_cstr(uuid->v_object, str); fx_stream_write_cstr(out, str, NULL); + return FX_SUCCESS; } /*** CLASS DEFINITION *********************************************************/