diff --git a/bstring/bstrlib.c b/bstring/bstrlib.c index e062f83..41e9ec6 100644 --- a/bstring/bstrlib.c +++ b/bstring/bstrlib.c @@ -60,7 +60,7 @@ do { \ if ((L) > 0) { \ memmove((D), (S), (L)); \ } \ -} while (0); +} while (0) /** * Compute the snapped size for a given requested size. @@ -672,23 +672,23 @@ bstrnicmp(const bstring b0, const bstring b1, int n) } int -biseqcaseless(const bstring b0, const bstring b1) +biseqcaselessblk(const bstring b, const void *blk, int len) { - int i, n; - if (bdata (b0) == NULL || b0->slen < 0 || - bdata (b1) == NULL || b1->slen < 0) { + int i; + if (bdata(b) == NULL || b->slen < 0 || + blk == NULL || len < 0) { return BSTR_ERR; } - if (b0->slen != b1->slen) { - return BSTR_OK; + if (b->slen != len) { + return 0; } - if (b0->data == b1->data || b0->slen == 0) { + if (len == 0 || b->data == blk) { return 1; } - for (i = 0, n = b0->slen; i < n; i++) { - if (b0->data[i] != b1->data[i]) { - unsigned char c = (unsigned char)downcase(b0->data[i]); - if (c != (unsigned char)downcase(b1->data[i])) { + for (i = 0; i < len; i++) { + if (b->data[i] != ((const unsigned char *)blk)[i]) { + unsigned char c = (unsigned char)downcase(b->data[i]); + if (c != (unsigned char)downcase(((unsigned char *)blk)[i])) { return 0; } } @@ -696,6 +696,15 @@ biseqcaseless(const bstring b0, const bstring b1) return 1; } +int +biseqcaseless(const bstring b0, const bstring b1) +{ + if (NULL == b1) { + return BSTR_ERR; + } + return biseqcaselessblk(b0, b1->data, b1->slen); +} + int bisstemeqcaselessblk(const bstring b0, const void *blk, int len) { @@ -791,6 +800,22 @@ btrimws(bstring b) return BSTR_OK; } +int +biseqblk(const bstring b, const void *blk, int len) +{ + if (len < 0 || b == NULL || blk == NULL || + b->data == NULL || b->slen < 0) { + return BSTR_ERR; + } + if (b->slen != len) { + return 0; + } + if (len == 0 || b->data == blk) { + return 1; + } + return !memcmp(b->data, blk, len); +} + int biseq(const bstring b0, const bstring b1) { @@ -811,7 +836,6 @@ biseq(const bstring b0, const bstring b1) int bisstemeqblk(const bstring b0, const void *blk, int len) { - int i; if (!bdata(b0) || b0->slen < 0 || !blk || len < 0) { return BSTR_ERR; } @@ -821,7 +845,7 @@ bisstemeqblk(const bstring b0, const void *blk, int len) if (b0->data == (const unsigned char *)blk || len == 0) { return 1; } - for (i = 0; i < len; i ++) { + for (int i = 0; i < len; i ++) { if (b0->data[i] != ((const unsigned char *)blk)[i]) { return BSTR_OK; } @@ -1474,61 +1498,68 @@ bsetstr(bstring b0, int pos, const bstring b1, unsigned char fill) } int -binsert(bstring b1, int pos, const bstring b2, unsigned char fill) +binsertblk(bstring b, int pos, const void *blk, int len, + unsigned char fill) { int d, l; - ptrdiff_t pd; - bstring aux = (bstring) b2; - if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || - b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) { + unsigned char *aux = (unsigned char *)blk; + if (b == NULL || blk == NULL || pos < 0 || len < 0 || + b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen) { return BSTR_ERR; } - /* Aliasing case */ - if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && - pd < (ptrdiff_t) b1->mlen) { - if (NULL == (aux = bstrcpy (b2))) { - return BSTR_ERR; - } - } /* Compute the two possible end pointers */ - d = b1->slen + aux->slen; - l = pos + aux->slen; + d = b->slen + len; + l = pos + len; if ((d|l) < 0) { - if (aux != b2) { - bdestroy(aux); + return BSTR_ERR; /* Integer wrap around. */ + } + /* Aliasing case */ + if (((size_t)((const unsigned char *)blk + len)) >= ((size_t)b->data) && + ((size_t)blk) < ((size_t)(b->data + b->mlen))) { + if (NULL == (aux = (unsigned char *)malloc(len))) { + return BSTR_ERR; } - return BSTR_ERR; + memcpy(aux, blk, len); } if (l > d) { /* Inserting past the end of the string */ - if (balloc(b1, l + 1) != BSTR_OK) { - if (aux != b2) { - bdestroy(aux); + if (balloc(b, l + 1) != BSTR_OK) { + if (aux != (const unsigned char *)blk) { + free(aux); } return BSTR_ERR; } - memset(b1->data + b1->slen, (int)fill, - (size_t)(pos - b1->slen)); - b1->slen = l; + memset(b->data + b->slen, (int)fill, + (size_t)(pos - b->slen)); + b->slen = l; } else { /* Inserting in the middle of the string */ - if (balloc(b1, d + 1) != BSTR_OK) { - if (aux != b2) { - bdestroy(aux); + if (balloc(b, d + 1) != BSTR_OK) { + if (aux != (const unsigned char *)blk) { + free(aux); } return BSTR_ERR; } - bBlockCopy(b1->data + l, b1->data + pos, d - l); - b1->slen = d; + bBlockCopy(b->data + l, b->data + pos, d - l); + b->slen = d; } - bBlockCopy(b1->data + pos, aux->data, aux->slen); - b1->data[b1->slen] = (unsigned char)'\0'; - if (aux != b2) { - bdestroy(aux); + bBlockCopy(b->data + pos, aux, len); + b->data[b->slen] = (unsigned char)'\0'; + if (aux != (const unsigned char *)blk) { + free(aux); } return BSTR_OK; } +int +binsert(bstring b1, int pos, const bstring b2, unsigned char fill) +{ + if (NULL == b2 || (b2->mlen > 0 && b2->slen > b2->mlen)) { + return BSTR_ERR; + } + return binsertblk(b1, pos, b2->data, b2->slen, fill); +} + int breplace(bstring b1, int pos, int len, const bstring b2, unsigned char fill) { @@ -2305,16 +2336,23 @@ bspeek(bstring r, const struct bStream *s) } bstring -bjoin(const struct bstrList *bl, const bstring sep) +bjoinblk(const struct bstrList *bl, const void *blk, int len) { bstring b; + unsigned char *p; int i, c, v; if (bl == NULL || bl->qty < 0) { return NULL; } - if (sep == NULL || sep->slen < 0 || sep->data == NULL) { + if (len < 0) { + return NULL; + } + if (len > 0 && blk == NULL) { return NULL; } + if (bl->qty < 1) { + return bfromcstr(""); + } for (i = 0, c = 1; i < bl->qty; i++) { v = bl->entry[i]->slen; if (v < 0) { @@ -2325,33 +2363,64 @@ bjoin(const struct bstrList *bl, const bstring sep) return NULL; /* Wrap around ?? */ } } - if (sep != NULL) { - c += (bl->qty - 1) * sep->slen; - } b = (bstring)malloc(sizeof(struct tagbstring)); - if (NULL == b) { - return NULL; /* Out of memory */ - } - b->data = (unsigned char *)malloc(c); - if (b->data == NULL) { - free (b); - return NULL; - } - b->mlen = c; - b->slen = c-1; - for (i = 0, c = 0; i < bl->qty; i++) { - if (i > 0 && sep != NULL) { - memcpy(b->data + c, sep->data, sep->slen); - c += sep->slen; + if (len == 0) { + p = b->data = (unsigned char *)malloc(c); + if (p == NULL) { + free(b); + return NULL; + } + for (i = 0; i < bl->qty; i++) { + v = bl->entry[i]->slen; + memcpy(p, bl->entry[i]->data, v); + p += v; + } + } else { + v = (bl->qty - 1) * len; + if ((bl->qty > 512 || len > 127) && + v / len != bl->qty - 1) { + return NULL; /* Wrap around ?? */ } - v = bl->entry[i]->slen; - memcpy(b->data + c, bl->entry[i]->data, v); c += v; + if (c < v) { + return NULL; /* Wrap around ?? */ + } + p = b->data = (unsigned char *)malloc(c); + if (p == NULL) { + free(b); + return NULL; + } + v = bl->entry[0]->slen; + memcpy(p, bl->entry[0]->data, v); + p += v; + for (i = 1; i < bl->qty; i++) { + memcpy(p, blk, len); + p += len; + v = bl->entry[i]->slen; + if (v) { + memcpy(p, bl->entry[i]->data, v); + p += v; + } + } } - b->data[c] = (unsigned char)'\0'; + b->mlen = c; + b->slen = c - 1; + b->data[c - 1] = (unsigned char)'\0'; return b; } +bstring +bjoin(const struct bstrList *bl, const bstring sep) +{ + if (sep == NULL) { + return bjoinblk(bl, NULL, 0); + } + if (sep->slen < 0 || (sep->slen > 0 && sep->data == NULL)) { + return NULL; + } + return bjoinblk(bl, sep->data, sep->slen); +} + #define BSSSC_BUFF_LEN (256) int diff --git a/bstring/bstrlib.h b/bstring/bstrlib.h index 0e61bc5..f82f131 100644 --- a/bstring/bstrlib.h +++ b/bstring/bstrlib.h @@ -358,6 +358,18 @@ bcatblk(bstring b, const void *s, int len); BSTR_PUBLIC int binsert(bstring s1, int pos, const bstring s2, unsigned char fill); +/** + * Inserts the block of characters at blk with length len into b at position + * pos. + * + * If the position pos is past the end of b, then the character "fill" is + * appended as necessary to make up the gap between the end of b and pos. + * Unlike bsetstr, binsertblk does not allow blk to be NULL. + */ +BSTR_PUBLIC int +binsertblk(bstring b, int pos, const void *blk, int len, + unsigned char fill); + /** * Inserts the character fill repeatedly into s1 at position pos for a * length len. @@ -450,6 +462,18 @@ bstrnicmp(const bstring b0, const bstring b1, int n); BSTR_PUBLIC int biseqcaseless(const bstring b0, const bstring b1); +/** + * Compare content of b and the array of bytes in blk for length len for + * equality without differentiating between character case. + * + * If the content differs other than in case, 0 is returned, if, ignoring + * case, the content is the same, 1 is returned, if there is an error, -1 is + * returned. If the length of the strings are different, this function is + * O(1). '\0' characters are not treated in any special way. + */ +BSTR_PUBLIC int +biseqcaselessblk(const bstring b, const void *blk, int len); + /** * Compare beginning of bstring b0 with a block of memory of length len * without differentiating between case for equality. @@ -475,6 +499,17 @@ bisstemeqcaselessblk(const bstring b0, const void *blk, int len); BSTR_PUBLIC int biseq(const bstring b0, const bstring b1); +/** + * Compare the bstring b with the character block blk of length len. + * + * If the content differs, 0 is returned, if the content is the same, 1 is + * returned, if there is an error, -1 is returned. If the length of the + * strings are different, this function is O(1). '\0' characters are not + * treated in any special way. + */ +BSTR_PUBLIC int +biseqblk(const bstring b, const void *blk, int len); + /** * Compare beginning of bstring b0 with a block of memory of length len for * equality. @@ -834,6 +869,16 @@ bsplitstr(const bstring str, const bstring splitStr); BSTR_PUBLIC bstring bjoin(const struct bstrList *bl, const bstring sep); +/** + * Join the entries of a bstrList into one bstring by sequentially + * concatenating them with the content from blk for length len in between. + * + * If there is an error NULL is returned, otherwise a bstring with the + * correct result is returned. + */ +BSTR_PUBLIC bstring +bjoinblk(const struct bstrList *bl, const void *blk, int len); + /** * Iterate the set of disjoint sequential substrings over str starting at * position pos divided by the character splitChar. @@ -1538,6 +1583,18 @@ bseof(const struct bStream *s); #define bsStaticBlkParms(q) \ ((void *)("" q "")), ((int)sizeof(q) -1) +/* Static convenience macros for blk functions with string literals */ + +#define bcatStatic(b, s) ((bcatblk)((b), bsStaticBlkParms(s))) +#define bfromStatic(s) ((blk2bstr)(bsStaticBlkParms(s))) +#define bassignStatic(b, s) ((bassignblk)((b), bsStaticBlkParms(s))) +#define binsertStatic(b, p, s, f) ((binsertblk)((b), (p), bsStaticBlkParms(s), (f))) +#define bjoinStatic(b, s) ((bjoinblk)((b), bsStaticBlkParms(s))) +#define biseqStatic(b, s) ((biseqblk)((b), bsStaticBlkParms(s))) +#define bisstemeqStatic(b, s) ((bisstemeqblk)((b), bsStaticBlkParms(s))) +#define biseqcaselessStatic(b, s) ((biseqcaselessblk)((b), bsStaticBlkParms(s))) +#define bisstemeqcaselessStatic(b, s) ((bisstemeqcaselessblk)((b), bsStaticBlkParms(s))) + /* Reference building macros */ /** @@ -1631,7 +1688,7 @@ do { \ (t).slen = 0; \ } \ (t).mlen = -__LINE__; \ -} while (0); +} while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it @@ -1661,7 +1718,7 @@ do { \ (t).data = bstrtmp_s + bstrtmp_idx; \ (t).slen = bstrtmp_len - bstrtmp_idx; \ (t).mlen = -__LINE__; \ -} while (0); +} while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it @@ -1691,7 +1748,7 @@ do { \ (t).data = bstrtmp_s; \ (t).slen = bstrtmp_len + 1; \ (t).mlen = -__LINE__; \ -} while (0); +} while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it @@ -1726,7 +1783,7 @@ do { \ (t).data = bstrtmp_s + bstrtmp_idx; \ (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \ (t).mlen = -__LINE__; \ -} while (0); +} while (0) /* Write protection macros */ @@ -1743,7 +1800,7 @@ do { \ if ((t).mlen >= 0) { \ (t).mlen = -1; \ } \ -} while (0); +} while (0) /** * Allow bstring to be written to via the bstrlib API. @@ -1762,7 +1819,7 @@ do { \ if ((t).mlen == -1) { \ (t).mlen = (t).slen + ((t).slen == 0); \ } \ -} while (0); +} while (0) /** * Returns 1 if the bstring is write protected, otherwise 0 is returned. diff --git a/tests/bstest.c b/tests/bstest.c index 7a93cd1..87bdaca 100644 --- a/tests/bstest.c +++ b/tests/bstest.c @@ -3075,6 +3075,222 @@ START_TEST(core_046) } END_TEST +/* biseqblk */ + +static void +test47_0(const bstring b, const unsigned char *blk, int len, int res) +{ + int ret = biseqblk(b, blk, len); + ck_assert_int_eq(ret, res); +} + +START_TEST(core_047) +{ + /* tests with NULL */ + test47_0(NULL, NULL, 0, BSTR_ERR); + test47_0(&emptyBstring, NULL, 0, BSTR_ERR); + test47_0(NULL, emptyBstring.data, 0, BSTR_ERR); + test47_0(&shortBstring, NULL, shortBstring.slen, BSTR_ERR); + test47_0(NULL, shortBstring.data, 0, BSTR_ERR); + test47_0(&badBstring1, badBstring1.data, badBstring1.slen, BSTR_ERR); + test47_0(&badBstring2, badBstring2.data, badBstring2.slen, BSTR_ERR); + test47_0(&shortBstring, badBstring2.data, badBstring2.slen, BSTR_ERR); + test47_0(&badBstring2, shortBstring.data, shortBstring.slen, BSTR_ERR); + /* normal operation tests */ + test47_0(&emptyBstring, emptyBstring.data, emptyBstring.slen, 1); + test47_0(&shortBstring, emptyBstring.data, emptyBstring.slen, 0); + test47_0(&emptyBstring, shortBstring.data, shortBstring.slen, 0); + test47_0(&shortBstring, shortBstring.data, shortBstring.slen, 1); + { + bstring b = bstrcpy(&shortBstring); + ck_assert(b != NULL); + b->data[1]++; + test47_0(b, shortBstring.data, shortBstring.slen, 0); + bdestroy(b); + } + test47_0(&shortBstring, longBstring.data, longBstring.slen, 0); + test47_0(&longBstring, shortBstring.data, shortBstring.slen, 0); +} +END_TEST + +/* biseqcaselessblk */ + +static void +test48_0(const bstring b, const unsigned char *blk, int len, int res) +{ + int ret = biseqcaselessblk(b, blk, len); + ck_assert_int_eq(ret, res); +} + +START_TEST(core_048) +{ + struct tagbstring t0 = bsStatic("bOgUs"); + struct tagbstring t1 = bsStatic("bOgUR"); + struct tagbstring t2 = bsStatic("bOgUt"); + /* tests with NULL */ + test48_0(NULL, NULL, 0, BSTR_ERR); + test48_0(&emptyBstring, NULL, 0, BSTR_ERR); + test48_0(NULL, emptyBstring.data, 0, BSTR_ERR); + test48_0(&emptyBstring, badBstring1.data, emptyBstring.slen, BSTR_ERR); + test48_0(&badBstring1, emptyBstring.data, badBstring1.slen, BSTR_ERR); + test48_0(&shortBstring, badBstring2.data, badBstring2.slen, BSTR_ERR); + test48_0(&badBstring2, shortBstring.data, badBstring2.slen, BSTR_ERR); + /* normal operation tests */ + test48_0(&emptyBstring, emptyBstring.data, emptyBstring.slen, 1); + test48_0(&shortBstring, t0.data, t0.slen, 1); + test48_0(&shortBstring, t1.data, t1.slen, 0); + test48_0(&shortBstring, t2.data, t2.slen, 0); +} +END_TEST + +/* binsertblk */ + +static void +test49_0(bstring b0, int pos, const void *blk, int len, + unsigned char fill, const char *res, int reslen) +{ + bstring b2; + int ret = 0; + if (b0 && b0->data && b0->slen >= 0 && + blk && len >= 0) { + b2 = bstrcpy(b0); + if (b2 == NULL) { + ck_abort(); + return; + } + bwriteprotect(*b2); + ret = binsertblk(b2, pos, blk, len, fill); + ck_assert_int_ne(ret, 0); + ret = biseq(b0, b2); + ck_assert_int_eq(ret, 1); + bwriteallow(*b2); + ret = binsertblk(b2, pos, blk, len, fill); + if (res == NULL) { + ck_assert_int_eq(ret, BSTR_ERR); + } else { + ck_assert_int_eq(ret, BSTR_OK); + ck_assert_int_ge(reslen, 0); + ck_assert_int_eq(b2->slen, reslen); + ret = memcmp(b2->data, res, reslen); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(b2->data[b2->slen], '\0'); + } + ret = bdestroy(b2); + ck_assert_int_eq(ret, BSTR_OK); + } else { + ret = binsertblk(b0, pos, blk, len, fill); + ck_assert_int_eq(ret, BSTR_ERR); + } +} + +START_TEST(core_049) +{ + /* tests with NULL */ + test49_0(NULL, 0, NULL, 0, '?', NULL, -1); + test49_0(NULL, 0, "", 0, '?', NULL, -1); + test49_0(&badBstring1, 0, NULL, 0, '?', NULL, -1); + test49_0(&badBstring1, 0, badBstring1.data, 4, '?', NULL, -1); + test49_0(&badBstring2, 0, NULL, 0, '?', NULL, -1); + test49_0(&badBstring2, 0, badBstring2.data, 5, '?', NULL, -1); + /* normal operation tests */ + test49_0(&emptyBstring, 0, "", 0, '?', "", (int)(sizeof("") - 1)); + test49_0(&emptyBstring, 5, "", 0, '?', "?????", (int)(sizeof("?????") - 1)); + test49_0(&emptyBstring, 5, SHORT_STRING, 5, '?', "?????bogus", + (int)(sizeof("?????bogus") - 1)); + test49_0(&shortBstring, 0, "", 0, '?', "bogus", (int)(sizeof("bogus") - 1)); + test49_0(&emptyBstring, 0, SHORT_STRING, 5, '?', "bogus", + (int)(sizeof("bogus") - 1)); + test49_0(&shortBstring, 0, SHORT_STRING, 5, '?', "bogusbogus", + (int)(sizeof("bogusbogus") - 1)); + test49_0(&shortBstring, -1, SHORT_STRING, 5, '?', NULL, -1); + test49_0(&shortBstring, 2, SHORT_STRING, 5, '?', "bobogusgus", + (int)(sizeof("bobogusgus") - 1)); + test49_0(&shortBstring, 6, SHORT_STRING, 5, '?', "bogus?bogus", + (int)(sizeof("bogus?bogus") - 1)); + /* alias test: insert substring of self */ + { + bstring b0 = bfromcstr("aaaaabbbbb"); + ck_assert(b0 != NULL); + b0->slen = 6; + int ret = binsertblk(b0, 2, b0->data + 4, 4, '?'); + ck_assert_int_eq(ret, BSTR_OK); + ck_assert_int_eq(biseqcstr(b0, "aaabbbaaab"), 1); + bdestroy(b0); + } +} +END_TEST + +/* bjoinblk */ + +START_TEST(core_050) +{ + struct bstrList *l; + bstring b; + bstring c; + int ret; + /* error cases */ + ck_assert(bjoinblk(NULL, ",", 1) == NULL); + ck_assert(bjoinblk(NULL, NULL, 0) == NULL); + /* empty list */ + l = bstrListCreate(); + ck_assert(l != NULL); + c = bjoinblk(l, ",", 1); + ck_assert(c != NULL); + ck_assert_int_eq(c->slen, 0); + bdestroy(c); + bstrListDestroy(l); + /* split and rejoin with blk separator */ + { + b = bfromcstr("one,two,three"); + ck_assert(b != NULL); + l = bsplit(b, ','); + ck_assert(l != NULL); + ck_assert_int_eq(l->qty, 3); + c = bjoin(l, NULL); + ck_assert(c != NULL); + ck_assert_int_eq(biseqcstr(c, "onetwothree"), 1); + ck_assert_int_eq(c->data[c->slen], '\0'); + bdestroy(c); + c = bjoinblk(l, ",", 1); + ck_assert(c != NULL); + ret = biseq(c, b); + ck_assert_int_eq(ret, 1); + ck_assert_int_eq(c->data[c->slen], '\0'); + bdestroy(c); + /* rejoin with zero-length separator */ + c = bjoinblk(l, "", 0); + ck_assert(c != NULL); + ck_assert_int_eq(biseqcstr(c, "onetwothree"), 1); + ck_assert_int_eq(c->data[c->slen], '\0'); + bdestroy(c); + /* rejoin with multi-char separator */ + c = bjoinblk(l, "-->", 3); + ck_assert(c != NULL); + ck_assert_int_eq(biseqcstr(c, "one-->two-->three"), 1); + ck_assert_int_eq(c->data[c->slen], '\0'); + bdestroy(c); + bstrListDestroy(l); + bdestroy(b); + } + /* single entry list */ + { + l = bstrListCreate(); + ck_assert(l != NULL); + bstrListAlloc(l, 2); + l->entry[0] = bfromcstr("only"); + l->qty = 1; + c = bjoinblk(l, ",", 1); + ck_assert(c != NULL); + ck_assert_int_eq(biseqcstr(c, "only"), 1); + ck_assert_int_eq(c->data[c->slen], '\0'); + bdestroy(c); + bdestroy(l->entry[0]); + l->qty = 0; + bstrListDestroy(l); + } +} +END_TEST + int main(void) { @@ -3129,6 +3345,10 @@ main(void) tcase_add_test(core, core_044); tcase_add_test(core, core_045); tcase_add_test(core, core_046); + tcase_add_test(core, core_047); + tcase_add_test(core, core_048); + tcase_add_test(core, core_049); + tcase_add_test(core, core_050); suite_add_tcase(suite, core); /* Run tests */ SRunner *runner = srunner_create(suite);