diff --git a/externals/cxc/hex2dec-help.pd b/externals/cxc/hex2dec-help.pd index 0e907bdd6a0734b490c2ebb405fb5391bdcc4eac..ffa1a8b0c8dabd0343d86afe6ab6e81af9ba0754 100644 --- a/externals/cxc/hex2dec-help.pd +++ b/externals/cxc/hex2dec-help.pd @@ -1,22 +1,45 @@ -#N canvas 0 31 450 300 10; -#X obj 138 153 hex2dec; -#X floatatom 137 189 5 0 0 0 - - -; -#X msg 139 105 symbol fff; -#X msg 237 123 123; -#X obj 257 194 print; -#X text 136 51 doesn't seem to work; -#N canvas 397 200 494 344 META 0; +#N canvas 3 60 560 479 10; +#X obj 29 413 hex2dec; +#N canvas 334 170 494 344 META 0; #X text 12 135 HELP_PATCH_AUTHORS Jonathan Wilkes revised the patch to conform to the PDDP template for Pd version 0.42.; #X text 12 55 DESCIPTION hexadecimal to decimal conversion; #X text 12 75 INLET_0 symbol float; #X text 12 95 OUTLET_0 float symbol; -#X text 12 5 KEYWORDS control conversion symbol_op needs_work (object -doesn't work); #X text 12 35 LICENSE GPL v2; #X text 12 115 AUTHOR jdl@xdv.org; -#X restore 394 274 pd META; -#X connect 0 0 1 0; -#X connect 0 0 4 0; -#X connect 2 0 0 0; +#X text 12 5 KEYWORDS control conversion symbol_op; +#X restore 504 454 pd META; +#X text 22 11 treat input as a hex string and convert to float; +#X msg 39 181 symbol ff; +#X msg 65 237 symbol 255; +#X msg 29 145 symbol 0xff \, symbol 0x02; +#X text 28 104 To minimize errors due to Pd's type limitations \, prepend +a "0x" to each hex string like so:; +#X obj 29 448 print; +#X text 187 145 <- "0x" makes everything a symbol atom; +#X msg 57 209 3 \, 3.14; +#X msg 73 279 symbol 0xffffff; +#X floatatom 89 448 14 0 0 0 - - -, f 14; +#X text 107 181 <- Unprefixed hex strings will work...; +#X text 117 209 ... as will floats (truncated to ints)...; +#X text 178 279 <- Also \, try to limit your input to byte-sized hex +strings. Even 32-bit ints won't fit losslessly into single-precision +floating point numbers. (Although they will fit into double-precision +floats.); +#X text 59 39 An incoming symbol message with a valid hex string is +output in decimal notation as a float.; +#X text 137 237 ... but without the "0x" prefix it's too easy to introduce +subtle bugs like this one. So always use the "0x" prefix.; +#X msg 73 361 list ff aa cc; +#X text 165 360 lists may be sent \, too. But be careful-- more than +10 elements will cause memory allocation that is not realtime safe. +; +#X connect 0 0 7 0; +#X connect 0 0 11 0; #X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X connect 9 0 0 0; +#X connect 10 0 0 0; +#X connect 17 0 0 0; diff --git a/externals/cxc/hex2dec.c b/externals/cxc/hex2dec.c index a812f4fc41dd5c4ef7ad668130fa523f3344074c..d85a4bb72240db48cc58804d4c2fe2c03febf46a 100644 --- a/externals/cxc/hex2dec.c +++ b/externals/cxc/hex2dec.c @@ -1,47 +1,136 @@ -#include "m_pd.h" -#include <math.h> - +#include <stdlib.h> +#include <limits.h> +#include <errno.h> #include <string.h> -#include <stdio.h> -/* ----------------------- hex2dec --------------------- */ +#include "m_pd.h" static t_class *hex2dec_class; -typedef struct _hex2dec -{ - t_object x_obj; - t_symbol *x_format; +typedef struct _hex2dec { + t_object x_obj; } t_hex2dec; -static void *hex2dec_new(t_symbol *s) +static int hex2dec_strtoll(t_hex2dec *x, char *buf, long long int *val) { - t_hex2dec *x = (t_hex2dec *)pd_new(hex2dec_class); - if (!s->s_name) s = gensym("file.%d"); - outlet_new(&x->x_obj, &s_symbol); - x->x_format = s; - return (x); + char *endptr; + errno = 0; + + /* No empty strings, please. This guards against things like + [symbol 42( silently failing */ + if (!buf || *buf == '\0') + { + pd_error(x, "hex2dec: empty hex string detected"); + return 0; + } + + *val = strtoll(buf, &endptr, 16); + if (errno == ERANGE) { + if (*val == LLONG_MIN) + pd_error(x, "hex2dec: underflow detected"); + else if (*val == LLONG_MAX) + pd_error(x, "hex2dec: overflow detected"); + else + pd_error(x, "hex2dec: unknown range error"); + return 0; + } + else if (errno != 0) + { + pd_error(x, "hex2dec: unknown error"); + return 0; + } + else if (*endptr != '\0') + { + pd_error(x, "hex2dec: invalid input '%s'", buf); + return 0; + } + return 1; } -static void hex2dec_float(t_hex2dec *x, t_floatarg f) + /* Let's see how we do with static allocation for size 10 lists. This + can be expanded later if people want to avoid heap allocations for + larger messages. */ +#define STATIC_SIZE 10 +int warned_about_allocation; + +static void hex2dec_list(t_hex2dec *x, t_symbol *s, int argc, t_atom *argv) { + t_atom at[STATIC_SIZE], *outvec; char buf[MAXPDSTRING]; - sprintf(buf, x->x_format->s_name, (int)f); - outlet_symbol(x->x_obj.ob_outlet, gensym(buf)); + if (argc > STATIC_SIZE) + { + if (!warned_about_allocation) + { + post("warning: hex2dec: realtime unsafe memory allocation for " + "lists with more than %d elements", STATIC_SIZE); + warned_about_allocation++; + } + outvec = (t_atom *)t_getbytes(argc * sizeof(t_atom)); + } + else + outvec = at; + + int i; + for (i = 0; i < argc; i++) + { + if (argv[i].a_type == A_SYMBOL) + { + long long int val; + if (hex2dec_strtoll(x, argv[i].a_w.w_symbol->s_name, &val)) + SETFLOAT(outvec + i, (t_float)val); + else + return; /* no output if we hit any errors */ + } + else if (argv[i].a_type == A_FLOAT) + { + long long int val; + sprintf(buf, "%lld", (long long int)argv[i].a_w.w_float); + if (hex2dec_strtoll(x, buf, &val)) + SETFLOAT(outvec + i, (t_float)val); + else + return; + } + else + { + pd_error(x, "hex2dec: only symbol and float accepted"); + /* cleanup for large lists */ + if (argc > STATIC_SIZE) + t_freebytes(outvec, argc * sizeof(t_atom)); + return; + } + } + outlet_list(x->x_obj.ob_outlet, &s_list, argc, outvec); + + /* cleanup for large lists */ + if (argc > STATIC_SIZE) + t_freebytes(outvec, argc * sizeof(t_atom)); } -static void hex2dec_symbol(t_hex2dec *x, t_symbol *s) +/* We could accept anything. But then we'd have to allocate for larger + messages, copy the selector to the first slot, then copy argv in the + remaining slots. There are some macros to achieve this in x_list.c + but they're specific to the list classes and therefore not public. */ +static void hex2dec_anything(t_hex2dec *x, t_symbol *s, int argc, t_atom *argv) { - char buf[MAXPDSTRING]; - sprintf(buf, x->x_format->s_name, s->s_name); - outlet_symbol(x->x_obj.ob_outlet, gensym(buf)); + if (s && s != &s_) + pd_error(x, "hex2dec: no method for '%s' (did you mean " + "'%s %s%s'?)", + s->s_name, argc ? "list" : "symbol", s->s_name, argc ? " ..." : ""); + else if (s && s == &s_) + pd_error(x, "hex2dec: no method for empty symbol"); + else + pd_error(x, "hex2dec: only float, symbol, or list accepted"); } -void hex2dec_setup(void) -{ - hex2dec_class = class_new(gensym("hex2dec"), - (t_newmethod)hex2dec_new, 0, - sizeof(t_hex2dec), 0, A_DEFSYM, 0); - class_addfloat(hex2dec_class, hex2dec_float); - class_addsymbol(hex2dec_class, hex2dec_symbol); +static void *hex2dec_new(void) { + t_hex2dec *x = (t_hex2dec *)pd_new(hex2dec_class); + outlet_new(&x->x_obj, &s_float); + return (void *)x; +} + +void hex2dec_setup(void) { + hex2dec_class = class_new(gensym("hex2dec"), (t_newmethod)hex2dec_new, + 0, sizeof(t_hex2dec), CLASS_DEFAULT, A_DEFFLOAT, 0); + class_addlist(hex2dec_class, hex2dec_list); + class_addanything(hex2dec_class, hex2dec_anything); } diff --git a/scripts/regression_tests/cxc_hex2dec_big_list.pd b/scripts/regression_tests/cxc_hex2dec_big_list.pd new file mode 100644 index 0000000000000000000000000000000000000000..d07ec6706aa78e43cd2e04740d89c1465dc05ad1 --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_big_list.pd @@ -0,0 +1,47 @@ +#N canvas 3 60 593 558 12; +#X obj 41 8 inlet; +#X obj 41 474 outlet; +#X obj 41 336 cxc/hex2dec; +#X obj 41 375 list length; +#X obj 41 37 bang; +#X obj 41 442 list append small list should generate output of same +size; +#X msg 111 33 bang; +#X obj 115 177 f; +#X obj 116 140 until; +#X obj 115 206 makefilename 0x%.2d; +#X obj 115 270 trigger anything; +#X obj 41 301 list; +#X obj 41 72 trigger bang bang bang; +#X obj 151 177 + 1; +#X msg 162 140 0; +#X obj 115 241 list prepend; +#X obj 41 404 == 100; +#X obj 116 111 f 100; +#X floatatom 387 58 5 0 0 0 - - -, f 5; +#X obj 387 86 t a; +#X connect 0 0 4 0; +#X connect 2 0 3 0; +#X connect 3 0 16 0; +#X connect 4 0 12 0; +#X connect 5 0 1 0; +#X connect 6 0 12 0; +#X connect 7 0 9 0; +#X connect 7 0 13 0; +#X connect 8 0 7 0; +#X connect 9 0 15 0; +#X connect 10 0 11 1; +#X connect 10 0 15 1; +#X connect 11 0 2 0; +#X connect 12 0 11 0; +#X connect 12 1 17 0; +#X connect 12 2 14 0; +#X connect 12 2 15 1; +#X connect 13 0 7 1; +#X connect 14 0 7 1; +#X connect 15 0 10 0; +#X connect 16 0 5 0; +#X connect 17 0 8 0; +#X connect 18 0 19 0; +#X connect 19 0 17 1; +#X connect 19 0 16 1; diff --git a/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd b/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd new file mode 100644 index 0000000000000000000000000000000000000000..cd8eb1846add3871f76024434e745a4c04323833 --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_empty_symbol.pd @@ -0,0 +1,27 @@ +#N canvas 65 89 593 521 12; +#X obj 41 8 inlet; +#X obj 41 424 outlet; +#X obj 155 136 cxc/hex2dec; +#X obj 116 78 unpost; +#X obj 41 37 trigger bang bang bang; +#X obj 41 191 list; +#X obj 41 220 route bang; +#X obj 108 249 b; +#X msg 41 249 0; +#X msg 108 278 1; +#X obj 41 322 list append empty symbol payload is not allowed; +#X msg 155 106 symbol; +#X connect 0 0 4 0; +#X connect 3 0 5 1; +#X connect 3 1 11 0; +#X connect 4 0 5 0; +#X connect 4 1 3 0; +#X connect 4 2 5 1; +#X connect 5 0 6 0; +#X connect 6 0 8 0; +#X connect 6 1 7 0; +#X connect 7 0 9 0; +#X connect 8 0 10 0; +#X connect 9 0 10 0; +#X connect 10 0 1 0; +#X connect 11 0 2 0; diff --git a/scripts/regression_tests/cxc_hex2dec_invalid.pd b/scripts/regression_tests/cxc_hex2dec_invalid.pd new file mode 100644 index 0000000000000000000000000000000000000000..96d8166878ffa5a3d3d6cad824ac0c54449c79b7 --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_invalid.pd @@ -0,0 +1,28 @@ +#N canvas 3 60 593 521 12; +#X obj 41 8 inlet; +#X obj 41 424 outlet; +#X obj 155 136 cxc/hex2dec; +#X obj 116 78 unpost; +#X obj 41 37 trigger bang bang bang; +#X obj 41 191 list; +#X obj 41 220 route bang; +#X obj 108 249 b; +#X msg 41 249 0; +#X msg 108 278 1; +#X msg 155 106 symbol 0xinvalid; +#X obj 41 322 list append invalid input string should output an error +; +#X connect 0 0 4 0; +#X connect 3 0 5 1; +#X connect 3 1 10 0; +#X connect 4 0 5 0; +#X connect 4 1 3 0; +#X connect 4 2 5 1; +#X connect 5 0 6 0; +#X connect 6 0 8 0; +#X connect 6 1 7 0; +#X connect 7 0 9 0; +#X connect 8 0 11 0; +#X connect 9 0 11 0; +#X connect 10 0 2 0; +#X connect 11 0 1 0; diff --git a/scripts/regression_tests/cxc_hex2dec_overflow.pd b/scripts/regression_tests/cxc_hex2dec_overflow.pd new file mode 100644 index 0000000000000000000000000000000000000000..5af1f5ffc16afb31472db8df84958961d273faeb --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_overflow.pd @@ -0,0 +1,27 @@ +#N canvas 3 60 593 521 12; +#X obj 41 8 inlet; +#X obj 41 424 outlet; +#X obj 155 136 cxc/hex2dec; +#X obj 116 78 unpost; +#X obj 41 37 trigger bang bang bang; +#X obj 41 191 list; +#X obj 41 220 route bang; +#X obj 108 249 b; +#X msg 41 249 0; +#X msg 108 278 1; +#X msg 155 106 symbol 0xffffffffffffffff; +#X obj 41 322 list append overflow should output an error; +#X connect 0 0 4 0; +#X connect 3 0 5 1; +#X connect 3 1 10 0; +#X connect 4 0 5 0; +#X connect 4 1 3 0; +#X connect 4 2 5 1; +#X connect 5 0 6 0; +#X connect 6 0 8 0; +#X connect 6 1 7 0; +#X connect 7 0 9 0; +#X connect 8 0 11 0; +#X connect 9 0 11 0; +#X connect 10 0 2 0; +#X connect 11 0 1 0; diff --git a/scripts/regression_tests/cxc_hex2dec_small_list.pd b/scripts/regression_tests/cxc_hex2dec_small_list.pd new file mode 100644 index 0000000000000000000000000000000000000000..04c8c16c75f5afe35efd151190ac6ce987b74ae1 --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_small_list.pd @@ -0,0 +1,17 @@ +#N canvas 3 60 593 521 12; +#X obj 41 8 inlet; +#X obj 41 424 outlet; +#X obj 41 136 cxc/hex2dec; +#X msg 41 106 list 0x00 0x01 0x02 0x03; +#X obj 41 165 list length; +#X obj 41 194 == 4; +#X obj 41 37 bang; +#X obj 41 322 list append small list should generate output of same +size; +#X connect 0 0 6 0; +#X connect 2 0 4 0; +#X connect 3 0 2 0; +#X connect 4 0 5 0; +#X connect 5 0 7 0; +#X connect 6 0 3 0; +#X connect 7 0 1 0; diff --git a/scripts/regression_tests/cxc_hex2dec_underflow.pd b/scripts/regression_tests/cxc_hex2dec_underflow.pd new file mode 100644 index 0000000000000000000000000000000000000000..09f208923970def81583af34b5501b4c754d22db --- /dev/null +++ b/scripts/regression_tests/cxc_hex2dec_underflow.pd @@ -0,0 +1,27 @@ +#N canvas 3 60 593 521 12; +#X obj 41 8 inlet; +#X obj 41 424 outlet; +#X obj 155 136 cxc/hex2dec; +#X obj 116 78 unpost; +#X obj 41 37 trigger bang bang bang; +#X obj 41 191 list; +#X obj 41 220 route bang; +#X obj 108 249 b; +#X msg 41 249 0; +#X msg 108 278 1; +#X msg 155 106 symbol -0xffffffffffffffff; +#X obj 41 322 list append underflow should output an error; +#X connect 0 0 4 0; +#X connect 3 0 5 1; +#X connect 3 1 10 0; +#X connect 4 0 5 0; +#X connect 4 1 3 0; +#X connect 4 2 5 1; +#X connect 5 0 6 0; +#X connect 6 0 8 0; +#X connect 6 1 7 0; +#X connect 7 0 9 0; +#X connect 8 0 11 0; +#X connect 9 0 11 0; +#X connect 10 0 2 0; +#X connect 11 0 1 0;