From 89efe5aeaed162611dd94dc14456da255e34bc65 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 26 Nov 2019 20:59:56 -0500 Subject: [PATCH] Dynamic string (dstring) rework --- src/frontend/diff.c | 64 +-- src/frontend/numparam/general.h | 10 +- src/frontend/numparam/mystring.c | 60 ++- src/frontend/numparam/spicenum.c | 49 +- src/frontend/numparam/xpressn.c | 155 +++--- src/frontend/vectors.c | 74 +-- src/include/ngspice/dstring.h | 280 ++++++++-- src/misc/dstring.c | 862 ++++++++++++++++++------------- 8 files changed, 949 insertions(+), 605 deletions(-) diff --git a/src/frontend/diff.c b/src/frontend/diff.c index 4e6502989..ed5f74d02 100644 --- a/src/frontend/diff.c +++ b/src/frontend/diff.c @@ -9,28 +9,28 @@ Patched: 2010/2012 by Bill Swartz (hash table for vectors) * Do a 'diff' of two plots. */ -#include "ngspice/ngspice.h" -#include "ngspice/ftedefs.h" +#include "ngspice/dstring.h" #include "ngspice/dvec.h" +#include "ngspice/ftedefs.h" +#include "ngspice/hash.h" +#include "ngspice/ngspice.h" #include "ngspice/sim.h" #include "diff.h" -#include -#include #include "variable.h" -static bool nameeq(const char *n1,const char *n2); -static char *canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p, +static bool nameeq(const char *n1, const char *n2); +static char *canonical_name(const char *name, DSTRINGPTR dbuf_p, bool make_i_name_lower); static char * -canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p, +canonical_name(const char *name, DSTRINGPTR dbuf_p, bool make_i_name_lower) { - spice_dstring_reinit(dbuf_p); /* Reset dynamic buffer */ + ds_clear(dbuf_p); /* Reset dynamic buffer */ /* "i(some_name)" -> "some_name#branch" */ /* "I(some_name)" -> "some_name#branch" */ @@ -38,22 +38,28 @@ canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p, static const char sz_branch[] = "#branch"; const char *p_start = name + 2; size_t n = strlen(p_start) - 1; /* copy all but final ')' */ - if (make_i_name_lower) { - (void) spice_dstring_append_lower(dbuf_p, p_start, (int) n); + ds_case_t case_type = make_i_name_lower ? + ds_case_lower : ds_case_as_is; + bool f_ok = ds_cat_mem_case(dbuf_p, p_start, (int) n, + case_type) == DS_E_OK; + f_ok &= ds_cat_mem(dbuf_p, sz_branch, + sizeof sz_branch / sizeof *sz_branch - 1) == DS_E_OK; + if (!f_ok) { + controlled_exit(-1); } - else { - (void) spice_dstring_append(dbuf_p, p_start, (int) n); - } - return spice_dstring_append(dbuf_p, sz_branch, - sizeof sz_branch / sizeof *sz_branch - 1); + return ds_get_buf(dbuf_p); } /* Convert a name starting with a digit, such as a numbered node to * something like v(33) */ if (isdigit_c(*name)) { - (void) spice_dstring_append(dbuf_p, "v(", 2); - (void) spice_dstring_append(dbuf_p, name, -1); - return spice_dstring_append_char(dbuf_p, ')'); + bool f_ok = ds_cat_mem(dbuf_p, "v(", 2) == DS_E_OK; + f_ok &= ds_cat_str(dbuf_p, name) == DS_E_OK; + f_ok &= ds_cat_char(dbuf_p, ')') == DS_E_OK; + if (!f_ok) { + controlled_exit(-1); + } + return ds_get_buf(dbuf_p); } /* Finally if neither of the special cases above occur, there is @@ -62,7 +68,10 @@ canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p, * argument. Making a copy ensures that it can be modified without * changing the original, but in the current use cases that is * not an issue. */ - return spice_dstring_append(dbuf_p, name, -1); + if (ds_cat_str(dbuf_p, name) != DS_E_OK) { + controlled_exit(-1); + } + return ds_get_buf(dbuf_p); } /* end of function canonical_name */ @@ -79,19 +88,17 @@ nameeq(const char *n1, const char *n2) return TRUE; } - SPICE_DSTRING ds1, ds2; /* Buffers to build canonical names */ - - /* Init the dynamic string buffers */ - spice_dstring_init(&ds1); - spice_dstring_init(&ds2); + /* Init the dynamic string buffers to build canonical names */ + DS_CREATE(ds1, 100); + DS_CREATE(ds2, 100); /* Compare canonical names */ const BOOL rc = (BOOL) cieq(canonical_name(n1, &ds1, FALSE), canonical_name(n2, &ds2, FALSE)); /* Free the dynamic string buffers */ - spice_dstring_free(&ds1); - spice_dstring_free(&ds2); + ds_free(&ds1); + ds_free(&ds2); return rc; } /* end of function nameeq */ @@ -110,7 +117,6 @@ com_diff(wordlist *wl) char *v1_name; /* canonical v1 name */ char *v2_name; /* canonical v2 name */ NGHASHPTR crossref_p; /* cross reference hash table */ - SPICE_DSTRING ibuf; /* used to build canonical name */ wordlist *tw; char numbuf[BSIZE_SP], numbuf2[BSIZE_SP], numbuf3[BSIZE_SP], numbuf4[BSIZE_SP]; /* For printnum */ @@ -180,7 +186,7 @@ com_diff(wordlist *wl) for (v1 = p1->pl_dvecs; v1; v1 = v1->v_next) v1->v_link2 = NULL; - spice_dstring_init(&ibuf); + DS_CREATE(ibuf, 100); /* used to build canonical name */ crossref_p = nghash_init(NGHASH_MIN_SIZE); nghash_unique(crossref_p, FALSE); @@ -208,7 +214,7 @@ com_diff(wordlist *wl) } } - spice_dstring_free(&ibuf); + ds_free(&ibuf); nghash_free(crossref_p, NULL, NULL); for (v1 = p1->pl_dvecs; v1; v1 = v1->v_next) diff --git a/src/frontend/numparam/general.h b/src/frontend/numparam/general.h index aa7eeb6c7..a17c26d9a 100644 --- a/src/frontend/numparam/general.h +++ b/src/frontend/numparam/general.h @@ -9,11 +9,11 @@ #include "ngspice/bool.h" -char *pscopy(SPICE_DSTRINGPTR s, const char *str, const char *stop); -void scopyd(SPICE_DSTRINGPTR a, SPICE_DSTRINGPTR b); -void scopys(SPICE_DSTRINGPTR a, const char *b); -void sadd(SPICE_DSTRINGPTR s, const char *t); -void cadd(SPICE_DSTRINGPTR s, char c); +void pscopy(DSTRINGPTR s, const char *str, const char *stop); +void scopyd(DSTRINGPTR dst, const DSTRINGPTR src); +void scopys(DSTRINGPTR a, const char *b); +void sadd(DSTRINGPTR s, const char *t); +void cadd(DSTRINGPTR s, char c); bool alfa(char c); bool alfanum(char c); diff --git a/src/frontend/numparam/mystring.c b/src/frontend/numparam/mystring.c index fa9e67092..297b7a970 100644 --- a/src/frontend/numparam/mystring.c +++ b/src/frontend/numparam/mystring.c @@ -71,9 +71,11 @@ yes_or_no(void) * Function: add string t to dynamic string dstr_p. * ----------------------------------------------------------------- */ void -sadd(SPICE_DSTRINGPTR dstr_p, const char *t) +sadd(DSTRINGPTR dstr_p, const char *t) { - spice_dstring_append(dstr_p, t, -1); + if (ds_cat_str(dstr_p, t) != DS_E_OK) { + controlled_exit(-1); + } } @@ -81,12 +83,11 @@ sadd(SPICE_DSTRINGPTR dstr_p, const char *t) * Function: add character c to dynamic string dstr_p. * ----------------------------------------------------------------- */ void -cadd(SPICE_DSTRINGPTR dstr_p, char c) +cadd(DSTRINGPTR dstr_p, char c) { - char tmp_str[2]; - tmp_str[0] = c; - tmp_str[1] = '\0'; - spice_dstring_append(dstr_p, tmp_str, -1); + if (ds_cat_char(dstr_p, c) != DS_E_OK) { + controlled_exit(-1); + } } @@ -95,43 +96,43 @@ cadd(SPICE_DSTRINGPTR dstr_p, char c) * terminated. * ----------------------------------------------------------------- */ void -scopyd(SPICE_DSTRINGPTR s, SPICE_DSTRINGPTR t) /* returns success flag */ +scopyd(DSTRINGPTR dst, const DSTRINGPTR src) /* returns success flag */ { - spice_dstring_reinit(s); - spice_dstring_append(s, spice_dstring_value(t), -1); + (void) ds_clear(dst); + if (ds_cat_ds(dst, src) != DS_E_OK) { + controlled_exit(-1); + } } /* ----------------------------------------------------------------- * Create copy of the string in the dynamic string. Dynamic strings - * are always NULLterminated. + * are always NULL terminated. * ----------------------------------------------------------------- */ void -scopys(SPICE_DSTRINGPTR s, const char *t) /* returns success flag */ +scopys(DSTRINGPTR s, const char *t) /* returns success flag */ { - spice_dstring_reinit(s); - spice_dstring_append(s, t, -1); + ds_clear(s); + if (ds_cat_str(s, t) != DS_E_OK) { + controlled_exit(-1); + } } -char * -pscopy(SPICE_DSTRINGPTR dstr_p, const char *t, const char *stop) +/* Copy until stop char (exclusive) or end of string if none given */ +void +pscopy(DSTRINGPTR dstr_p, const char *t, const char *stop) { - int i; - char *s_p; - - if (!stop) + if (!stop) { /* locate end of string if no stop char given */ stop = strchr(t, '\0'); + } - s_p = _spice_dstring_setlength(dstr_p, (int)(stop - t)); + if (ds_cat_mem(dstr_p, t, stop - t) != DS_E_OK) { + controlled_exit(-1); + } - for (i = 0; t < stop;) - s_p[i++] = *t++; - - s_p[i] = '\0'; - - return s_p; -} + return; +} /* end of function pscopy */ bool @@ -157,3 +158,6 @@ alfanumps(char c) { return alfa(c) || ((c >= '0') && (c <= '9')) || c == '-'; } + + + diff --git a/src/frontend/numparam/spicenum.c b/src/frontend/numparam/spicenum.c index 9d9a69f11..d4405964b 100644 --- a/src/frontend/numparam/spicenum.c +++ b/src/frontend/numparam/spicenum.c @@ -69,7 +69,7 @@ static long placeholder = 0; static void -stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol) +stripsomespace(DSTRINGPTR dstr_p, bool incontrol) { /* if s starts with one of some markers, strip leading space */ @@ -78,7 +78,7 @@ stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol) ? "*.&+#$" : "*.&+#$" "xX"; - char *s = spice_dstring_value(dstr_p); + char *s = ds_get_buf(dstr_p); int i = 0; while (s[i] && (s[i] <= ' ')) @@ -90,15 +90,13 @@ stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol) static int -stripbraces(SPICE_DSTRINGPTR dstr_p) +stripbraces(DSTRINGPTR dstr_p) /* puts the funny placeholders. returns the number of {...} substitutions */ { int n = 0; - char *s = spice_dstring_value(dstr_p); + char *s = ds_get_buf(dstr_p); char *p, *brace; - SPICE_DSTRING tstr; /* temporary dynamic string */ - - spice_dstring_init(&tstr); + DS_CREATE(tstr, 200); p = s; while ((brace = strchr(p, '{')) != NULL) { @@ -132,32 +130,30 @@ stripbraces(SPICE_DSTRINGPTR dstr_p) if (*j_ptr >= ' ') cadd(&tstr, ' '); - int ilen = spice_dstring_length(&tstr); + int ilen = (int) ds_get_length(&tstr); sadd(&tstr, j_ptr); scopyd(dstr_p, &tstr); - s = spice_dstring_value(dstr_p); + s = ds_get_buf(dstr_p); p = s + ilen; } dynsubst = placeholder; - spice_dstring_free(&tstr); + ds_free(&tstr); return n; } static void -findsubname(dico_t *dico, SPICE_DSTRINGPTR dstr_p) +findsubname(dico_t *dico, DSTRINGPTR dstr_p) /* truncate the parameterized subckt call to regular old Spice */ /* scan a string from the end, skipping non-idents and {expressions} */ /* then truncate s after the last subckt(?) identifier */ { - char * const s = spice_dstring_value(dstr_p); - char *p = s + spice_dstring_length(dstr_p); + char * const s = ds_get_buf(dstr_p); + char *p = s + ds_get_length(dstr_p); - SPICE_DSTRING name; /* extract a name */ - - spice_dstring_init(&name); + DS_CREATE(name, 200); /* extract a name */ while (p > s) { @@ -193,23 +189,23 @@ findsubname(dico_t *dico, SPICE_DSTRINGPTR dstr_p) else for (t = p; alfanum(*t); t++) ; - spice_dstring_reinit(&name); + ds_clear(&name); pscopy(&name, p, t); - entry = entrynb(dico, spice_dstring_value(&name)); + entry = entrynb(dico, ds_get_buf(&name)); if (entry && (entry->tp == NUPA_SUBCKT)) { - spice_dstring_setlength(dstr_p, (int) (p_end - s)); - spice_dstring_free(&name); + (void) ds_set_length(dstr_p, p_end - s); + ds_free(&name); return; } } } - spice_dstring_free(&name); + ds_free(&name); } static char -transform(dico_t *dico, SPICE_DSTRINGPTR dstr_p, bool incontrol) +transform(dico_t *dico, DSTRINGPTR dstr_p, bool incontrol) /* line s is categorized and crippled down to basic Spice * returns in u control word following dot, if any * @@ -238,7 +234,7 @@ transform(dico_t *dico, SPICE_DSTRINGPTR dstr_p, bool incontrol) char category; stripsomespace(dstr_p, incontrol); - s = spice_dstring_value(dstr_p); + s = ds_get_buf(dstr_p); if (s[0] == '.') { /* check PS parameter format */ @@ -571,9 +567,8 @@ nupa_copy(struct card *deck) char *t; char c, d; - SPICE_DSTRING u; - spice_dstring_init(&u); + DS_CREATE(u, 200); pscopy(&u, s, s_end); /* strip trailing space, CrLf and so on */ dicoS->srcline = linenum; @@ -599,14 +594,14 @@ nupa_copy(struct card *deck) dicoS->dyncategory[linenum] = c; } /* keep a local copy and mangle the string */ - t = copy(spice_dstring_value(&u)); + t = copy(ds_get_buf(&u)); if (!t) { fputs("Fatal: String malloc crash in nupa_copy()\n", stderr); controlled_exit(EXIT_FAILURE); } - spice_dstring_free(&u); + ds_free(&u); return t; } diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index 64cc72b16..cdd5299d0 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -300,7 +300,6 @@ dicostack_pop(dico_t *dico) /* pop operation for nested subcircuit locals */ { char *inst_name; /* name of subcircuit instance */ - char *param_p; /* qualified inst parameter name */ entry_t *entry; /* current entry */ NGHASHPTR htable_p; /* current hash table */ NGHASHITER iter; /* hash iterator - thread safe */ @@ -317,22 +316,23 @@ dicostack_pop(dico_t *dico) inst_name = dico->inst_name[dico->stack_depth]; htable_p = dico->symbols[dico->stack_depth]; if (htable_p) { - SPICE_DSTRING param_name; /* build a qualified name */ - spice_dstring_init(¶m_name); + /* build a qualified name */ + DS_CREATE(param_name, 100); NGHASH_FIRST(&iter); for (entry = (entry_t *) nghash_enumerateRE(htable_p, &iter); - entry; - entry = (entry_t *) nghash_enumerateRE(htable_p, &iter)) - { - spice_dstring_reinit(¶m_name); - param_p = spice_dstring_print(¶m_name, "%s.%s", - inst_name, entry->symbol); - nupa_add_inst_param(param_p, entry->vl); + entry; + entry = (entry_t *) nghash_enumerateRE(htable_p, &iter)) { + ds_clear(¶m_name); + if (ds_cat_printf(¶m_name, "%s.%s", + inst_name, entry->symbol) != DS_E_OK) { + controlled_exit(-1); + } + nupa_add_inst_param(ds_get_buf(¶m_name), entry->vl); dico_free_entry(entry); } nghash_free(htable_p, NULL, NULL); - spice_dstring_free(¶m_name); + ds_free(¶m_name); } tfree(inst_name); @@ -521,11 +521,11 @@ defsubckt(dico_t *dico, struct card *card) s_end++; if (s_end > s) { - SPICE_DSTRING ustr; /* temp user string */ - spice_dstring_init(&ustr); + DS_CREATE(ustr, 200); /* temp user string */ pscopy(&ustr, s, s_end); - err = nupa_define(dico, spice_dstring_value(&ustr), ' ', NUPA_SUBCKT, 0.0, w, NULL); - spice_dstring_free(&ustr); + err = nupa_define(dico, ds_get_buf(&ustr), ' ', + NUPA_SUBCKT, 0.0, w, NULL); + ds_free(&ustr); } else { err = message(dico, "Subcircuit or Model without name.\n"); } @@ -542,14 +542,13 @@ findsubckt(dico_t *dico, const char *s) const char *name_e = skip_back_ws(s + strlen(s), s); const char *name_b = skip_back_non_ws(name_e, s); - SPICE_DSTRING ustr; /* u= subckt name is last token in string s */ entry_t *entry; /* symbol table entry */ - spice_dstring_init(&ustr); + DS_CREATE(ustr, 200); /* u= subckt name is last token in string s */ pscopy(&ustr, name_b, name_e); - entry = entrynb(dico, spice_dstring_value(&ustr)); - spice_dstring_free(&ustr); + entry = entrynb(dico, ds_get_buf(&ustr)); + ds_free(&ustr); if (entry && (entry->tp == NUPA_SUBCKT)) { return entry->ivl; @@ -850,10 +849,9 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) char uop[nprece + 1]; int i, natom; bool ok; - SPICE_DSTRING tstr; const char *s_orig = s; - spice_dstring_init(&tstr); + DS_CREATE(tstr, 200); for (i = 0; i <= nprece; i++) { accu[i] = 0.0; @@ -922,17 +920,17 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) u = formula(dico, s, kptr, &error); state = S_atom; if (fu > 0) { - if (fu == XFU_TERNARY_FCN) + if ((fu == XFU_TERNARY_FCN)) u = ternary_fcn(v, w, u); - else if (fu == XFU_AGAUSS) + else if ((fu == XFU_AGAUSS)) u = agauss(v, w, u); - else if (fu == XFU_GAUSS) + else if ((fu == XFU_GAUSS)) u = gauss(v, w, u); - else if (fu == XFU_UNIF) + else if ((fu == XFU_UNIF)) u = unif(v, u); - else if (fu == XFU_AUNIF) + else if ((fu == XFU_AUNIF)) u = aunif(v, u); - else if (fu == XFU_LIMIT) + else if ((fu == XFU_LIMIT)) u = limit(v, u); else u = mathfunction(fu, v, u); @@ -946,9 +944,9 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) if (fu > 0) { state = S_init; /* S_init means: ignore for the moment */ } else { - spice_dstring_reinit(&tstr); + ds_clear(&tstr); pscopy(&tstr, s, s_next); - u = fetchnumentry(dico, spice_dstring_value(&tstr), &error); + u = fetchnumentry(dico, ds_get_buf(&tstr), &error); state = S_atom; } s = s_next; @@ -1043,7 +1041,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) *perror = error; - spice_dstring_free(&tstr); + ds_free(&tstr); if (error) return 1.0; @@ -1061,7 +1059,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) */ static void -double_to_string(SPICE_DSTRINGPTR qstr_p, double value) +double_to_string(DSTRINGPTR qstr_p, double value) { char buf[ACT_CHARACTS + 1]; if (snprintf(buf, sizeof(buf), "% 25.17e", value) != ACT_CHARACTS) { @@ -1074,12 +1072,12 @@ double_to_string(SPICE_DSTRINGPTR qstr_p, double value) /* transform exression in string `t' to result q */ static bool -evaluate_expr(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char *t, const char * const t_end) +evaluate_expr(dico_t *dico, DSTRINGPTR qstr_p, const char *t, const char * const t_end) { bool err = 0; double u; - spice_dstring_reinit(qstr_p); + ds_clear(qstr_p); u = formula(dico, t, t_end, &err); if (err) @@ -1094,10 +1092,10 @@ evaluate_expr(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char *t, const char * /********* interface functions for spice3f5 extension ***********/ static char * -insertnumber(dico_t *dico, char * const s, SPICE_DSTRINGPTR ustr_p) +insertnumber(dico_t *dico, char * const s, DSTRINGPTR ustr_p) /* insert u in string s in place of the next placeholder number */ { - const char *u = spice_dstring_value(ustr_p); + const char *u = ds_get_buf(ustr_p); char buf[ACT_CHARACTS+1]; @@ -1138,9 +1136,8 @@ nupa_substitute(dico_t *dico, const char *s, char *r) const char * const s_end = s + strlen(s); bool err = 0; - SPICE_DSTRING qstr; /* temp result dynamic string */ + DS_CREATE(qstr, 200); /* temp result dynamic string */ - spice_dstring_init(&qstr); while (s < s_end) { @@ -1171,7 +1168,7 @@ nupa_substitute(dico_t *dico, const char *s, char *r) /* exeption made for .meas */ if (s + 4 == kptr && strncasecmp(s, "LAST", 4) == 0) { - spice_dstring_reinit(&qstr); + ds_clear(&qstr); sadd(&qstr, "last"); } else { err = evaluate_expr(dico, &qstr, s, kptr); @@ -1188,14 +1185,14 @@ nupa_substitute(dico_t *dico, const char *s, char *r) } Lend: - spice_dstring_free(&qstr); + ds_free(&qstr); return err; } static const char * -getword(const char *s, SPICE_DSTRINGPTR tstr_p) +getword(const char *s, DSTRINGPTR tstr_p) { const char *s_end = s + strlen(s); const char *word; @@ -1207,7 +1204,7 @@ getword(const char *s, SPICE_DSTRINGPTR tstr_p) while (alfa(*s) || isdigit_c(*s)) s++; - spice_dstring_reinit(tstr_p); + ds_clear(tstr_p); pscopy(tstr_p, word, s); return s; @@ -1215,7 +1212,7 @@ getword(const char *s, SPICE_DSTRINGPTR tstr_p) static char * -getexpress(nupa_type *type, SPICE_DSTRINGPTR tstr_p, const char *s) +getexpress(nupa_type *type, DSTRINGPTR tstr_p, const char *s) /* returns expression-like string until next separator Input i=position before expr, output i=just after expr, on separator. returns tpe=='R' if (numeric, 'S' if (string only @@ -1309,11 +1306,8 @@ nupa_assignment(dico_t *dico, const char *s, char mode) double rval = 0.0; char *t_p; /* dstring contents value */ - SPICE_DSTRING tstr; /* temporary dstring */ - SPICE_DSTRING ustr; /* temporary dstring */ - - spice_dstring_init(&tstr); - spice_dstring_init(&ustr); + DS_CREATE(tstr, 200); /* temporary dstrings */ + DS_CREATE(ustr, 200); while ((p < s_end) && (*p <= ' ')) p++; @@ -1325,7 +1319,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode) while (p < s_end) { p = getword(p, &tstr); - t_p = spice_dstring_value(&tstr); + t_p = ds_get_buf(&tstr); if (t_p[0] == '\0') { error = message(dico, " Identifier expected\n"); break; @@ -1343,7 +1337,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode) p = getexpress(&dtype, &ustr, p + 1) + 1; if (dtype == NUPA_REAL) { - const char *tmp = spice_dstring_value(&ustr); + const char *tmp = ds_get_buf(&ustr); rval = formula(dico, tmp, tmp + strlen(tmp), &error); if (error) { message(dico, @@ -1355,7 +1349,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode) wval = (int) (p - s); } - error = nupa_define(dico, spice_dstring_value(&tstr), mode /* was ' ' */ , + error = nupa_define(dico, ds_get_buf(&tstr), mode /* was ' ' */ , dtype, rval, wval, NULL); if (error) break; @@ -1366,8 +1360,8 @@ nupa_assignment(dico_t *dico, const char *s, char mode) } } - spice_dstring_free(&tstr); - spice_dstring_free(&ustr); + ds_free(&tstr); + ds_free(&ustr); return error; } @@ -1406,25 +1400,20 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, */ { int n, narg = 0; - SPICE_DSTRING subname; - SPICE_DSTRING tstr; - SPICE_DSTRING ustr; - SPICE_DSTRING vstr; - SPICE_DSTRING idlist; bool err = 0; - spice_dstring_init(&subname); - spice_dstring_init(&tstr); - spice_dstring_init(&ustr); - spice_dstring_init(&vstr); - spice_dstring_init(&idlist); + DS_CREATE(subname, 200); + DS_CREATE(tstr, 200); + DS_CREATE(ustr, 200); + DS_CREATE(vstr, 200); + DS_CREATE(idlist, 200); /***** first, analyze the subckt definition line */ - n = 0; /* number of parameters if any */ + n = 0; /* number of parameters if any */ scopys(&tstr, s); - const char *j2 = strstr(spice_dstring_value(&tstr), "subckt"); + const char *j2 = strstr(ds_get_buf(&tstr), "subckt"); if (j2) { j2 = skip_ws(j2 + 6); /* skip subckt and whitespace */ pscopy(&subname, j2, skip_non_ws(j2)); @@ -1432,7 +1421,7 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, err = message(dico, " ! a subckt line!\n"); } - const char *i2 = strstr(spice_dstring_value(&tstr), "params:"); + const char *i2 = strstr(ds_get_buf(&tstr), "params:"); if (i2) { const char *optr, *jptr; @@ -1441,9 +1430,9 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, /* search identifier to the left of '=' assignments */ - for (optr = spice_dstring_value(&tstr); - (jptr = strchr(optr, '=')) != NULL; - optr = jptr + 1) + for (optr = ds_get_buf(&tstr); + (jptr = strchr(optr, '=')) != NULL; + optr = jptr + 1) { const char *kptr, *hptr; @@ -1486,22 +1475,20 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, */ scopys(&tstr, skip_non_ws(x)); - char * const t_p = spice_dstring_value(&tstr); + char * const t_p = ds_get_buf(&tstr); char *jp = NULL; /* search for the last occurence of `subname' in the given line */ for (;;) { - char *next_p = - search_isolated_identifier(jp ? jp + 1 : t_p, - spice_dstring_value(&subname)); + char *next_p = search_isolated_identifier(jp ? jp + 1 : t_p, + ds_get_buf(&subname)); if (!next_p) break; jp = next_p; } if (jp) { - - jp += spice_dstring_length(&subname); + jp += ds_get_length(&subname); while (isspace_c(*jp) || (*jp == ',')) jp++; @@ -1512,7 +1499,7 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, /* try to fetch valid arguments */ char *kp = jp; - spice_dstring_reinit(&ustr); + ds_clear(&ustr); if (alfanum(*kp) || *kp == '.') { /* number, identifier */ @@ -1526,9 +1513,9 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, message(dico, "Subckt call, symbol %c not understood\n", *kp); } - char * const u_p = spice_dstring_value(&ustr); + char * const u_p = ds_get_buf(&ustr); if (*u_p) { - char * const idlist_p = spice_dstring_value(&idlist); + char * const idlist_p = ds_get_buf(&idlist); char *dollar = strchr(idlist_p, '$'); if (dollar) { /* replace dollar with expression string u */ @@ -1552,17 +1539,17 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, err = message(dico, " Mismatch: %d formal but %d actual params.\n" "%s\n", - n, narg, spice_dstring_value(&idlist)); + n, narg, ds_get_buf(&idlist)); /* ;} else { debugwarn(dico, idlist) */ } - err = nupa_assignment(dico, spice_dstring_value(&idlist), 'N'); + err = nupa_assignment(dico, ds_get_buf(&idlist), 'N'); - spice_dstring_free(&subname); - spice_dstring_free(&tstr); - spice_dstring_free(&ustr); - spice_dstring_free(&vstr); - spice_dstring_free(&idlist); + ds_free(&subname); + ds_free(&tstr); + ds_free(&ustr); + ds_free(&vstr); + ds_free(&idlist); return err; } diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 3751e4bef..3daba86cf 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -33,41 +33,45 @@ struct dvec *EVTfindvec(char *node); static void vec_rebuild_lookup_table(struct plot *pl) { - int cnt; /* count entries */ - struct dvec *d; /* dynamic vector */ - NGHASHPTR lookup_p; /* lookup table for speed */ - SPICE_DSTRING dbuf; /* dynamic buffer */ - char *lower_name; /* lower case name */ - - if (pl->pl_lookup_table) { + if (pl->pl_lookup_table) { /* existing table */ nghash_empty(pl->pl_lookup_table, NULL, NULL); - } else { - cnt = 0; - for (d = pl->pl_dvecs; d; d = d->v_next) + } + else { /* new table */ + int cnt = 0; /* count entries */ + struct dvec *d; /* dynamic vector */ + for (d = pl->pl_dvecs; d; d = d->v_next) { /* get # vec */ cnt++; + } pl->pl_lookup_table = nghash_init(cnt); /* allow multiple entries */ nghash_unique(pl->pl_lookup_table, FALSE); } - lookup_p = pl->pl_lookup_table; - spice_dstring_init(&dbuf); - for (d = pl->pl_dvecs; d; d = d->v_next) { - spice_dstring_reinit(&dbuf); - lower_name = spice_dstring_append_lower(&dbuf, d->v_name, -1); - nghash_insert(lookup_p, lower_name, d); + + { + /* Access lookup table directly for speed */ + NGHASHPTR lookup_p = pl->pl_lookup_table; + DS_CREATE(dbuf, 200); /* make dynamic buffer */ + struct dvec *d; /* dynamic vector */ + for (d = pl->pl_dvecs; d; d = d->v_next) { + ds_clear(&dbuf); + if (ds_cat_str_case(&dbuf, d->v_name, ds_case_lower) != DS_E_OK) { + controlled_exit(-1); + } + char * const lower_name = ds_get_buf(&dbuf); + nghash_insert(lookup_p, lower_name, d); /* add lower-cased name */ + } /* end of loop over vectors */ + ds_free(&dbuf); } - spice_dstring_free(&dbuf); + pl->pl_lookup_valid = TRUE; -} +} /* end of function vec_rebuild_lookup_table */ + /* Find a named vector in a plot. We are careful to copy the vector if * v_link2 is set, because otherwise we will get screwed up. */ static struct dvec * findvec(char *word, struct plot *pl) { - SPICE_DSTRING dbuf; /* dynamic buffer */ - char *lower_name; /* lower case name */ - char *node_name; struct dvec *d, *newv = NULL, *end = NULL, *v; if (pl == NULL) @@ -152,8 +156,11 @@ findvec(char *word, struct plot *pl) { if (!pl->pl_lookup_valid) vec_rebuild_lookup_table(pl); - spice_dstring_init(&dbuf); - lower_name = spice_dstring_append_lower(&dbuf, word, -1); + DS_CREATE(dbuf, 200); /* make dynamic buffer */ + if (ds_cat_str_case(&dbuf, word, ds_case_lower) != DS_E_OK) { + controlled_exit(-1); + } + char * const lower_name = ds_get_buf(&dbuf); for (d = nghash_find(pl->pl_lookup_table, lower_name); d; @@ -163,11 +170,15 @@ findvec(char *word, struct plot *pl) { } if (!d) { - spice_dstring_reinit(&dbuf); - spice_dstring_append(&dbuf, "v(", -1); - spice_dstring_append_lower(&dbuf, word, -1); - node_name = spice_dstring_append_char(&dbuf, ')'); - + ds_clear(&dbuf); + bool f_ok = ds_cat_str(&dbuf, "v(") == DS_E_OK; + f_ok &= ds_cat_str_case(&dbuf, word, + ds_case_lower) == DS_E_OK; + f_ok &= ds_cat_char(&dbuf, ')') == DS_E_OK; + if (!f_ok) { + controlled_exit(-1); + } + char * const node_name = ds_get_buf(&dbuf); for (d = nghash_find(pl->pl_lookup_table, node_name); d; d = nghash_find_again(pl->pl_lookup_table, node_name)) { @@ -176,7 +187,7 @@ findvec(char *word, struct plot *pl) { } } - spice_dstring_free(&dbuf); + ds_free(&dbuf); #ifdef XSPICE /* gtri - begin - Add processing for getting event-driven vector */ @@ -191,8 +202,9 @@ findvec(char *word, struct plot *pl) { vec_new(d); } - return (d); -} + return d; +} /* end of function findvec */ + /* If there are imbedded numeric strings, compare them numerically, not diff --git a/src/include/ngspice/dstring.h b/src/include/ngspice/dstring.h index b95f76234..25ad51773 100644 --- a/src/include/ngspice/dstring.h +++ b/src/include/ngspice/dstring.h @@ -1,40 +1,250 @@ /* dstring.h */ -#ifndef ngspice_DSTRING_H -#define ngspice_DSTRING_H +#ifndef DSTRING_H +#define DSTRING_H -/* ----------------------------------------------------------------- - * This structure is modified from Tcl. We do this to avoid a - * conflict and later add a conditional compile to just use the Tcl - * code if desired. ------------------------------------------------------------------ */ -#define SPICE_DSTRING_STATIC_SIZE 200 -typedef struct spice_dstring { - char *string; /* Points to beginning of string: either - * staticSpace below or a malloced array. */ - int length ; /* Number of characters in the string excluding the - * terminating NULL. */ - int spaceAvl ; /* Total number of bytes available for the - * string and its terminating NULL char. */ - char staticSpace[SPICE_DSTRING_STATIC_SIZE] ; - /* Space to use in common case where string - * is small. */ -} SPICE_DSTRING, *SPICE_DSTRINGPTR ; -/* ----------------------------------------------------------------- - * spice_dstring_xxxx routines. Used to manipulate dynamic strings. ------------------------------------------------------------------ */ -extern void spice_dstring_init(SPICE_DSTRINGPTR dsPtr) ; -extern char *spice_dstring_append(SPICE_DSTRINGPTR dsPtr,const char *string,int length) ; -extern char *spice_dstring_append_lower(SPICE_DSTRINGPTR dsPtr,const char *string,int length) ; -extern char *spice_dstring_append_char(SPICE_DSTRINGPTR dsPtr,char c) ; -extern char *spice_dstring_print(SPICE_DSTRINGPTR dsPtr,const char *format, ... ) ; -extern char *spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) ; -extern char *_spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) ; -extern void spice_dstring_free(SPICE_DSTRINGPTR dsPtr) ; -#define spice_dstring_reinit(x_xz) spice_dstring_setlength(x_xz,0) ; -#define spice_dstring_value(x_xz) ((x_xz)->string) -#define spice_dstring_space(x_xz) ((x_xz)->spaceAvl) -#define spice_dstring_length(x_xz) ((x_xz)->length) +#include +#include +#include +#include -#endif +/* Error codes */ +#define DS_E_OK 0 +#define DS_E_INVALID (-1) +#define DS_E_NO_MEMORY (-2) + +/* Macros to create and initialize the most common type of dstring, which is + * one that uses the stack for the initial buffer and begins empty. + * + * Example: + * + * DS_CREATE(ds1, 50); // Creates dstring ds1 backed by 50 bytes of stack + * // memory and initialized to "". + * Note that each DS_CREATE macro must be on a separate line due to the use + * of the __LINE__ macro. Using __COUNTER__ in its place would resolve this + * issue, but __COUNTER__ is not part of the ANSI standard. + */ +#undef DS_CONCAT +#undef DS_CONCAT2 +#define DS_CONCAT2(a, b) a##b +#define DS_CONCAT(a, b) DS_CONCAT2(a, b) +#define DS_CREATE(ds_name, n) \ + char DS_CONCAT(ds_buf___, __LINE__)[n]; \ + DSTRING ds_name; \ + ds_init(&ds_name, DS_CONCAT(ds_buf___, __LINE__), 0,\ + sizeof DS_CONCAT(ds_buf___, __LINE__), ds_buf_type_stack) + + +/* Structure for maintaining a dynamic string */ +typedef struct Dstring { + char *p_buf; /* Active data buffer */ + size_t length; /* Number of characters in the string excluding the + * terminating NULL. */ + size_t n_byte_alloc; /* Allocated size of current buffer */ + char *p_stack_buf; /* address of stack-based buffer backing dstring + * or NULL if none */ + size_t n_byte_stack_buf; /* size of stack_buffer or 0 if none */ +} DSTRING, *DSTRINGPTR; + + +/* Enumeration defining buffer types used during initialization */ +typedef enum ds_buf_type { + ds_buf_type_stack, /* Buffer allocated from stack */ + ds_buf_type_heap /* Buffer allocated from heap */ +} ds_buf_type_t; + +/* Enumeration defining case conversion */ +typedef enum ds_case { + ds_case_as_is, /* Leave characters as they are */ + ds_case_lower, /* Losercase chars */ + ds_case_upper /* Uppercase chars */ +} ds_case_t; + + + +/* General initialization */ +int ds_init(DSTRING *p_ds, char *p_buf, size_t length_string, + size_t n_byte_buf, ds_buf_type_t type_buffer); + +/* Free all memory used */ +void ds_free(DSTRING *p_ds); + + +/* Concatenate string */ +int ds_cat_str_case(DSTRING *p_ds, const char *sz, ds_case_t case_type); +inline int ds_cat_str(DSTRING *p_ds, const char *sz) +{ + return ds_cat_str_case(p_ds, sz, ds_case_as_is); +} /* end of function ds_cat_str */ + + + +/* Concatenate character */ +int ds_cat_char_case(DSTRING *p_ds, char c, ds_case_t case_type); +inline int ds_cat_char(DSTRING *p_ds, char c) +{ + return ds_cat_char_case(p_ds, c, ds_case_as_is); +} /* end of function ds_cat_char */ + + + +/* Concatenate another dstring */ +int ds_cat_ds_case(DSTRING *p_ds_dst, const DSTRING *p_ds_src, + ds_case_t case_type); +inline int ds_cat_ds(DSTRING *p_ds_dst, const DSTRING *p_ds_src) +{ + return ds_cat_ds_case(p_ds_dst, p_ds_src, ds_case_as_is); +} /* end of function ds_cat_ds */ + + + +/* General concatenation of a memory buffer */ +int ds_cat_mem_case(DSTRING *p_ds, const char *p_src, size_t n_char, + ds_case_t type_case); +inline int ds_cat_mem(DSTRING *p_ds, const char *p_src, size_t n_char) +{ + return ds_cat_mem_case(p_ds, p_src, n_char, ds_case_as_is); +} /* end of function ds_cat_mem */ + + + +/* Ensure minimum internal buffer size */ +int ds_reserve(DSTRING *p_ds, size_t n_byte_alloc_min); + +/* Concatenate the result of a printf-style format */ +int ds_cat_printf(DSTRING *p_ds, const char *sz_fmt, ...); + +/* Concatenate the result of a printf-style format using va_list */ +int ds_cat_vprintf(DSTRING *p_ds, const char *sz_fmt, va_list p_arg); + +/* Reallocate/free to eliminate unused buffer space */ +int ds_compact(DSTRING *p_ds); + + + +/* This function sets the length of the buffer to some size less than + * the current allocated size + * + * Return codes + * DS_E_OK: length set OK + * DS_E_INVALID: length to large for current allocation + */ +inline int ds_set_length(DSTRING *p_ds, size_t length) +{ + if (length >= p_ds->n_byte_alloc) { + return DS_E_INVALID; + } + p_ds->length = length; + p_ds->p_buf[p_ds->length] = '\0'; + return DS_E_OK; +} /* end of function ds_set_length */ + + + +/* Sets the length of the data in the buffer to 0. It is equivalent to + * ds_set_length(p_ds, 0), except that the check for a valid length can + * be skipped since 0 is always valid. */ +inline void ds_clear(DSTRING *p_ds) +{ + p_ds->length = 0; + p_ds->p_buf[0] = '\0'; +} /* end of function ds_clear */ + + + +/* This function, if successful, returns an allocated buffer with the + * string to the caller and frees any other resources used by the DSTRING. + * If the buffer is not allocated and the DS_FREE_MOVE_OPT_FORCE_ALLOC + * option is not selected, NULL is returned. + * + * Parameters + * p_ds: Address of DSTRING to free + * opt: Bitwise options + * DS_FREE_MOVE_OPT_FORCE_ALLOC -- Force allocation in all cases. + * If fails, the DSTRING is unchanged. + * DS_FREE_MOVE_OPT_COMPACT -- Resize allocation to minimum size + * if one already exists. + * + * Return values + * The data string is returned as an allocation to be freed by the caller + * NULL is returned if either the allocation was stack-based and + * DS_FREE_MOVE_OPT_FORCE_ALLOC was not selected or if + * DS_FREE_MOVE_OPT_COMPACT or DS_FREE_MOVE_OPT_FORCE_ALLOC + * options were given and there was an allocation failure. + * In any case when NULL is returned, the DSTRING is unchanged + * on return. + * + * Remarks + * To force freeing of resources if this function fails, either it can + * be called again with no options or equivalently ds_free() can be used. + */ +#define DS_FREE_MOVE_OPT_FORCE_ALLOC 1 +#define DS_FREE_MOVE_OPT_COMPACT 2 +inline char *ds_free_move(DSTRING *p_ds, unsigned int opt) +{ + char * const p_buf_active = p_ds->p_buf; + + /* If the buffer is from the stack, allocate if requested. Note that the + * compaction option is meaningless in this case since it is allocated + * to the minimum size required */ + if (p_buf_active == p_ds->p_stack_buf) { /* not allocated */ + if (opt & DS_FREE_MOVE_OPT_FORCE_ALLOC) { + /* Allocate to minimum size */ + size_t n_byte_alloc = p_ds->length + 1; + char * const p_ret = (char *) malloc(n_byte_alloc); + if (p_ret == (char *) NULL) { + return (char *) NULL; + } + return memcpy(p_ret, p_buf_active, n_byte_alloc); + } + return (char *) NULL; + } + /* Else allocated */ + if (opt & DS_FREE_MOVE_OPT_COMPACT) { + /* Allocate to minimum size */ + size_t n_byte_alloc = p_ds->length + 1; + char * const p_ret = (char *) realloc(p_buf_active, n_byte_alloc); + if (p_ret == (char *) NULL) { + /* Realloc to smaller size somehow failed! */ + return (char *) NULL; + } + return p_ret; /* Return resized allocation */ + } + return p_buf_active; /* Return unchanged */ +} /* end of function ds_free_move */ + + + +/* Returns the address of the buffer. The caller should never attempt + * to free the buffer. With care (not changing the length), it can + * be modified. */ +inline char *ds_get_buf(DSTRING *p_ds) +{ + return p_ds->p_buf; +} /* end of function ds_get_buffer */ + + + +/* Returns the current dstring length */ +inline size_t ds_get_length(const DSTRING *p_ds) +{ + return p_ds->length; +} /* end of function ds_get_length */ + + + +/* Returns the allocated dstring buffer size */ +inline size_t ds_get_buf_size(const DSTRING *p_ds) +{ + return p_ds->n_byte_alloc; +} /* end of function ds_get_buf_size */ + + + +#ifdef DSTRING_UNIT_TEST +#include +int ds_test(FILE *fp); +#endif /* UNIT_TEST_DSTRING */ + +#endif /* include guard */ diff --git a/src/misc/dstring.c b/src/misc/dstring.c index 65c0c37d3..3706134b4 100644 --- a/src/misc/dstring.c +++ b/src/misc/dstring.c @@ -1,409 +1,539 @@ /* ----------------------------------------------------------------- -FILE: dstring.c +FILE: dstring.c DESCRIPTION:This file contains the routines for manipulating dynamic strings. ----------------------------------------------------------------- */ -#include "ngspice/ngspice.h" +#include #include +#include +#include +#include +#include +#include + #include "ngspice/dstring.h" -/* definitions local to this file only */ -/* ********************** TYPE DEFINITIONS ************************* */ +static int ds_reserve_internal(DSTRING *p_ds, + size_t n_byte_alloc_opt, size_t n_byte_alloc_min); -/* ********************** STATIC DEFINITIONS ************************* */ -/* - *---------------------------------------------------------------------- +/* Instantiations of dstring functions in case inlining is not performed */ +int ds_cat_str(DSTRING *p_ds, const char *sz); +int ds_cat_char(DSTRING *p_ds, char c); +int ds_cat_ds(DSTRING *p_ds_dst, const DSTRING *p_ds_src); +int ds_cat_mem(DSTRING *p_ds, const char *p_src, size_t n_char); +int ds_set_length(DSTRING *p_ds, size_t length); +void ds_clear(DSTRING *p_ds); +char *ds_free_move(DSTRING *p_ds, unsigned int opt); +char *ds_get_buf(DSTRING *p_ds); +size_t ds_get_length(const DSTRING *p_ds); +size_t ds_get_buf_size(const DSTRING *p_ds); + + +/* This function initalizes a dstring using *p_buf as the initial backing * - * spice_dstring_init -- + * Parameters + * p_buf: Inital buffer backing the dstring + * length_string: Length of string in the initial buffer + * n_byte_data: Length of initial buffer. Must be at least 1 + * type_buffer: Type of buffer providing initial backing * - * Initializes a dynamic string, discarding any previous contents - * of the string (spice_dstring_free should have been called already - * if the dynamic string was previously in use). - * - * Results: - * None. - * - * Side effects: - * The dynamic string is initialized to be empty. - * - *---------------------------------------------------------------------- + * Return codes + * DS_E_OK: Init OK + * DS_E_INVALID: n_byte_data = 0 length_string too long, + * or unknown buffer type */ - -void spice_dstring_init(SPICE_DSTRINGPTR dsPtr) +int ds_init(DSTRING *p_ds, char *p_buf, size_t length_string, + size_t n_byte_buf, ds_buf_type_t type_buffer) { - dsPtr->string = dsPtr->staticSpace ; - dsPtr->length = 0 ; - dsPtr->spaceAvl = SPICE_DSTRING_STATIC_SIZE ; - dsPtr->staticSpace[0] = '\0'; -} /* end spice_dstring_init() */ + /* Validate buffer size */ + if (n_byte_buf == 0) { + return DS_E_INVALID; + } -/* - *---------------------------------------------------------------------- - * - * spice_dstring_append -- - * - * Append more characters to the current value of a dynamic string. - * - * Results: - * The return value is a pointer to the dynamic string's new value. - * - * Side effects: - * Length bytes from string (or all of string if length is less - * than zero) are added to the current value of the string. Memory - * gets reallocated if needed to accomodate the string's new size. - * - * Notes: char *string; String to append. If length is -1 then - * this must be null-terminated. - * INT length; Number of characters from string to append. - * If < 0, then append all of string, up to null at end. - * - *---------------------------------------------------------------------- - */ -char *spice_dstring_append(SPICE_DSTRINGPTR dsPtr, const char *string, int length) + /* Set current buffer */ + p_ds->p_buf = p_buf; + + /* Set size of current string >= rather than > because this function + * adds a terminating null */ + if (length_string >= n_byte_buf) { + return DS_E_INVALID; + } + + p_ds->n_byte_alloc = n_byte_buf; + p_ds->length = length_string; + p_ds->p_buf[length_string] = '\0'; + + /* Set stack buffer */ + if (type_buffer == ds_buf_type_stack) { + p_ds->p_stack_buf = p_buf; + p_ds->n_byte_stack_buf = n_byte_buf; + } + else if (type_buffer == ds_buf_type_heap) { + p_ds->p_stack_buf = (char *) NULL; + p_ds->n_byte_stack_buf = 0; + } + else { /* unknown buffer type */ + return DS_E_INVALID; + } + + return DS_E_OK; +} /* end of function ds_init */ + + + +/* This function frees all memory used by the dstring. After calling this + * function, the dstring should not be used again. */ +void ds_free(DSTRING *p_ds) { - int newSize ; /* needed size */ - char *newString ; /* newly allocated string buffer */ - char *dst ; /* destination */ - const char *end ; /* end of string */ - - if( length < 0){ - length = (int) strlen(string) ; + if (p_ds->p_buf != p_ds->p_stack_buf) { + free((void *) p_ds->p_buf); } - newSize = length + dsPtr->length ; +} /* end of function ds_free */ - /* ----------------------------------------------------------------- - * Allocate a larger buffer for the string if the current one isn't - * large enough. Allocate extra space in the new buffer so that there - * will be room to grow before we have to allocate again. - ----------------------------------------------------------------- */ - if (newSize >= dsPtr->spaceAvl) { - dsPtr->spaceAvl = 2 * newSize ; - newString = TMALLOC(char, dsPtr->spaceAvl) ; - memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ; - if (dsPtr->string != dsPtr->staticSpace) { - txfree(dsPtr->string) ; - } - dsPtr->string = newString; - } - /* ----------------------------------------------------------------- - * Copy the new string into the buffer at the end of the old - * one. - ----------------------------------------------------------------- */ - for( dst = dsPtr->string + dsPtr->length, end = string+length; - string < end; string++, dst++) { - *dst = *string ; - } - *dst = '\0' ; - dsPtr->length += length ; - return(dsPtr->string) ; - -} /* end spice_dstring_append() */ - -/* - *---------------------------------------------------------------------- - * - * spice_dstring_append_lower -- - * - * Append more characters converted to lower case to the current - * value of a dynamic string. - * - * Results: - * The return value is a pointer to the dynamic string's new value. - * - * Side effects: - * Length bytes from string (or all of string if length is less - * than zero) are added to the current value of the string. Memory - * gets reallocated if needed to accomodate the string's new size. - * - * Notes: char *string; String to append. If length is -1 then - * this must be null-terminated. - * INT length; Number of characters from string to append. - * If < 0, then append all of string, up to null at end. - * - *---------------------------------------------------------------------- - */ -char *spice_dstring_append_lower(SPICE_DSTRINGPTR dsPtr, const char *string, int length) +/* Concatenate string */ +int ds_cat_str_case(DSTRING *p_ds, const char *sz, ds_case_t case_type) { - int newSize ; /* needed size */ - char *newString ; /* newly allocated string buffer */ - char *dst ; /* destination */ - const char *end ; /* end of string */ + return ds_cat_mem_case(p_ds, sz, strlen(sz), case_type); +} /* end of function ds_cat_str_case */ - if( length < 0){ - length = (int) strlen(string) ; - } - newSize = length + dsPtr->length ; - /* ----------------------------------------------------------------- - * Allocate a larger buffer for the string if the current one isn't - * large enough. Allocate extra space in the new buffer so that there - * will be room to grow before we have to allocate again. - ----------------------------------------------------------------- */ - if (newSize >= dsPtr->spaceAvl) { - dsPtr->spaceAvl = 2 * newSize ; - newString = TMALLOC(char, dsPtr->spaceAvl) ; - memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ; - if (dsPtr->string != dsPtr->staticSpace) { - txfree(dsPtr->string) ; - } - dsPtr->string = newString; - } - /* ----------------------------------------------------------------- - * Copy the new string into the buffer at the end of the old - * one. - ----------------------------------------------------------------- */ - for( dst = dsPtr->string + dsPtr->length, end = string+length; - string < end; string++, dst++) { - if( isupper_c(*string) ) { - *dst = tolower_c(*string) ; - } else { - *dst = *string ; +/* Concatenate character */ +int ds_cat_char_case(DSTRING *p_ds, char c, ds_case_t case_type) +{ + return ds_cat_mem_case(p_ds, &c, 1, case_type); +} /* end of function ds_cat_char_case */ + + + +/* Concatenate another dstring */ +int ds_cat_ds_case(DSTRING *p_ds_dst, const DSTRING *p_ds_src, + ds_case_t case_type) +{ + return ds_cat_mem_case(p_ds_dst, p_ds_src->p_buf, p_ds_src->length, + case_type); +} /* end of function ds_cat_ds_case */ + + + +/* General concatenation of a memory buffer. A terminating null is added. */ +int ds_cat_mem_case(DSTRING *p_ds, const char *p_src, size_t n_char, + ds_case_t type_case) +{ + /* Resize buffer if necessary. Double required size, if available, + * to reduce the number of allocations */ + const size_t length_new = p_ds->length + n_char; + const size_t n_byte_needed = length_new + 1; + if (n_byte_needed > p_ds->n_byte_alloc) { + if (ds_reserve_internal(p_ds, + 2 * n_byte_needed, n_byte_needed) == DS_E_NO_MEMORY) { + return DS_E_NO_MEMORY; } } - *dst = '\0' ; - dsPtr->length += length ; - return(dsPtr->string) ; - -} /* end spice_dstring_append_lower() */ - -/* ----------------------------------------------------------------- - * Function: add character c to dynamic string dstr_p. - * ----------------------------------------------------------------- */ -char *spice_dstring_append_char( SPICE_DSTRINGPTR dstr_p, char c) -{ - return spice_dstring_append( dstr_p, &c, 1 ) ; -} /* end spice_dstring_append_char() */ - -static int spice_format_length( const char *fmt, va_list args ) -{ - int i ; /* integer */ - int len ; /* length of format */ - int size_format ; /* width of field */ - int found_special ; /* look for special characters */ - char *s ; /* string */ - double d ; - - /* ----------------------------------------------------------------- - * First find length of buffer. - ----------------------------------------------------------------- */ - len = 0 ; - while(fmt && *fmt){ - if( *fmt == '%' ){ - fmt++ ; - if( *fmt == '%' ){ - len++ ; - } else { - /* ----------------------------------------------------------------- - * We have a real formatting character, loop until we get a special - * character. - ----------------------------------------------------------------- */ - if( *fmt == '.' || *fmt == '-' ){ - fmt++ ; /* skip over these characters */ - } - size_format = atoi(fmt) ; - if( size_format > 0 ){ - len += size_format ; - } - found_special = FALSE ; - for( ; fmt && *fmt ; fmt++ ){ - switch( *fmt ){ - case 's': - s = va_arg(args, char *) ; - if( s ){ - len += (int) strlen(s) ; - } - found_special = TRUE ; - break ; - case 'i': - case 'd': - case 'o': - case 'x': - case 'X': - case 'u': - i = va_arg(args, int) ; - len += 10 ; - found_special = TRUE ; - break ; - case 'c': - i = va_arg(args, int) ; - len++ ; - found_special = TRUE ; - break ; - case 'f': - case 'e': - case 'F': - case 'g': - case 'G': - d = va_arg(args, double) ; - len += 35 ; - found_special = TRUE ; - break ; - default: - ; - } /* end switch() */ - - if( found_special ){ - break ; - } - } - } - } else { - len++ ; - } - fmt++ ; - } /* end while() */ - - return(len) ; - -} /* end Ymessage_format_length() */ - - -char *spice_dstring_print( SPICE_DSTRINGPTR dsPtr, const char *format, ... ) -{ - va_list args ; - int format_len ; /* length of format */ - int length ; /* new length */ - int orig_length ; /* original length of buffer */ - char *buffer ; /* proper length of buffer */ - - /* ----------------------------------------------------------------- - * First get the length of the buffer needed. - ----------------------------------------------------------------- */ - va_start( args, format ) ; - format_len = spice_format_length(format, args) ; - va_end(args) ; - - /* ----------------------------------------------------------------- - * Next allocate the proper buffer size. - ----------------------------------------------------------------- */ - orig_length = dsPtr->length ; - length = orig_length + format_len + 1 ; - buffer = spice_dstring_setlength( dsPtr, length) ; - - /* ----------------------------------------------------------------- - * Convert the format. - ----------------------------------------------------------------- */ - va_start( args, format ) ; - if( format ){ - vsprintf( buffer + orig_length, format, args ) ; - dsPtr->length = (int) strlen(buffer) ; - } else { - buffer = NULL ; + /* For "as-is" can simply memcpy */ + if (type_case == ds_case_as_is) { + char *p_dst = p_ds->p_buf + p_ds->length; + (void) memcpy(p_dst, p_src, n_char); + p_dst += n_char; + *p_dst = '\0'; + p_ds->length = length_new; + return DS_E_OK; } - va_end(args) ; - return( buffer ) ; -} /* end spice_dstring_print() */ - -/* - *---------------------------------------------------------------------- + /* For lowercasing, work char by char */ + if (type_case == ds_case_lower) { + char *p_dst = p_ds->p_buf + p_ds->length; + char *p_dst_end = p_dst + n_char; + for ( ; p_dst < p_dst_end; p_dst++, p_src++) { + *p_dst = (char) tolower(*p_src); + } + *p_dst_end = '\0'; + p_ds->length = length_new; + return DS_E_OK; + } + + /* Uppercasing done like lowercasing. Note that it would be possible to + * use a function pointer and select either tolower() or toupper() based + * on type_case, but doing so may degrade performance by inhibiting + * inlining. */ + if (type_case == ds_case_upper) { + char *p_dst = p_ds->p_buf + p_ds->length; + char *p_dst_end = p_dst + n_char; + for ( ; p_dst < p_dst_end; p_dst++, p_src++) { + *p_dst = (char) toupper(*p_src); + } + *p_dst_end = '\0'; + p_ds->length = length_new; + return DS_E_OK; + } + + return DS_E_INVALID; /* unknown case type */ +} /* end of function ds_cat_mem_case */ + + + +/* Ensure minimum internal buffer size */ +int ds_reserve(DSTRING *p_ds, size_t n_byte_alloc) +{ + /* Return if buffer already large enough */ + if (p_ds->n_byte_alloc >= n_byte_alloc) { + return DS_E_OK; + } + + return ds_reserve_internal(p_ds, n_byte_alloc, 0); +} /* end of function ds_reserve */ + + + +/* This function resizes the buffer for the string and handles freeing + * the original alloction, if necessary. It is assumed that the requested + * size or sizes are larger than the current size. * - * _spice_dstring_setlength -- + * Parameters + * p_ds: Dstring pointer + * n_byte_alloc_opt: Optimal alloction amount + * n_byte_alloc_min: Absolute minimum allocation amount or 0 if no + * smaller amount can be allocated * - * Change the length of a dynamic string. This can cause the - * string to either grow or shrink, depending on the value of - * length. + * Return codes + * DS_E_OK: At least the minimum allocation was performed + * DS_E_NO_MEMORY: Unable to resize the buffer */ +static int ds_reserve_internal(DSTRING *p_ds, + size_t n_byte_alloc_opt, size_t n_byte_alloc_min) +{ + size_t n_byte_alloc = n_byte_alloc_opt; + /* Allocate. First try (larger) optimal size, and gradually fall back + * to min size if that fails and one was provided. */ + char * p_buf_new; + if (n_byte_alloc_min == 0) { + n_byte_alloc_min = n_byte_alloc_opt; + } + for ( ; ; ) { + if ((p_buf_new = (char *) malloc(n_byte_alloc)) != (char *) NULL) { + break; /* Allocated OK */ + } + + if (n_byte_alloc == n_byte_alloc_min) { /* min alloc failed */ + return DS_E_NO_MEMORY; + } + + if ((n_byte_alloc /= 2) < n_byte_alloc_min) { /* last try */ + n_byte_alloc = n_byte_alloc_min; + } + } /* end of loop trying smaller allocations */ + + /* Copy to the new buffer */ + (void) memcpy(p_buf_new, p_ds->p_buf, p_ds->length + 1); + + /* If there already was a dynamic allocation, free it */ + if (p_ds->p_buf != p_ds->p_stack_buf) { + free((void *) p_ds->p_buf); + } + + /* Assign new active buffer and its size */ + p_ds->p_buf = p_buf_new; + p_ds->n_byte_alloc = n_byte_alloc; + + return DS_E_OK; +} /* end of function ds_reserve_nocheck */ + + + +/* Concatenate the result of a printf-style format * - * Results: - * Returns the current string buffer. + * Return codes as for ds_cat_vprintf */ +int ds_cat_printf(DSTRING *p_ds, const char *sz_fmt, ...) +{ + va_list p_arg; + va_start(p_arg, sz_fmt); + const int xrc = ds_cat_vprintf(p_ds, sz_fmt, p_arg); + va_end(p_arg); + return xrc; +} /* end of function ds_cat_printf */ + + + +/* Concatenate the result of a printf-style format using va_list * - * Side effects: - * The length of dsPtr is changed to length but a null byte is not - * stored at that position in the string. Use spice_dstring_setlength - * for that function. If length is larger - * than the space allocated for dsPtr, then a panic occurs. - * - *---------------------------------------------------------------------- + * Return codes + * DS_E_OK: Formatted OK + * DS_E_NO_MEMORY: Unable to allocate memory to resize buffer + * DS_E_INVALID: Invalid formatter / data */ - -char *_spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) +int ds_cat_vprintf(DSTRING *p_ds, const char *sz_fmt, va_list p_arg) { - char *newString ; - - if (length < 0) { - length = 0 ; + /* Make a copy of the argument list in case need to format more than + * once */ + va_list p_arg2; + va_copy(p_arg2, p_arg); + const size_t n_byte_free = p_ds->n_byte_alloc - p_ds->length; + char * const p_dst = p_ds->p_buf + p_ds->length; + const int rc = vsnprintf(p_dst, n_byte_free, sz_fmt, p_arg); + if (rc < 0) { /* Check for formatting error */ + return DS_E_INVALID; } - if (length >= dsPtr->spaceAvl) { - dsPtr->spaceAvl = length+1; - newString = TMALLOC(char, dsPtr->spaceAvl) ; - /* ----------------------------------------------------------------- - * SPECIAL NOTE: must use memcpy, not strcpy, to copy the string - * to a larger buffer, since there may be embedded NULLs in the - * string in some cases. - ----------------------------------------------------------------- */ - memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ; - if( dsPtr->string != dsPtr->staticSpace ) { - txfree(dsPtr->string) ; - } - dsPtr->string = newString ; + /* Else check for buffer large enough and set length if it is */ + if (rc < n_byte_free) { + p_ds->length += rc; + return DS_E_OK; } - dsPtr->length = length ; - return(dsPtr->string) ; -} /* end _spice_dstring_setlength() */ + /* Else buffer too small, so resize and format again */ + { + /* Double required size to avoid excessive allocations +1 for + * null, which is not included in the count returned by snprintf */ + const size_t n_byte_alloc_min = p_ds->length + rc + 1; + if (ds_reserve_internal(p_ds, + 2 * n_byte_alloc_min, n_byte_alloc_min) == DS_E_NO_MEMORY) { + /* vsnprintf may have written bytes to the buffer. + * Ensure that dstring in a consistent state by writing + * a null at the length of the string */ + p_ds->p_buf[p_ds->length] = '\0'; + return DS_E_NO_MEMORY; + } + const size_t n_byte_free2 = p_ds->n_byte_alloc - p_ds->length; + char * const p_dst2 = p_ds->p_buf + p_ds->length; + const int rc2 = vsnprintf(p_dst2, n_byte_free2, sz_fmt, p_arg2); + if (rc2 < 0) { /* Check for formatting error */ + /* vsnprintf may have written bytes to the buffer. + * Ensure that dstring in a consistent state by writing + * a null at the length of the string */ + p_ds->p_buf[p_ds->length] = '\0'; + return DS_E_INVALID; + } - -/* - *---------------------------------------------------------------------- - * - * spice_dstring_setlength -- - * - * Change the length of a dynamic string. This can cause the - * string to either grow or shrink, depending on the value of - * length. - * - * Results: - * Returns the current string buffer. - * - * Side effects: - * The length of dsPtr is changed to length and a null byte is - * stored at that position in the string. If length is larger - * than the space allocated for dsPtr, then a panic occurs. - * - *---------------------------------------------------------------------- - */ + /* Else update length. No need to check buffer size since it was + * sized to fit the string. */ + p_ds->length += rc2; + return DS_E_OK; + } +} /* end of function ds_cat_vprintf */ -char *spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) + + + +/* Reallocate/free to eliminate unused buffer space. + * + * Return codes + * DS_E_OK: Compacted OK + * DS_E_NO_MEMORY: Compaction failed, but dstring still valid */ +int ds_compact(DSTRING *p_ds) { - char *str_p ; /* newly create string */ + const size_t n_byte_alloc_min = p_ds->length + 1; - str_p = _spice_dstring_setlength( dsPtr,length) ; - str_p[length] = '\0' ; - return( str_p ) ; - -} /* end spice_dstring_setlength() */ - -/* - *---------------------------------------------------------------------- - * - * spice_dstring_free -- - * - * Frees up any memory allocated for the dynamic string and - * reinitializes the string to an empty state. - * - * Results: - * None. - * - * Side effects: - * The previous contents of the dynamic string are lost, and - * the new value is an empty string. - * - *---------------------------------------------------------------------- - */ - -void spice_dstring_free(SPICE_DSTRINGPTR dsPtr) -{ - if (dsPtr->string != dsPtr->staticSpace) { - txfree(dsPtr->string) ; + /* If the string is in the stack buffer, there is nothing to do */ + if (p_ds->p_stack_buf == p_ds->p_buf) { + return DS_E_OK; } - dsPtr->string = dsPtr->staticSpace ; - dsPtr->length = 0 ; - dsPtr->spaceAvl = SPICE_DSTRING_STATIC_SIZE; - dsPtr->staticSpace[0] = '\0' ; -} /* end spice_dstring_free() */ + /* Else if the string will fit in the stack buffer, copy it there and + * free the allocation. */ + if (p_ds->n_byte_stack_buf >= n_byte_alloc_min) { + (void) memcpy(p_ds->p_stack_buf, p_ds->p_buf, n_byte_alloc_min); + free((void *) p_ds->p_buf); + p_ds->p_buf = p_ds->p_stack_buf; + p_ds->n_byte_alloc = p_ds->n_byte_stack_buf; + return DS_E_OK; + } + + /* Else if the heap buffer is the minimum size, there is nothng to do */ + if (n_byte_alloc_min == p_ds->n_byte_alloc) { + return DS_E_OK; + } + + /* Else realloc the heap buffer */ + { + void *p = realloc(p_ds->p_buf, n_byte_alloc_min); + if (p == NULL) { + return DS_E_NO_MEMORY; + } + p_ds->p_buf = (char *) p; + p_ds->n_byte_alloc = n_byte_alloc_min; + return DS_E_OK; + } +} /* end of function ds_compact */ + + + +#ifdef DSTRING_UNIT_TEST +#if defined (_WIN32) && !defined(CONSOLE) +#include "ngspice/wstdio.h" +#endif +static void ds_print_info(DSTRING *p_ds, FILE *fp, const char *sz_id); +static int ds_test_from_macro(FILE *fp); +static int ds_test_from_stack(FILE *fp); +static int ds_test_from_heap(FILE *fp); +static int ds_test1(DSTRING *p_ds, FILE *fp); + + +int ds_test(FILE *fp) +{ + if (ds_test_from_macro(fp) != 0) { /* create from macro and run test */ + return -1; + } + if (ds_test_from_stack(fp) != 0) { /* create from stack */ + return -1; + } + if (ds_test_from_heap(fp) != 0) { /* create from heap */ + return -1; + } + + return 0; +} /* end of function ds_test */ + + + +/* Run tests from a macro-created dstring */ +static int ds_test_from_macro(FILE *fp) +{ + DS_CREATE(ds, 10); + (void) fprintf(fp, "Macro initialization\n"); + return ds_test1(&ds, fp); +} /* end of function ds_test_from_macro */ + + + +/* Run tests from a manually created stack-backed dstring */ +static int ds_test_from_stack(FILE *fp) +{ + static char p_buf[30] = "Hello World"; + DSTRING ds; + (void) fprintf(fp, "Stack initialization\n"); + (void) ds_init(&ds, p_buf, 11, sizeof p_buf, ds_buf_type_stack); + return ds_test1(&ds, fp); +} /* end of function ds_test_from_stack */ + + + +/* Run tests from a heap-backed dstring */ +static int ds_test_from_heap(FILE *fp) +{ + char *p_buf = (char *) malloc(25); + if (p_buf == (char *) NULL) { + return -1; + } + (void) memcpy(p_buf, "Heap", 4); + DSTRING ds; + (void) ds_init(&ds, p_buf, 4, 25, ds_buf_type_heap); + (void) fprintf(fp, "Heap initialization\n"); + return ds_test1(&ds, fp); +} /* end of function ds_test_from_heap */ + + + +static int ds_test1(DSTRING *p_ds, FILE *fp) +{ + /* Print info on entry */ + ds_print_info(p_ds, fp, "On entry to ds_test1\n"); + + int i; + for (i = 0; i < 10; i++) { + if (ds_cat_str(p_ds, "Abc") != 0) { + (void) fprintf(fp, "Unable to cat string %d.\n", i); + return -1; + } + if (ds_cat_str_case(p_ds, "Abc", ds_case_as_is) != 0) { + (void) fprintf(fp, "Unable to cat string as-is %d.\n", i); + return -1; + } + if (ds_cat_str_case(p_ds, "Abc", ds_case_upper) != 0) { + (void) fprintf(fp, "Unable to cat string upper %d.\n", i); + return -1; + } + if (ds_cat_str_case(p_ds, "Abc", ds_case_lower) != 0) { + (void) fprintf(fp, "Unable to cat string lower %d.\n", i); + return -1; + } + if (ds_cat_char(p_ds, 'z') != 0) { + (void) fprintf(fp, "Unable to cat char %d.\n", i); + return -1; + } + if (ds_cat_char_case(p_ds, 'z', ds_case_as_is) != 0) { + (void) fprintf(fp, "Unable to cat char as-is %d.\n", i); + return -1; + } + if (ds_cat_char_case(p_ds, 'z', ds_case_upper) != 0) { + (void) fprintf(fp, "Unable to cat char upper %d.\n", i); + return -1; + } + if (ds_cat_char_case(p_ds, 'Z', ds_case_lower) != 0) { + (void) fprintf(fp, "Unable to cat char lower %d.\n", i); + return -1; + } + + if (ds_cat_mem(p_ds, "Zyxw", 4) != 0) { + (void) fprintf(fp, "Unable to cat string %d.\n", i); + return -1; + } + if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_as_is) != 0) { + (void) fprintf(fp, "Unable to cat string as-is %d.\n", i); + return -1; + } + if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_upper) != 0) { + (void) fprintf(fp, "Unable to cat string upper %d.\n", i); + return -1; + } + if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_lower) != 0) { + (void) fprintf(fp, "Unable to cat string lower %d.\n", i); + return -1; + } + + if (ds_cat_printf(p_ds, "--- And finally a formatted %s (%d)", + "string", i) != 0) { + (void) fprintf(fp, "Unable to cat formatted string %d.\n", i); + return -1; + } + + /* Print info after cats */ + ds_print_info(p_ds, fp, "After appending strings"); + + /* Truncate the string */ + if (ds_set_length(p_ds, i * (size_t) 10) != 0) { + (void) fprintf(fp, "Unable to set size %d.\n", i); + return -1; + } + + /* Print info after truncation */ + ds_print_info(p_ds, fp, "After setting length"); + + /* Compact the string */ + if (ds_compact(p_ds) != 0) { + (void) fprintf(fp, "Unable to compact %d.\n", i); + return -1; + } + + /* Print info after compaction */ + ds_print_info(p_ds, fp, "After compacting the string"); + } /* end of loop over tests */ + + ds_free(p_ds); /* free buffer if allocated */ + + return 0; +} /* end of funtion ds_test */ + + + +/* Print some info about the DSTRING */ +static void ds_print_info(DSTRING *p_ds, FILE *fp, const char *sz_id) +{ + (void) fprintf(fp, "%s: length = %zu; " + "allocated buffer size = %zu; value = \"%s\"; " + "address of active buffer = %p; " + "address of stack buffer = %p; " + "size of stack buffer = %zu\n", + sz_id, + ds_get_length(p_ds), ds_get_buf_size(p_ds), + ds_get_buf(p_ds), ds_get_buf(p_ds), + p_ds->p_stack_buf, p_ds->n_byte_stack_buf); +} /* end of function ds_print_info */ + + + +#endif /* DSTRING_UNIT_TEST */ + + +