From 0d8513a9086f284cb2cd33beacfd4e8d64adcc44 Mon Sep 17 00:00:00 2001 From: Bryan Jurish Date: Thu, 26 Jul 2007 12:20:50 +0000 Subject: pdstring v0.05 + added persistent string buffer to any2string + added initial buffer size and eos-character arguments to any2string, string2any - defaults are backwards-compatible but ugly + added --enable-object-externals option to configure svn path=/trunk/externals/moocow/pdstring/; revision=8247 --- README.txt | 45 +++++++++ configure.in | 29 +++++- src/Makefile.am | 10 +- src/any2string-help.pd | 39 ++++++++ src/any2string.c | 171 ++++++++++++++++++--------------- src/any2string_static.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pdstring-help.pd | 24 ++--- src/pdstring.c | 17 +++- src/string2any-help.pd | 42 +++++++++ src/string2any.c | 129 ++++++++++++++++++------- 10 files changed, 621 insertions(+), 131 deletions(-) create mode 100644 README.txt create mode 100644 src/any2string-help.pd create mode 100644 src/any2string_static.c create mode 100644 src/string2any-help.pd diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b715526 --- /dev/null +++ b/README.txt @@ -0,0 +1,45 @@ + README for pd external package 'pdstring' + + Last updated for pdstring v0.02 + +DESCRIPTION + The 'pdstring' package contains objects for converting to and from + (ASCII)-strings, represented as lists of floats. + +INSTALLATION + Issue the following commands to the shell: + + cd PACKAGE-XX.YY (or wherever you extracted the distribution) + ./configure + make + make install + +BUILD OPTIONS + The 'configure' script supports the following options, among others: + + * --enable-debug , --disable-debug + Whether to enable verbose debugging messages. Default=no. + +ACKNOWLEDGEMENTS + PD by Miller Puckette and others. + + Ideas, black magic, and other nuggets of information drawn from code by + Guenter Geiger, Larry Troxler, and iohannes m zmoelnig. + +KNOWN BUGS + Memory Usage + Encoding each byte of a string as its own float is shamefully wasteful: + it uses only 1 byte out of at least 3 which could be losslessly used + given ANSI/IEEE Std 754-1985 floats, not to mention the remaining + byte(s) (usually 1) of the float itself or the (usually 4) bytes used + for the a_type flag. Unfortunately, Pd trims some floating point + precision in message boxes and in float atoms, so a truly lossless float + encoding for Pd would only be possible using 2 bytes per float (wasting + 1/2 the space of the float itself), and (to me), the memory saving such + an encoding would provide is just not worth the lack of transparency and + additional workload it would involve (but contact me if you want the + code anyways). + +AUTHOR + Bryan Jurish + diff --git a/configure.in b/configure.in index 703fa8f..350fb84 100644 --- a/configure.in +++ b/configure.in @@ -4,7 +4,7 @@ AC_PREREQ(2.5) dnl Some handy macros define([THE_PACKAGE_NAME], [pdstring]) -define([THE_PACKAGE_VERSION], [0.04]) +define([THE_PACKAGE_VERSION], [0.05]) define([THE_PACKAGE_AUTHOR], [moocow@ling.uni-potsdam.de]) AC_INIT(THE_PACKAGE_NAME, THE_PACKAGE_VERSION, THE_PACKAGE_AUTHOR) @@ -72,8 +72,31 @@ AC_CHECK_HEADERS([string.h], [/* nonempty includes: compile only */]) -dnl PD externals +##------- PD externals +PD_LIB_EXTERNALS="pdstring" +PD_OBJ_EXTERNALS="any2string string2any" + +AC_ARG_ENABLE(object-externals, + AC_HELP_STRING([--enable-object-externals], [Whether to build single-object externals (default=no)]), + [want_objext="$enableval"], + [want_objext="no"]) +AC_MSG_CHECKING([whether to build single-object externals]) + +##-- always build lib PD_OBJECT_EXTERNALS="pdstring\$(EXEEXT)" + +if test "$want_objext" != "no" ; then + ##-- single-objects + AC_MSG_RESULT(yes) + for ext in $PD_OBJ_EXTERNALS ; do + PD_OBJECT_EXTERNALS="$PD_OBJECT_EXTERNALS ${ext}\$(EXEEXT)" + done + AC_DEFINE(PDSTRING_OBJECT_EXTERNALS,1, + [Define this if you are building single-object externals]) +else + ##-- libs + AC_MSG_RESULT(no) +fi AC_SUBST(PD_OBJECT_EXTERNALS) @@ -128,7 +151,7 @@ if test "$enable_debug" == "yes" ; then AC_DEFINE(ANY2STRING_DEBUG,1, [Define this to include debugging code for the 'string2any' external.]) AC_DEFINE(STRING2ANY_DEBUG,1, - [Define this to include debugging code for 'the any2string' external.]) + [Define this to include debugging code for the 'any2string' external.]) else AC_MSG_RESULT(no) DEBUG="no" diff --git a/src/Makefile.am b/src/Makefile.am index 2ff35cd..fcc75a2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,17 +39,17 @@ EXTRA_PROGRAMS = \ pdexterns_DATA = ## --- documentation -pddoc_DATA = pdstring-help.pd +pddoc_DATA = \ + pdstring-help.pd \ + any2string-help.pd \ + string2any-help.pd #----------------------------------------------------------------------- # sources #----------------------------------------------------------------------- -pdstring_SOURCES = \ - any2string.c \ - string2any.c \ - pdstring.c +pdstring_SOURCES = pdstring.c any2string_SOURCES = any2string.c diff --git a/src/any2string-help.pd b/src/any2string-help.pd new file mode 100644 index 0000000..695de06 --- /dev/null +++ b/src/any2string-help.pd @@ -0,0 +1,39 @@ +#N canvas 90 20 636 478 10; +#X obj 25 400 print any2string-help; +#X obj 25 314 any2string 128 -1; +#X msg 109 197 0; +#X msg 77 197 -1; +#X text 19 47 INLETS:; +#X text 31 61 1 - pd messages; +#X text 247 49 OUTLETS:; +#X text 263 63 1 - lists of ASCII-valued floats; +#X floatatom 44 147 8 0 0 0 - - -; +#X symbolatom 34 125 10 0 0 0 - - -; +#X text 189 125 ... no special handling for symbols; +#X text 188 103 anything can be converted to a list...; +#X text 190 147 ... but "float" selector is silently dropped; +#X msg 25 104 A B C; +#X text 193 312 First argument: initial buffer size (length of object-local +text buffer). This should get re-allocated when necessary. Specify +0 (zero) to use the default value.; +#X text 195 367 Second argument: initial EOS character. See above. +; +#X text 320 458 Bryan Jurish ; +#X text 19 439 SEE ALSO:; +#X msg 139 197 42; +#X text 21 29 SYNTAX: any2string [INITIAL_BUFSIZE [EOS_CHAR]]; +#X text 184 194 The second inlet sets the terminating EOS ("end-of-string") +character. This value gets implicitly appended to all output lists. +Specify a negative value here to avoid implicit EOS characters entirely. +For backwards compatibility \, the default EOS character is 0 (zero). +; +#X text 51 5 any2string : convert pd messages to a list of floats; +#X text 31 75 2 - EOS character (float); +#X obj 100 438 pdstring-help; +#X connect 1 0 0 0; +#X connect 2 0 1 1; +#X connect 3 0 1 1; +#X connect 8 0 1 0; +#X connect 9 0 1 0; +#X connect 13 0 1 0; +#X connect 18 0 1 1; diff --git a/src/any2string.c b/src/any2string.c index 094907f..e005be2 100644 --- a/src/any2string.c +++ b/src/any2string.c @@ -1,8 +1,8 @@ /* -*- Mode: C -*- */ /*=============================================================================*\ - * File: any2string.c + * File: any2string_dynamic.c * Author: Bryan Jurish - * Description: convert pd messages to strings + * Description: convert pd messages to strings (dynamic allocation) * * Copyright (c) 2004 - 2007 Bryan Jurish. * @@ -25,9 +25,12 @@ *=============================================================================*/ #include - #include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + /* black magic */ #ifdef NT #pragma warning( disable : 4244 ) @@ -45,7 +48,7 @@ # define A2SDEBUG(x) #endif -#define ANY2STRING_INITIAL_BUFLEN 32 +#define ANY2STRING_DEFAULT_BUFLEN 256 /*===================================================================== @@ -58,8 +61,11 @@ typedef struct _any2string t_object x_obj; int x_alloc; int x_argc; + t_float x_eos; //-- EOS character to add (<0 for none) + char *x_text; t_atom *x_argv; t_binbuf *x_binbuf; + t_inlet *x_eos_in; t_outlet *x_outlet; } t_any2string; @@ -67,6 +73,7 @@ typedef struct _any2string /*===================================================================== * Constants *=====================================================================*/ +static char *any2string_banner = "any2string: pdstring version " PACKAGE_VERSION " by Bryan Jurish"; /*===================================================================== * Methods @@ -78,75 +85,60 @@ typedef struct _any2string static void any2string_anything(t_any2string *x, t_symbol *sel, int argc, t_atom *argv) { t_atom *a; - char *text=NULL, *s, *s_max; + char *s, *s_max; int len; - A2SDEBUG(post("-------any2string_anything()---------")); + A2SDEBUG(post("-------any2string_anything(%p,...)---------", x)); /*-- set up binbuf --*/ - A2SDEBUG(post("any2string: binbuf_clear()")); + A2SDEBUG(post("any2string[%p]: binbuf_clear()", x)); binbuf_clear(x->x_binbuf); - A2SDEBUG(post("any2string: binbuf_add()")); + + /*-- binbuf_add(): selector --*/ + if (sel != &s_float && sel != &s_list && sel != &s_) { + t_atom a; + A2SDEBUG(post("any2string[%p]: binbuf_add(): selector: '%s'", x, sel->s_name)); + SETSYMBOL((&a), sel); + binbuf_add(x->x_binbuf, 1, &a); + } + A2SDEBUG(else { post("any2string[%p]: selector: '%s': IGNORED", x, sel->s_name); }); + + /*-- binbuf_add(): arg list --*/ + A2SDEBUG(post("any2string[%p]: binbuf_add(): arg list", x)); binbuf_add(x->x_binbuf, argc, argv); - A2SDEBUG(startpost("any2string: binbuf_print: ")); + A2SDEBUG(post("any2string[%p]: binbuf_print: ", x)); A2SDEBUG(binbuf_print(x->x_binbuf)); - A2SDEBUG(post("any2string: binbuf_gettext()")); - binbuf_gettext(x->x_binbuf, &text, &len); - A2SDEBUG(post("any2string: binbuf_gettext() = \"%s\" ; len=%d", text, len)); - /*text[len] = 0;*/ /*-- ? avoid errors: free(): invalid next size(fast): [HEX_ADDRESS] */ + A2SDEBUG(post("any2string[%p]: binbuf_gettext()", x)); + binbuf_gettext(x->x_binbuf, &(x->x_text), &len); + A2SDEBUG(post("any2string[%p]: binbuf_gettext() = \"%s\" ; len=%d", x, x->x_text, len)); + /*text[len] = 0;*/ /*-- ? avoid errors: "free(): invalid next size(fast): 0x..." */ - /*-- get string length --*/ - x->x_argc = len+1; - if (sel != &s_float && sel != &s_list && sel != &s_) { - x->x_argc += strlen(sel->s_name); - if (argc > 0) x->x_argc++; - } - A2SDEBUG(post("any2string: argc=%d", x->x_argc)); + /*-- get output atom-list length --*/ + x->x_argc = len; + if (x->x_eos >= 0) { x->x_argc++; } + A2SDEBUG(post("any2string[%p]: argc=%d", x, x->x_argc)); - /*-- (re-)allocate --*/ + /*-- (re-)allocate (maybe) --*/ if (x->x_alloc < x->x_argc) { - A2SDEBUG(post("any2string: reallocate(%d->%d)", x->x_alloc, x->x_argc)); - freebytes(x->x_argv, x->x_alloc * sizeof(t_atom)); - x->x_argv = (t_atom *)getbytes(x->x_argc * sizeof(t_atom)); + A2SDEBUG(post("any2string[%p]: reallocate(%d->%d)", x, x->x_alloc, x->x_argc)); + freebytes(x->x_argv, x->x_alloc*sizeof(t_atom)); + x->x_argv = (t_atom*)getbytes(x->x_argc * sizeof(t_atom)); x->x_alloc = x->x_argc; } - /*-- add selector (maybe) --*/ - a=x->x_argv; - if (sel != &s_float && sel != &s_list && sel != &s_) { - A2SDEBUG(post("any2string: for {...} //sel")); - for (s=sel->s_name; *s; s++, a++) { - A2SDEBUG(post("any2string: for: SETFLOAT(a,'%c'=%d) //sel", *s, *s)); - SETFLOAT(a,*s); - } - A2SDEBUG(post("any2string: /for {...} //sel")); - - if (argc > 0) { - SETFLOAT(a,' '); - a++; - } - } - - /*-- add binbuf text --*/ - A2SDEBUG(post("any2string: for {...}")); - s_max = text+len; - for (s=text; s < s_max; s++, a++) { - A2SDEBUG(post("any2string: for: //SETFLOAT(a,'%c'=%d)", *s, *s)); + /*-- atom buffer: binbuf text --*/ + A2SDEBUG(post("any2string[%p]: atom buffer: for {...}", x)); + a = x->x_argv; + s_max = x->x_text+len; + for (s=x->x_text; s < s_max; s++, a++) { + A2SDEBUG(post("any2string[%p]: atom buffer[%d]: SETFLOAT(a,%d='%c')", x, (a-x->x_argv), *s, *s)); SETFLOAT(a,*s); } - A2SDEBUG(post("any2string: /for {...}")); - - /*-- add terminating NUL --*/ - SETFLOAT(a,0); - - A2SDEBUG(post("any2string: freebytes()")); - freebytes(text, len); + A2SDEBUG(post("any2string: atom buffer: DONE")); - /* - A2SDEBUG(post("any2string: binbuf_free()")); - binbuf_free(x->x_binbuf); - */ + /*-- add EOS character (maybe) --*/ + if (x->x_eos >= 0) { SETFLOAT(a, ((int)x->x_eos)); } A2SDEBUG(post("any2string: outlet_list(..., %d, ...)", x->x_argc)); outlet_list(x->x_outlet, &s_list, x->x_argc, x->x_argv); @@ -156,19 +148,39 @@ static void any2string_anything(t_any2string *x, t_symbol *sel, int argc, t_atom /*-------------------------------------------------------------------- * new */ -static void *any2string_new(void) +static void *any2string_new(t_symbol *sel, int argc, t_atom *argv) { t_any2string *x = (t_any2string *)pd_new(any2string_class); //-- defaults - x->x_alloc = ANY2STRING_INITIAL_BUFLEN; - x->x_argc = 0; - x->x_argv = (t_atom *)getbytes(x->x_alloc * sizeof(t_atom)); + x->x_eos = 0; + x->x_alloc = ANY2STRING_DEFAULT_BUFLEN; + + //-- args: 0: bufsize + if (argc > 0) { + int bufsize = atom_getintarg(0, argc, argv); + if (bufsize > 0) { x->x_alloc = bufsize; } + } + //-- args: 1: eos + if (argc > 1) { + x->x_eos = atom_getfloatarg(1, argc, argv); + } + + //-- allocate + x->x_text = (char *)getbytes(x->x_alloc*sizeof(char)); + x->x_argc = 0; + x->x_argv = (t_atom *)getbytes(x->x_alloc*sizeof(t_atom)); x->x_binbuf = binbuf_new(); + //-- inlets + x->x_eos_in = floatinlet_new(&x->x_obj, &x->x_eos); + //-- outlets x->x_outlet = outlet_new(&x->x_obj, &s_list); + //-- report + A2SDEBUG(post("any2string_new(): x=%p, alloc=%d, eos=%d, text=%p, argv=%p, binbuf=%p", x, x->x_alloc, x->x_eos, x->x_text, x->x_argv, x->x_binbuf)); + return (void *)x; } @@ -177,36 +189,47 @@ static void *any2string_new(void) */ static void any2string_free(t_any2string *x) { + if (x->x_text) { + freebytes(x->x_text, x->x_alloc*sizeof(char)); + x->x_text = NULL; + } if (x->x_argv) { - freebytes(x->x_argv, x->x_alloc * sizeof(t_atom)); + freebytes(x->x_argv, x->x_alloc*sizeof(t_atom)); x->x_argv = NULL; } binbuf_free(x->x_binbuf); + inlet_free(x->x_eos_in); outlet_free(x->x_outlet); return; } /*-------------------------------------------------------------------- - * setup + * setup (guts) */ -void any2string_setup(void) +void any2string_setup_guts(void) { //-- class any2string_class = class_new(gensym("any2string"), - (t_newmethod)any2string_new, - (t_method)any2string_free, - sizeof(t_any2string), - CLASS_DEFAULT, - 0); + (t_newmethod)any2string_new, + (t_method)any2string_free, + sizeof(t_any2string), + CLASS_DEFAULT, + A_GIMME, //-- initial_bufsize, eos_char + 0); //-- methods - /*class_addmethod(any2string_class, (t_method)any2string_symbol, &s_symbol, - A_DEFSYMBOL, 0); - */ - class_addanything(any2string_class, - (t_method)any2string_anything); + class_addanything(any2string_class, (t_method)any2string_anything); //-- help symbol - class_sethelpsymbol(any2string_class, gensym("pdstring-help.pd")); + class_sethelpsymbol(any2string_class, gensym("any2string-help.pd")); +} + +/*-------------------------------------------------------------------- + * setup + */ +void any2string_setup(void) +{ + post(any2string_banner); + any2string_setup_guts(); } diff --git a/src/any2string_static.c b/src/any2string_static.c new file mode 100644 index 0000000..50da35d --- /dev/null +++ b/src/any2string_static.c @@ -0,0 +1,246 @@ +/* -*- Mode: C -*- */ +/*=============================================================================*\ + * File: any2string_static.c + * Author: Bryan Jurish + * Description: convert pd messages to strings (static buffer allocation) + * + * Copyright (c) 2004 - 2007 Bryan Jurish. + * + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file "COPYING", in this distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *=============================================================================*/ + +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* black magic */ +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +/*-------------------------------------------------------------------- + * DEBUG + *--------------------------------------------------------------------*/ +/*#define ANY2STRING_DEBUG 1*/ +/*#undef ANY2STRING_DEBUG*/ + +#ifdef ANY2STRING_DEBUG +# define A2SDEBUG(x) x +#else +# define A2SDEBUG(x) +#endif + +#define ANY2STRING_DEFAULT_BUFLEN 512 + + +/*===================================================================== + * Structures and Types: any2string + *=====================================================================*/ +static t_class *any2string_class; + +typedef struct _any2string +{ + t_object x_obj; + int x_alloc; //-- buffer size (text, x_argv) + int x_argc; //-- current number of atoms to outlet + t_atom *x_argv; //-- float-list to outlet + t_outlet *x_outlet; //-- outlet +} t_any2string; + + +/*===================================================================== + * Utilities + *=====================================================================*/ + +/*-------------------------------------------------------------------- + * append_string + */ +static void any2string_append_string(t_any2string *x, char *s, unsigned int maxlen, int doescape) +{ + char *sp; + char *ep = s+maxlen; + + for (sp=s; *sp && spx_argcx_alloc; sp++, x->x_argc++) { + if (doescape && (*sp==';' || *sp==',' || *sp=='\\' + || (*sp == '$' && sp<(ep-1) && sp[1] >= '0' && sp[1] <= '9'))) + { + A2SDEBUG(post("any2string_append_string: ESCAPE: x_argv[%d] = '%c' = %d", x->x_argc, '\\', '\\')); + x->x_argv[x->x_argc++].a_w.w_float = '\\'; + if (x->x_argc >= x->x_alloc) break; + } + A2SDEBUG(post("any2string_append_string: x_argv[%d] = '%c' = %d", x->x_argc, *sp, *sp)); + x->x_argv[x->x_argc].a_w.w_float = *sp; + } +} + +/*-------------------------------------------------------------------- + * append_atom + */ +#define ANY2STRING_APPEND_BUFSIZE 30 +static void any2string_append_atom(t_any2string *x, t_atom *a) +{ + char buf[ANY2STRING_APPEND_BUFSIZE]; + A2SDEBUG(post("~~ any2string_append_atom(%p,...) ~~", x)); + + if (x->x_argc >= x->x_alloc) { return; } + + /*-- stringify a single atom (inspired by atom_string() from m_atom.c) --*/ + switch (a->a_type) { + case A_SEMI: any2string_append_string(x, ";", 1, 0); break; + case A_COMMA: any2string_append_string(x, ",", 1, 0); break; + case A_POINTER: any2string_append_string(x, "(pointer)", 9, 0); break; + case A_FLOAT: + snprintf(buf, ANY2STRING_APPEND_BUFSIZE, "%g", a->a_w.w_float); + any2string_append_string(x, buf, ANY2STRING_APPEND_BUFSIZE, 0); + break; + case A_SYMBOL: + any2string_append_string(x, a->a_w.w_symbol->s_name, strlen(a->a_w.w_symbol->s_name), 1); + break; + case A_DOLLAR: + snprintf(buf, ANY2STRING_APPEND_BUFSIZE, "$%d", a->a_w.w_index); + any2string_append_string(x, buf, ANY2STRING_APPEND_BUFSIZE, 0); + break; + case A_DOLLSYM: + any2string_append_string(x, a->a_w.w_symbol->s_name, strlen(a->a_w.w_symbol->s_name), 0); + break; + default: + pd_error(x,"any2string_append_atom: unknown atom type '%d'", a->a_type); + break; + } + + if (x->x_argc < x->x_alloc) { + A2SDEBUG(post("any2string_append_atom[%p]: x_argv[%d] = '%c' = %d", x, x->x_argc, ' ', ' ')); + x->x_argv[x->x_argc++].a_w.w_float = ' '; + } +} + +/*===================================================================== + * Methods + *=====================================================================*/ + +/*-------------------------------------------------------------------- + * anything + */ +static void any2string_anything(t_any2string *x, t_symbol *sel, int argc, t_atom *argv) +{ + t_atom *argv_end = argv+argc; + x->x_argc=0; + + A2SDEBUG(post("-------any2string_anything(%p,...) ---------", x)); + + /*-- stringify selector (maybe) --*/ + if (sel != &s_float && sel != &s_list && sel != &s_) { + t_atom a; + SETSYMBOL(&a,sel); + any2string_append_atom(x, &a); + } + + /*-- stringify arg list --*/ + for ( ; argvx_argcx_alloc; argv++) { + any2string_append_atom(x, argv); + } + + /*-- add terminating NUL (if we can) --*/ + A2SDEBUG(post("any2string[%p]: terminating NUL: x_argv[%d]=0", x, x->x_argc-1)); + if (x->x_argc >= x->x_alloc) { + pd_error(x, "any2string: input length exceeds buffer size!"); + x->x_argc = x->x_alloc; + x->x_argv[x->x_argc-1].a_w.w_float = '*'; //-- simulate atom_string() behavior + } else if (x->x_argc > 0) { + x->x_argv[x->x_argc-1].a_w.w_float = 0; + } + + A2SDEBUG(post("any2string[%p]: outlet_list(..., %d, ...)", x, x->x_argc)); + outlet_list(x->x_outlet, &s_list, x->x_argc, x->x_argv); +} + + +/*-------------------------------------------------------------------- + * new + */ +static void *any2string_new(t_floatarg bufsize) +{ + t_any2string *x = (t_any2string *)pd_new(any2string_class); + int i; + + //-- bufsize + if (bufsize <= 0) { + x->x_alloc = ANY2STRING_DEFAULT_BUFLEN; + } else { + x->x_alloc = bufsize; + } + A2SDEBUG(post("any2string_new: buf_req=%g, alloc=%d", bufsize, x->x_alloc)); + + //-- defaults + x->x_argc = 0; + x->x_argv = (t_atom*)getbytes(x->x_alloc*sizeof(t_atom)); + + //-- initialize (set a_type) + for (i=0; i < x->x_alloc; i++) { + SETFLOAT((x->x_argv+i),0); + } + + //-- outlets + x->x_outlet = outlet_new(&x->x_obj, &s_list); + A2SDEBUG(post("any2string_new: x=%p, alloc=%d, argv=%p, nbytes=%d", x, x->x_alloc,x->x_argv,x->x_alloc*sizeof(t_atom))); + return (void *)x; +} + +/*-------------------------------------------------------------------- + * free + */ +static void any2string_free(t_any2string *x) +{ + A2SDEBUG(post("any2string_free(x=%p)", x)); + if (x->x_argv) { + A2SDEBUG(post("any2string_free(x=%p): x_argv=%p (size=%d)", x, x->x_argv, x->x_alloc*sizeof(t_atom))); + freebytes(x->x_argv, x->x_alloc*sizeof(t_atom)); + } + A2SDEBUG(post("any2string_free(x=%p): x_outlet=%p", x, x->x_outlet)); + outlet_free(x->x_outlet); + return; +} + +/*-------------------------------------------------------------------- + * setup + */ +void any2string_setup(void) +{ + //-- class + any2string_class = class_new(gensym("any2string"), + (t_newmethod)any2string_new, + (t_method)any2string_free, + sizeof(t_any2string), + CLASS_DEFAULT, + A_DEFFLOAT, + 0); + + //-- methods + class_addanything(any2string_class, + (t_method)any2string_anything); + + + //-- help symbol + class_sethelpsymbol(any2string_class, gensym("pdstring-help.pd")); +} diff --git a/src/pdstring-help.pd b/src/pdstring-help.pd index e83b9db..2ab94b3 100644 --- a/src/pdstring-help.pd +++ b/src/pdstring-help.pd @@ -1,11 +1,9 @@ -#N canvas 381 42 563 311 10; +#N canvas 191 76 563 311 10; #X text 258 284 Bryan Jurish ; #X floatatom 56 81 8 0 0 0 - - -; #X symbolatom 46 59 10 0 0 0 - - -; #X text 201 59 ... no special handling for symbols; #X text 200 37 anything can be converted to a list...; -#X obj 383 3 pdstring; -#X text 54 3 any2string \, string2any : ASCII conversions in; #X obj 37 108 any2string; #X obj 37 230 string2any; #X floatatom 54 178 4 0 0 0 - - -; @@ -48,12 +46,14 @@ #X obj 37 254 print string2any-out; #X msg 37 38 A B C; #X msg 55 199 list 49 50 51; -#X connect 1 0 7 0; -#X connect 2 0 7 0; -#X connect 7 0 15 0; -#X connect 7 0 8 0; -#X connect 8 0 16 0; -#X connect 9 0 8 0; -#X connect 13 0 12 0; -#X connect 17 0 7 0; -#X connect 18 0 8 0; +#X text 54 3 any2string \, string2any : ASCII conversions in [pdstring] +lib; +#X connect 1 0 5 0; +#X connect 2 0 5 0; +#X connect 5 0 13 0; +#X connect 5 0 6 0; +#X connect 6 0 14 0; +#X connect 7 0 6 0; +#X connect 11 0 10 0; +#X connect 15 0 5 0; +#X connect 16 0 6 0; diff --git a/src/pdstring.c b/src/pdstring.c index c77b68e..37bd4ff 100644 --- a/src/pdstring.c +++ b/src/pdstring.c @@ -54,8 +54,15 @@ typedef struct _pdstring /*===================================================================== * External declarations *=====================================================================*/ -extern void any2string_setup(void); -extern void string2any_setup(void); +#ifndef PDSTRING_OBJECT_EXTERNALS +/* +extern void any2string_setup_guts(void); +extern void string2any_setup_guts(void); +*/ +#include "any2string.c" +#include "string2any.c" + +#endif /*-------------------------------------------------------------------- * new @@ -81,8 +88,10 @@ void pdstring_setup(void) { post(pdstring_version); - any2string_setup(); - string2any_setup(); +#ifndef PDSTRING_OBJECT_EXTERNALS + any2string_setup_guts(); + string2any_setup_guts(); +#endif pdstring_class = class_new(gensym("pdstring"), (t_newmethod)pdstring_new, diff --git a/src/string2any-help.pd b/src/string2any-help.pd new file mode 100644 index 0000000..8e44e15 --- /dev/null +++ b/src/string2any-help.pd @@ -0,0 +1,42 @@ +#N canvas 121 48 612 604 10; +#X text 59 7 string2any : convert lists of floats to pd messages; +#X text 13 30 SYNTAX: string2any [INITIAL_BUFSIZE [EOS_CHAR]]; +#X text 13 52 INLETS:; +#X text 27 68 1 - ASCII-valued float lists; +#X text 309 54 OUTLETS:; +#X text 325 68 1 - pd messages; +#X text 325 82 2 - bang on done; +#X obj 24 350 string2any 128 0; +#X obj 24 529 print string2any-help; +#X obj 131 454 print string2any-help-done; +#X msg 24 113 97 32 98 32 99; +#X msg 39 135 97 0 98 0 99; +#X msg 99 206 0; +#X msg 67 206 -1; +#X msg 131 206 32; +#X text 166 202 The second inlet sets the message-separating EOS ("end-of-string") +character. If this character is encountered in an input list \, the +list is "split" at that point \, and two separate messages are output +at the leftmost outlet. You can specify a negative value here to avoid +implicit input splitting entirely. For backwards compatibility \, the +default EOS character is -1 \, which results in truncation of input +messages whenever a 0 (zero) is encountered.; +#X text 163 350 First argument: initial buffer size (length of object-local +text buffer). This should get re-allocated when necessary. Specify +0 (zero) to use the default value.; +#X text 165 405 Second argument: initial EOS character. See above. +; +#X text 159 477 Right outlet gives a bang after the entire input list +has been processed.; +#X text 189 531 Parsed pd messages are sent to the left outlet.; +#X text 306 580 Bryan Jurish ; +#X text 27 83 2 - EOS character (float); +#X text 161 121 lists of floats are converted to pd messages.; +#X msg 54 161 StringThing 97; +#X text 168 161 ... selector is ignored ...; +#X text 21 571 SEE ALSO:; +#X obj 102 570 pdstring-help; +#X connect 7 0 8 0; +#X connect 10 0 7 0; +#X connect 11 0 7 0; +#X connect 23 0 7 0; diff --git a/src/string2any.c b/src/string2any.c index 93756ec..bcde417 100644 --- a/src/string2any.c +++ b/src/string2any.c @@ -28,6 +28,10 @@ #include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + /* black magic */ #ifdef NT #pragma warning( disable : 4244 ) @@ -37,7 +41,8 @@ /*-------------------------------------------------------------------- * DEBUG *--------------------------------------------------------------------*/ -//#define STRING2ANY_DEBUG 1 +/*#define STRING2ANY_DEBUG 1*/ +/*#undef STRING2ANY_DEBUG*/ #ifdef STRING2ANY_DEBUG # define S2ADEBUG(x) x @@ -45,12 +50,13 @@ # define S2ADEBUG(x) #endif -#define STRING2ANY_INITIAL_BUFLEN 32 +#define STRING2ANY_DEFAULT_BUFLEN 256 /*===================================================================== * Constants *=====================================================================*/ +static char *string2any_banner = "string2any: pdstring version " PACKAGE_VERSION " by Bryan Jurish"; /*===================================================================== * Structures and Types: any2string @@ -62,34 +68,33 @@ typedef struct _string2any { t_object x_obj; size_t x_size; + t_float x_eos; //-- eos character char *x_text; t_binbuf *x_binbuf; + t_inlet *x_eos_in; t_outlet *x_outlet; + t_outlet *x_outlet_done; } t_string2any; /*===================================================================== - * Constants - *=====================================================================*/ - -/*===================================================================== - * Methods + * Utilities *=====================================================================*/ /*-------------------------------------------------------------------- - * anything + * string2any_atoms() */ -static void string2any_anything(t_string2any *x, t_symbol *sel, int argc, t_atom *argv) +static void string2any_atoms(t_string2any *x, int argc, t_atom *argv) { char *s; - int x_argc; + int x_argc, a_argc=argc; t_atom *x_argv; /*-- allocate --*/ - if (x->x_size <= argc) { + if (x->x_size <= (argc+1)) { freebytes(x->x_text, x->x_size*sizeof(char)); x->x_size = argc+1; - x->x_text = (char *)getbytes(x->x_size * sizeof(char)); + x->x_text = (char *)getbytes(x->x_size*sizeof(char)); } /*-- get text --*/ @@ -97,12 +102,12 @@ static void string2any_anything(t_string2any *x, t_symbol *sel, int argc, t_atom *s = atom_getfloat(argv); } *s = 0; - S2ADEBUG(post("string2any: text: \"%s\"", x->x_text)); + S2ADEBUG(post("string2any[%p]: text: \"%s\", strlen=%d, argc=%d", x, x->x_text, strlen(x->x_text), a_argc)); /*-- clear and fill binbuf --*/ binbuf_clear(x->x_binbuf); - binbuf_text(x->x_binbuf, x->x_text, strlen(x->x_text)); - S2ADEBUG(startpost("string2any: buf: ")); + binbuf_text(x->x_binbuf, x->x_text, a_argc); //-- handle NULs if binbuf will (but it won't) ? + S2ADEBUG(post("string2any[%p]: binbuf_print: ", x)); S2ADEBUG(binbuf_print(x->x_binbuf)); /*-- output --*/ @@ -123,20 +128,70 @@ static void string2any_anything(t_string2any *x, t_symbol *sel, int argc, t_atom } +/*===================================================================== + * Methods + *=====================================================================*/ + +/*-------------------------------------------------------------------- + * anything + */ +static void string2any_anything(t_string2any *x, t_symbol *sel, int argc, t_atom *argv) +{ + int i0=0, i; + + /*-- scan & output --*/ + if (x->x_eos >= 0) { + for (i=i0; i < argc; i++) { + if (((int)atom_getfloatarg(i,argc,argv))==((int)x->x_eos)) { + string2any_atoms(x, i-i0, argv+i0); + i0=i+1; + } + } + } + + if (i0 < argc) { + string2any_atoms(x, argc-i0, argv+i0); + } + + outlet_bang(x->x_outlet_done); +} + + /*-------------------------------------------------------------------- * new */ -static void *string2any_new(void) +static void *string2any_new(t_symbol *sel, int argc, t_atom *argv) { t_string2any *x = (t_string2any *)pd_new(string2any_class); //-- defaults x->x_binbuf = binbuf_new(); - x->x_size = STRING2ANY_INITIAL_BUFLEN; - x->x_text = (char *)getbytes(x->x_size * sizeof(char)); + x->x_size = STRING2ANY_DEFAULT_BUFLEN; + x->x_eos = -1; + + //-- args: 0: bufsize + if (argc > 0) { + int initial_bufsize = atom_getintarg(0,argc,argv); + if (initial_bufsize > 0) x->x_size = initial_bufsize; + x->x_eos = -1; //-- backwards-compatibility hack: no default eos character if only bufsize is specified + } + //-- args: 1: separator + if (argc > 1) { + x->x_eos = atom_getfloatarg(1,argc,argv); + } + + //-- allocate + x->x_text = (char *)getbytes(x->x_size*sizeof(char)); + + //-- inlets + x->x_eos_in = floatinlet_new(&x->x_obj, &x->x_eos); //-- outlets - x->x_outlet = outlet_new(&x->x_obj, &s_list); + x->x_outlet = outlet_new(&x->x_obj, &s_list); + x->x_outlet_done = outlet_new(&x->x_obj, &s_bang); + + //-- debug + S2ADEBUG(post("string2any_new: x=%p, size=%d, eos=%d, binbuf=%p, text=%p", x, x->x_size, x->x_eos, x->x_binbuf, x->x_text)); return (void *)x; } @@ -147,35 +202,43 @@ static void *string2any_new(void) static void string2any_free(t_string2any *x) { if (x->x_text) { - freebytes(x->x_text, x->x_size * sizeof(char)); + freebytes(x->x_text, x->x_size*sizeof(char)); x->x_text = NULL; } binbuf_free(x->x_binbuf); + inlet_free(x->x_eos_in); + outlet_free(x->x_outlet_done); outlet_free(x->x_outlet); return; } /*-------------------------------------------------------------------- - * setup + * setup: guts */ -void string2any_setup(void) +void string2any_setup_guts(void) { //-- class string2any_class = class_new(gensym("string2any"), - (t_newmethod)string2any_new, - (t_method)string2any_free, - sizeof(t_string2any), - CLASS_DEFAULT, - 0); + (t_newmethod)string2any_new, + (t_method)string2any_free, + sizeof(t_string2any), + CLASS_DEFAULT, + A_GIMME, //-- initial_bufsize, eos_char + 0); //-- methods - /*class_addmethod(string2any_class, (t_method)string2any_symbol, &s_symbol, - A_DEFSYMBOL, 0); - */ - class_addanything(string2any_class, - (t_method)string2any_anything); + class_addanything(string2any_class, (t_method)string2any_anything); //-- help symbol - class_sethelpsymbol(string2any_class, gensym("pdstring-help.pd")); + class_sethelpsymbol(string2any_class, gensym("string2any-help.pd")); +} + +/*-------------------------------------------------------------------- + * setup + */ +void string2any_setup(void) +{ + post(string2any_banner); + string2any_setup_guts(); } -- cgit v1.2.1