diff options
Diffstat (limited to 'lib/qstring.h')
-rw-r--r-- | lib/qstring.h | 529 |
1 files changed, 242 insertions, 287 deletions
diff --git a/lib/qstring.h b/lib/qstring.h index 0597eda8..5b1d4932 100644 --- a/lib/qstring.h +++ b/lib/qstring.h @@ -22,23 +22,11 @@ #ifndef _ZEBRA_QSTRING_H #define _ZEBRA_QSTRING_H -#include "zebra.h" - -#include <stddef.h> -#include <stdint.h> - +#include "misc.h" +#include "vargs.h" +#include "zassert.h" #include "memory.h" - -#ifndef Inline -#define Inline static inline -#endif - -/* GCC have printf type attribute check. */ -#ifdef __GNUC__ -#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif /* __GNUC__ */ +#include "elstring.h" /*============================================================================== * These "qstrings" address address the lack of a flexible length string in 'C'. @@ -49,24 +37,32 @@ * * The caller does, however, have to explicitly release the contents of a * qstring when it is done with. + * + * + * */ +struct qstring +{ + elstring_t els ; /* *embedded* */ + + usize size ; /* of the els body */ -typedef struct qstring qstring_t ; + usize cp ; + + usize b_size ; + void* b_body ; +} ; + +typedef struct qstring qstring_t[1] ; typedef struct qstring* qstring ; -struct qstring +/* Setting an qstring object to all zeros is enough to initialise it to + * an empty string -- including the embedded elstring. + */ +CONFIRM(ELSTRING_INIT_ALL_ZEROS) ; +enum { - union - { - void* body ; - const void* const_body ; - char* char_body ; - unsigned char* uchar_body ; - } ; - size_t size ; - - size_t len ; - size_t cp ; + QSTRING_INIT_ALL_ZEROS = true } ; /*------------------------------------------------------------------------------ @@ -75,92 +71,139 @@ struct qstring * NB: if the body has not yet been allocated, these functions will return * NULL or NULL + the offset. */ -Inline char* /* pointer to body of qstring */ -qs_chars(qstring qs) +Inline elstring +qs_els_nn(qstring qs) { - return (char*)qs->body ; + return qs->els ; } ; -Inline unsigned char* /* pointer to body of qstring */ -qs_bytes(qstring qs) +Inline elstring +qs_els(qstring qs) { - return (unsigned char*)qs->body ; + return qs->els ; } ; -Inline char* /* pointer to given offset in qstring */ -qs_chars_at(qstring qs, size_t off) +Inline void* +qs_body_nn(qstring qs) /* pointer to body of qstring (not NULL) */ { - return qs_chars(qs) + off ; + return els_body_nn(qs->els) ; } ; -Inline unsigned char* /* pointer to given offset in qstring */ -qs_bytes_at(qstring qs, size_t off) +Inline void* /* pointer to body of qstring */ +qs_body(qstring qs) { - return qs_bytes(qs) + off ; + return (qs != NULL) ? qs_body_nn(qs) : NULL ; } ; -Inline char* /* pointer to 'cp' offset in qstring */ -qs_cp_char(qstring qs) +Inline void /* set pointer to body of qstring (not NULL) */ +qs_set_body_nn(qstring qs, const void* body) +{ + els_set_body_nn(qs->els, body) ; /* sets term = fase */ +} ; + +Inline ulen /* length of qstring (not NULL) */ +qs_len_nn(qstring qs) +{ + return els_len_nn(qs->els) ; +} ; + +Inline ulen /* length of qstring */ +qs_len(qstring qs) +{ + return (qs != NULL) ? qs_len_nn(qs) : 0 ; +} ; + +Inline void /* set length of qstring (not NULL) */ +qs_do_set_len_nn(qstring qs, ulen len) +{ + els_set_len_nn(qs->els, len) ; /* sets term = false */ +} ; + +Inline ulen /* cp of qstring (not NULL) */ +qs_cp_nn(qstring qs) +{ + return qs->cp ; +} ; + +Inline ulen /* cp of qstring */ +qs_cp(qstring qs) +{ + return (qs != NULL) ? qs_cp_nn(qs) : 0 ; +} ; + +Inline void /* set cp of qstring (not NULL) */ +qs_do_set_cp_nn(qstring qs, ulen cp) +{ + qs->cp = cp ; +} ; + +Inline char* /* pointer to given offset in qstring */ +qs_char_at_nn(qstring qs, usize off) +{ + char* p ; + p = qs_body_nn(qs) ; + return (p != NULL) ? p + off : NULL ; +} ; + +Inline char* /* pointer to given offset in qstring */ +qs_char_at(qstring qs, usize off) { - return qs_chars_at(qs, qs->cp) ; + return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ; } ; -Inline unsigned char* /* pointer to 'cp' offset in qstring */ -qs_cp_byte(qstring qs) +Inline char* /* pointer to 'cp' offset in qstring */ +qs_cp_char(qstring qs) { - return qs_bytes_at(qs, qs->cp) ; + return (qs != NULL) ? qs_char_at_nn(qs, qs_cp_nn(qs)) : NULL ; } ; -Inline char* /* pointer to 'len' offset in qstring */ +Inline char* /* pointer to 'len' offset in qstring */ qs_ep_char(qstring qs) { - return qs_chars_at(qs, qs->len) ; + return (qs != NULL) ? qs_char_at_nn(qs, qs_len_nn(qs)) : NULL ; +} ; + +Inline bool /* whether qstring is known to be terminated */ +qs_term_nn(qstring qs) +{ + return els_term_nn(qs->els) ; } ; -Inline unsigned char* /* pointer to 'len' offset in qstring */ -qs_ep_byte(qstring qs) +Inline void /* set qstring is known to be terminated */ +qs_set_term_nn(qstring qs, bool how) { - return qs_bytes_at(qs, qs->len) ; + return els_set_term_nn(qs->els, how) ; } ; /*============================================================================== * Functions */ -extern qstring qs_init_new(qstring qs, size_t len) ; -extern qstring qs_make_to_length(qstring qs, size_t len) ; -extern void qs_free_body(qstring qs) ; -extern qstring qs_reset(qstring qs, int free_structure) ; +extern qstring qs_new(void) ; +extern qstring qs_init_new(qstring qs, usize len) ; +extern qstring qs_reset(qstring qs, free_keep_b free_structure) ; -#define qs_reset_keep(qs) qs_reset(qs, 0) -#define qs_reset_free(qs) qs_reset(qs, 1) - -Inline qstring qs_new(void) ; -Inline qstring qs_dummy(qstring qs, const char* src, int pos) ; - -extern qstring qs_printf(qstring qs, const char* format, ...) - PRINTF_ATTRIBUTE(2, 3) ; -extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ; +Private void qs_make_to_size(qstring qs, usize len, free_keep_b free) ; extern qstring qs_set(qstring qs, const char* src) ; -extern qstring qs_set_n(qstring qs, const char* src, size_t n) ; +extern qstring qs_set_n(qstring qs, const char* src, usize n) ; extern qstring qs_append(qstring qs, const char* src) ; -extern qstring qs_append_n(qstring qs, const char* src, size_t n) ; +extern qstring qs_append_n(qstring qs, const char* src, usize n) ; -Inline qstring qs_need(qstring qs, size_t len) ; -Inline qstring qs_set_len(qstring qs, size_t len) ; -extern qstring qs_add_len(qstring qs, size_t n, char** p_ep) ; -Inline void qs_clear(qstring qs) ; -Inline size_t qs_len(qstring qs) ; -Inline size_t qs_size(qstring qs) ; -Inline void* qs_term(qstring qs) ; +extern qstring qs_copy(qstring dst, qstring src) ; -Inline size_t qs_insert(qstring qs, const void* src, size_t n) ; -Inline void qs_replace(qstring qs, const void* src, size_t n) ; -Inline size_t qs_delete(qstring qs, size_t n) ; +extern qstring qs_set_alias(qstring qs, const char* src) ; +extern qstring qs_set_alias_n(qstring qs, const char* src, usize len) ; + +extern qstring qs_printf(qstring qs, const char* format, ...) + PRINTF_ATTRIBUTE(2, 3) ; +extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ; + +extern usize qs_insert(qstring qs, const void* src, usize n) ; +extern void qs_replace(qstring qs, const void* src, usize n) ; +extern usize qs_delete(qstring qs, usize n) ; -extern qstring qs_copy(qstring dst, qstring src) ; extern int qs_cmp_sig(qstring a, qstring b) ; /*============================================================================== @@ -168,301 +211,213 @@ extern int qs_cmp_sig(qstring a, qstring b) ; */ /*------------------------------------------------------------------------------ - * Make a brand new, completely empty qstring - */ -Inline qstring -qs_new(void) -{ - /* Zeroising has set: - * - * body = NULL -- no body - * size = 0 -- no body - * - * len = 0 - * cp = 0 - * - * Nothing more to do unless initial size != 0 - */ - return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ; -} ; - -/*------------------------------------------------------------------------------ - * Construct a "dummy" qstring from the given string. + * Clear contents of qstring -- preserves any qstring body, but sets len = 0. * - * Allocates a qstring if required. + * Does nothing if qstring is NULL * - * This sets: body = the src - * len = strlen(src) (0 if src is NULL) - * cp = 0 if 'pos' is zero - * len otherwise - * size = 0 + * Sets 'cp' = 'len' = 0. * - * The zero size means that the qstring handling will not attempt to free - * the body, nor will it write to it... Operations which require the qstring - * to have a size will allocate a new body, and discard this one. + * If is an alias qstring, discard the alias. * - * Returns: the address of the dummy qstring. + * NB: does not create a qstring body if there isn't one. */ -Inline qstring -qs_dummy(qstring qs, const char* src, int pos) +Inline void +qs_clear(qstring qs) { - if (qs == NULL) - qs = qs_new() ; - - qs->const_body = src ; - qs->len = (src != NULL) ? strlen(src) : 0 ; - qs->cp = (pos == 0) ? 0 : qs->len ; - qs->size = 0 ; - - return qs ; -} + if (qs != NULL) + { + qs_do_set_len_nn(qs, 0) ; /* sets term == false */ + if (qs->size == 0) + { + qs_set_body_nn(qs, qs->b_body) ; + qs->size = qs->b_size ; + } ; + qs->cp = 0 ; + } ; +} ; /*------------------------------------------------------------------------------ - * Need space for a string of 'len' characters (plus possible '\0'). + * Need space for a string of 'slen' characters (plus possible '\0'). * - * Allocates the qstring, if required. + * Allocate qstring if required (setting qs->len = qs->cp = 0). * - * Returns: address of qstring + * Returns: address of qstring -- with body that can be written upto and + * including 'slen' + 1. + * + * NB: has no effect on 'len' -- even if 'len' > 'slen'. * - * NB: asking for 0 bytes will cause a body to be allocated, ready for any - * '\0' ! + * NB: has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'. * - * NB: has no effect on 'cp' or 'len'. (Will be zero if new qstring allocated.) + * NB: if this is a aliased qstring, the alias is discarded and term = false. */ Inline qstring -qs_need(qstring qs, size_t len) +qs_need(qstring qs, usize slen) { - if ((qs == NULL) || (len >= qs->size)) - return qs_make_to_length(qs, len) ; + if (qs == NULL) + qs = qs_init_new(NULL, slen) ; /* Make the qstring if required */ + else + if (slen >= qs->size) /* for alias qs->size == 0 ! */ + qs_make_to_size(qs, slen, free_it) ; - assert(qs->body != NULL) ; return qs ; } ; /*------------------------------------------------------------------------------ * Set 'len' -- allocate or extend body as required. * - * Allocates the qstring, if required. + * Allocate qstring if required (setting qs->len = qs->cp = 0). * - * Returns: address of qstring + * Returns: address of qstring -- with body that can be written upto and + * including 'len' + 1. * - * NB: setting len == 0 bytes will cause a body to be allocated, ready for any - * '\0' ! + * Sets 'cp' to the (new) 'len' if 'cp' > 'len'. * - * NB: has no effect on 'cp' -- even if 'cp' > 'len'. - * - * NB: if this is a "dummy" qstring, a copy is made of the original body. + * NB: if this is a aliased qstring, a copy is made of all of the original body, + * even if that is longer than the required 'slen'. (And term = false.) */ Inline qstring -qs_set_len(qstring qs, size_t len) +qs_set_len(qstring qs, usize len) { - qs = qs_need(qs, len) ; - qs->len = len ; + if (qs == NULL) + qs = qs_init_new(NULL, len) ; /* Make the qstring if required */ + else + if (len >= qs->size) /* for alias qs->size == 0 ! */ + qs_make_to_size(qs, len, keep_it) ; + + qs_do_set_len_nn(qs, len) ; + + if (qs->cp > len) + qs->cp = len ; + return qs ; } ; /*------------------------------------------------------------------------------ - * Reset contents of qstring. + * Chop to given length -- will neither allocate nor extend body. * - * Does nothing if qstring is NULL + * Does nothing if qstring is NULL. + * + * Does not change the 'len' if it is <= length to chop to. * - * Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL. + * Sets 'cp' to the (new) 'len' if 'cp' > 'len'. * - * For "dummy" qstring, discards the body. + * NB: if this is a aliased qstring, then it remains an aliased string, but + * shorter and term = false (unless no change made to the length). */ Inline void -qs_clear(qstring qs) +qs_chop(qstring qs, usize clen) { if (qs != NULL) { - qs->len = 0 ; - qs->cp = 0 ; - if (qs->size > 0) - *((char*)qs->body) = '\0' ; - else - qs->body = NULL ; + usize len = qs_len_nn(qs) ; + if (len > clen) + qs_do_set_len_nn(qs, (len = clen)) ; /* sets term = false */ + if (qs->cp > len) + qs->cp = len ; } ; } ; /*------------------------------------------------------------------------------ - * Get length of qstring -- by doing strlen() -- and record it in qs->len. + * Set 'cp' -- allocate or extend body as required. * - * Returns: the string length - * - * NB: if no body has been allocated, length = 0 - */ -Inline size_t -qs_len(qstring qs) -{ - return (qs != NULL) ? (qs->len = (qs->body != NULL) ? strlen(qs_chars(qs)) - : 0) - : 0 ; -} ; - -/*------------------------------------------------------------------------------ - * Get size of qstring body. - * - * NB: if no body has been allocated, size == 0 - * if qstring is NULL, size == 0 - * - * NB: if this is a "dummy" qstring, size == 0. - */ -Inline size_t -qs_size(qstring qs) -{ - return (qs != NULL) ? qs->size : 0 ; -} ; - -/*------------------------------------------------------------------------------ - * Get address of current end of qstring body -- ie byte at 'len'. - * - * NB: allocates body if required. - * - * There will be space for '\0' after 'len', so the address returned - * is within the real body of the string. - * - * NB: if this is a "dummy" qstring, a copy is made of the original body. + * Allocates the qstring, if required. * - * NB: address of qstring may NOT be NULL. - */ -Inline void* -qs_end(qstring qs) -{ - if (qs->len >= qs->size) - qs_make_to_length(qs, qs->len) ; /* allows for trailing '\0' */ - - return (char*)qs->body + qs->len ; -} ; - -/*------------------------------------------------------------------------------ - * Set '\0' at qs->len -- allocate or extend body as required. + * Returns: address of qstring * - * Returns address of body -- NULL if the qstring is NULL + * NB: if there was no body, allocates a body for the string, even if 'cp' == 0. * - * NB: if this is a "dummy" qstring, a copy is made of the original body. + * NB: if new 'cp' > 'len', extends body (or allocates one), and sets 'len' to + * 'cp'. If this is an alias qstring, a copy of the string is made. */ -Inline void* -qs_term(qstring qs) +Inline qstring +qs_set_cp(qstring qs, usize cp) { - size_t len ; - if (qs == NULL) - return NULL ; + qs = qs_new(qs) ; - if ((len = qs->len) >= qs->size) - qs_make_to_length(qs, len) ; + if (qs->size <= cp) - *qs_chars_at(qs, len) = '\0' ; - return qs->body ; + if ((qs == NULL) || (cp >(cp > qs_len_nn(qs))) + qs = qs_set_len(qs, cp) ; + qs->cp = cp ; + return qs ; } ; /*------------------------------------------------------------------------------ - * Insert 'n' bytes at 'cp' -- moves anything cp..len up. - * - * Increases 'len'. but does not affect 'cp'. - * - * Returns: number of bytes beyond 'cp' that were moved before insert. + * If not "term": set '\0' at qs->len -- extending body as required. * - * NB: qstring MUST NOT be NULL + * Does NOT affect qs->cp or qs->len. * - * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce - * one or more undefined bytes. - * - * NB: the string is NOT re-terminated. + * Returns address of body -- NULL if the qstring is NULL * - * NB: if this is a "dummy" qstring, a copy is made of the original body. + * NB: if this is an alias, and it is not terminated, make a copy before adding + * terminating '\0'. */ -Inline size_t -qs_insert(qstring qs, const void* src, size_t n) +Inline void +qs_terminate_nn(qstring qs) { - size_t after ; - char* p ; - - if (qs->len < qs->cp) - qs->len = qs->cp ; - after = qs->len - qs->cp ; + if (!qs_term_nn(qs)) + { + usize len ; - qs_set_len(qs, qs->len + n) ; /* set len and ensure have space */ + len = qs_len_nn(qs) ; - p = qs_cp_char(qs) ; - if (after > 0) - memmove (p + n, p, after) ; + if (len >= qs->size) /* alias has size == 0 */ + qs_make_to_size(qs, len) ; /* make sure can insert '\0' */ - if (n > 0) - memmove(p, src, n) ; + *qs_char_at_nn(qs, len) = '\0' ; - return after ; + qs_set_term_nn(qs, true) ; + } ; } ; /*------------------------------------------------------------------------------ - * Replace 'n' bytes at 'cp' -- extending if required. + * Return pointer to '\0' terminated string value. * - * May increase 'len'. but does not affect 'cp'. + * If qs is NULL or body is NULL returns pointer to constant empty '\0' + * terminated string. * - * NB: qstring MUST NOT be NULL + * If string is terminated, return address of string, which may be an alias + * address. * - * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce - * one or more undefined bytes. + * Otherwise, makes sure that there is a '\0' at the qs->len position, making + * a copy of any aliased string if required, and returns address of result. * - * NB: the string is NOT re-terminated. - * - * NB: if this is a "dummy" qstring, a copy is made of the original body. + * NB: value returned may be the address of the qstring body, or the address of + * an aliased string. In any event, the string should not be changed or + * reset until this pointer has been discarded ! */ -Inline void -qs_replace(qstring qs, const void* src, size_t n) +Inline const char* +qs_string(qstring qs) { - if ((qs->len < qs->cp + n) || (qs->size == 0)) - qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */ + if ((qs == NULL) || (qs_len_nn(qs) == 0)) + return "" ; - if (n > 0) - memmove(qs_cp_char(qs), src, n) ; + qs_terminate_nn(qs) ; + + return qs_body_nn(qs) ; } ; /*------------------------------------------------------------------------------ - * Remove 'n' bytes at 'cp' -- extending if required. - * - * May change 'len'. but does not affect 'cp'. + * Assuming the given address is within the size of the given qstring, + * set qs->len and insert '\0' terminator there. * - * Returns: number of bytes beyond 'cp' that were moved before insert. + * Does NOT affect qs->cp. * - * NB: qstring MUST NOT be NULL + * NB: must NOT be a NULL qs. * - * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce - * one or more undefined bytes. + * NB: must NOT be an aliased qstring. * - * NB: the string is NOT re-terminated. + * NB: must NOT have a NULL body. */ -Inline size_t -qs_delete(qstring qs, size_t n) +Inline void +qs_term_here(qstring qs, char* here) { - size_t after ; - char* p ; + assert((here >= qs->s.char_body) && (here < (qs->s.char_body + qs->size))) ; - /* Watch out for "dummy" */ - if (qs->size == 0) - qs_make_to_length(qs, qs->len) ; - - /* If deleting up to or beyond len, then simply set len == cp */ - if ((qs->cp + n) >= qs->len) - { - qs_set_len(qs, qs->cp) ; /* set len, looks after cp > len */ - return 0 ; /* nothing after */ - } - - /* There is at least one byte after cp (so body must exist) */ - after = qs->len - (qs->cp + n) ; - - if (n > 0) - { - p = qs_cp_char(qs) ; - memmove (p, p + n, after) ; - - qs->len -= n ; - } ; - - return after ; + qs->len = (here - qs->s.char_body) ; + *here = '\0' ; } ; - #endif /* _ZEBRA_QSTRING_H */ |