source: trunk/CORE/arb_strbuf.h

Last change on this file was 19427, checked in by westram, 17 months ago
  • running unittests with sanitizer failed (since [19405])
    • fix source-dest-overlapping strcpy.
    • Note: similar fix was [11586].
File size: 7.7 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : arb_strbuf.h                                    //
4//   Purpose   : "unlimited" output buffer                       //
5//                                                               //
6//   Institute of Microbiology (Technical University Munich)     //
7//   http://www.arb-home.de/                                     //
8//                                                               //
9// ============================================================= //
10
11#ifndef ARB_STRBUF_H
12#define ARB_STRBUF_H
13
14#ifndef ARBTOOLS_H
15#include <arbtools.h>
16#endif
17#ifndef ARB_ASSERT_H
18#include <arb_assert.h>
19#endif
20#ifndef ARB_MEM_H
21#include "arb_mem.h"
22#endif
23#ifndef ATTRIBUTES_H
24#include <attributes.h>
25#endif
26#ifndef ARB_STRING_H
27#include "arb_string.h"
28#endif
29#ifndef _GLIBCXX_ALGORITHM
30#include <algorithm>
31#endif
32
33
34// -----------------------
35//      String streams
36
37class GBS_strstruct : virtual Noncopyable {
38    char   *data;
39    size_t  buffer_size;
40    size_t  pos;
41
42    void set_pos(size_t toPos) {
43        pos = toPos;
44        if (data) data[pos] = 0;
45    }
46    void inc_pos(size_t inc) { set_pos(pos+inc); }
47
48    char *release_mem(size_t& size) {
49        /*! take ownership of buffer and retrieve size of buffer (not size of content!!!) */
50        char *result = data;
51        size         = buffer_size;
52        buffer_size  = 0;
53        data         = NULp;
54        return result;
55    }
56    void assign_mem(char *block, size_t blocksize) {
57        free(data);
58
59        arb_assert(block && blocksize>0);
60
61        data      = block;
62        buffer_size = blocksize;
63
64        erase();
65    }
66
67    void alloc_mem(size_t blocksize) {
68        arb_assert(blocksize>0);
69        arb_assert(!data);
70
71        assign_mem(ARB_alloc<char>(blocksize), blocksize);
72    }
73    void realloc_mem(size_t newsize) {
74        if (!data) alloc_mem(newsize);
75        else {
76            // cppcheck-suppress memleakOnRealloc
77            ARB_realloc(data, newsize);
78            buffer_size = newsize;
79
80            arb_assert(pos<newsize);
81        }
82    }
83    void ensure_mem(size_t needed_size) {
84        // ensures insertion of 'needed_size' bytes is ok
85        size_t whole_needed_size = pos+needed_size+1;
86        if (buffer_size<whole_needed_size) {
87            size_t next_size = (whole_needed_size * 3) >> 1;
88            realloc_mem(next_size);
89        }
90    }
91
92public:
93
94    GBS_strstruct() :
95        data(NULp),
96        buffer_size(0),
97        pos(0)
98    {}
99    GBS_strstruct(size_t buffersize) :
100        data(NULp),
101        buffer_size(0),
102        pos(0)
103    {
104        alloc_mem(buffersize);
105    }
106    ~GBS_strstruct() { free(data); }
107
108    size_t get_buffer_size() const {
109        /*! returns the current buffer size. */
110        return buffer_size;
111    }
112    size_t get_position() const {
113        /*! returns the current content size. */
114        return pos;
115    }
116
117    bool filled() const { return get_position()>0; }
118    bool empty() const { return !filled(); }
119
120    const char *get_data() const {
121        /*! get content as const char*.
122         * Result is valid as long as 'this' exists and no reallocation takes place. */
123        return null2empty(data);
124    }
125    char *get_copy() const {
126        /*! return a heap copy of the current content of the buffer. */
127        return ARB_strndup(get_data(), get_position());
128    }
129    char *release() {
130        /*! take ownership of current buffer. might be oversized. */
131        size_t s; return release_mem(s);
132    }
133    char *release_memfriendly() {
134        /*! like release(), but shrink memory block if too oversized. */
135        if (buffer_size > (pos*2) && buffer_size>1000) {
136            realloc_mem(pos+1);
137        }
138        return release();
139    }
140
141    void erase() {
142        /*! erase buffer */
143        set_pos(0);
144    }
145    void cut_tail(size_t byte_count) {
146        /*! Removes 'byte_count' characters from the tail of the content */
147        set_pos(pos<byte_count ? 0 : pos-byte_count);
148    }
149
150    void cut(const size_t at, const size_t byte_count) {
151        if (byte_count>0) {
152            const size_t keepFrom = at+byte_count;
153
154            if (keepFrom <= get_position()) {
155                const size_t restlen = get_position()-keepFrom;
156                memmove(data+at, data+keepFrom, restlen+1);
157
158                set_pos(get_position()-byte_count);
159            }
160            else if (at < get_position()) {
161                set_pos(at);
162            }
163        }
164    }
165
166    void swap_content(GBS_strstruct& other) {
167        std::swap(data, other.data);
168        std::swap(buffer_size, other.buffer_size);
169        std::swap(pos, other.pos);
170    }
171
172    // --------------------
173
174    void put(char c) {
175        /*! append single character to content */
176        ensure_mem(1);
177        data[pos] = c;
178        inc_pos(1);
179    }
180    void nput(char c, size_t count) {
181        /*! append a character repeatedly to content */
182        ensure_mem(count);
183        if (count) {
184            memset(data+pos, c, count);
185            inc_pos(count);
186        }
187    }
188
189    void ncat(const char *from, size_t count) {
190        /*! append 'count' bytes of 'from' to content.
191         * (caution : copies zero byte and mem behind if used with wrong len!)
192         */
193        if (count) {
194            ensure_mem(count);
195            memcpy(data+pos, from, count);
196            inc_pos(count);
197        }
198    }
199    void cat(const char *from) {
200        /*! append whole string to content */
201        ncat(from, strlen(from));
202    }
203
204    int ncatPadded(const char *from, size_t count, size_t paddedWidth) {
205        /*! like catPadded(), when strlen is known or when only a prefix of 'from' shall be concatenated. */
206        ensure_mem(std::max(count, paddedWidth));
207        ncat(from, count);
208        int toPad = long(paddedWidth)-long(count);
209        if (toPad>0) nput(' ', toPad);
210        return toPad;
211    }
212    int catPadded(const char *from, size_t paddedWidth) {
213        /*! concatenate 'from' (will not be truncated if paddedWidth is smaller than 'strlen(from)').
214         * Append spaces to pad to 'paddedWidth'.
215         * Return number of spaces appended or a negative number ('strlen(from)-paddedWidth').
216         */
217        return ncatPadded(from, strlen(from), paddedWidth);
218    }
219
220    void npaste_at(size_t at, const char *from, size_t count) {
221        /*! copy 'count' bytes from 'from' over existing content starting at offset 'at'.
222         * Not meant to operate over end of content. Will fail assertion in that case.
223         */
224        size_t till = at+count-1;
225        if (get_position() <= till) {
226            arb_assert(0); // attempt to paste over end-of-buffer. this is most-likely by mistake
227            // fallback code:
228            nput(' ', till+1-get_position()); // increase string to allow paste
229        }
230        memcpy(data+at, from, count);
231    }
232    void paste_at(size_t at, const char *from) {
233        /*! like npaste_at(), but always use complete string 'from' */
234        npaste_at(at, from, strlen(from));
235    }
236
237    void vnprintf(size_t maxlen, const char *templat, va_list& parg) __ATTR__VFORMAT_MEMBER(2);
238    void nprintf(size_t maxlen, const char *templat, ...) __ATTR__FORMAT_MEMBER(2);
239
240    void putlong(long l) { nprintf(100, "%li", l); }
241    void putfloat(float f) { nprintf(100, "%f", f); }
242
243    // wrapped cats:
244    void cat_wrapped(const char *in, const char *from) {
245        /*! cat string 'from' wrapped into the 2 characters provided in 'in'. */
246        arb_assert(in[0] && in[1] && !in[2]); // 'in' has to be exactly 2 chars
247        put(in[0]);
248        cat(from);
249        put(in[1]);
250    }
251    void cat_sQuoted(const char *from) { cat_wrapped("''", from); }
252    void cat_dQuoted(const char *from) { cat_wrapped("\"\"", from); }
253};
254
255#else
256#error arb_strbuf.h included twice
257#endif // ARB_STRBUF_H
Note: See TracBrowser for help on using the repository browser.