Coverage Report

Created: 2025-01-28 06:45

/src/tarantool/third_party/lua-cjson/strbuf.c
Line
Count
Source (jump to first uncovered line)
1
/* strbuf - String buffer routines
2
 *
3
 * Copyright (c) 2010-2012  Mark Pulford <mark@kyne.com.au>
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining
6
 * a copy of this software and associated documentation files (the
7
 * "Software"), to deal in the Software without restriction, including
8
 * without limitation the rights to use, copy, modify, merge, publish,
9
 * distribute, sublicense, and/or sell copies of the Software, and to
10
 * permit persons to whom the Software is furnished to do so, subject to
11
 * the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 */
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <stdarg.h>
28
#include <string.h>
29
30
#include "strbuf.h"
31
#include "small/ibuf.h"
32
33
static void die(const char *fmt, ...)
34
0
{
35
0
    va_list arg;
36
37
0
    va_start(arg, fmt);
38
0
    vfprintf(stderr, fmt, arg);
39
0
    va_end(arg);
40
0
    fprintf(stderr, "\n");
41
42
0
    exit(-1);
43
0
}
44
45
void strbuf_create(strbuf_t *s, int len, struct ibuf *ibuf)
46
0
{
47
0
    int size;
48
49
0
    if (len <= 0)
50
0
        size = STRBUF_DEFAULT_SIZE;
51
0
    else
52
0
        size = len + 1;         /* \0 terminator */
53
54
0
    s->buf = ibuf_reserve(ibuf, size);
55
0
    s->size = size;
56
0
    s->length = 0;
57
0
    s->increment = STRBUF_DEFAULT_INCREMENT;
58
0
    s->reallocs = 0;
59
0
    s->debug = 0;
60
0
    s->ibuf = ibuf;
61
0
    if (!s->buf)
62
0
        die("Out of memory");
63
64
0
    strbuf_ensure_null(s);
65
0
}
66
67
void strbuf_set_increment(strbuf_t *s, int increment)
68
0
{
69
    /* Increment > 0:  Linear buffer growth rate
70
     * Increment < -1: Exponential buffer growth rate */
71
0
    if (increment == 0 || increment == -1)
72
0
        die("BUG: Invalid string increment");
73
74
0
    s->increment = increment;
75
0
}
76
77
static inline void debug_stats(strbuf_t *s)
78
0
{
79
0
    if (s->debug) {
80
0
        fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
81
0
                (long)s, s->reallocs, s->length, s->size);
82
0
    }
83
0
}
84
85
void strbuf_destroy(strbuf_t *s)
86
0
{
87
0
    debug_stats(s);
88
0
}
89
90
static int calculate_new_size(strbuf_t *s, int len)
91
0
{
92
0
    int reqsize, newsize;
93
94
0
    if (len <= 0)
95
0
        die("BUG: Invalid strbuf length requested");
96
97
    /* Ensure there is room for optional NULL termination */
98
0
    reqsize = len + 1;
99
100
    /* If the user has requested to shrink the buffer, do it exactly */
101
0
    if (s->size > reqsize)
102
0
        return reqsize;
103
104
0
    newsize = s->size;
105
0
    if (s->increment < 0) {
106
        /* Exponential sizing */
107
0
        while (newsize < reqsize)
108
0
            newsize *= -s->increment;
109
0
    } else {
110
        /* Linear sizing */
111
0
        newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
112
0
    }
113
114
0
    return newsize;
115
0
}
116
117
118
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
119
 * optional termination). */
120
void strbuf_resize(strbuf_t *s, int len)
121
0
{
122
0
    int newsize;
123
124
0
    newsize = calculate_new_size(s, len);
125
126
0
    if (s->debug > 1) {
127
0
        fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
128
0
                (long)s, s->size, newsize);
129
0
    }
130
131
0
    s->size = newsize;
132
133
0
    struct ibuf *ibuf = s->ibuf;
134
    /*
135
     * Propagate the write position to enable memcpy() when realloc happens
136
     * inside of the ibuf.
137
     */
138
0
    ibuf->wpos += s->length;
139
0
    s->buf = ibuf_reserve(ibuf, newsize - s->length) - s->length;
140
    /*
141
     * But then it is reverted because memcpy() of the old data is done, and the
142
     * write position + capacity are managed by strbuf itself.
143
     */
144
0
    ibuf->wpos = s->buf;
145
146
0
    if (!s->buf)
147
0
        die("Out of memory");
148
0
    s->reallocs++;
149
0
}
150
151
void strbuf_append_string(strbuf_t *s, const char *str)
152
0
{
153
0
    int space, i;
154
155
0
    space = strbuf_empty_length(s);
156
157
0
    for (i = 0; str[i]; i++) {
158
0
        if (space < 1) {
159
0
            strbuf_resize(s, s->length + 1);
160
0
            space = strbuf_empty_length(s);
161
0
        }
162
163
0
        s->buf[s->length] = str[i];
164
0
        s->length++;
165
0
        space--;
166
0
    }
167
0
}
168
169
/* strbuf_append_fmt() should only be used when an upper bound
170
 * is known for the output string. */
171
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
172
0
{
173
0
    va_list arg;
174
0
    int fmt_len;
175
176
0
    strbuf_ensure_empty_length(s, len);
177
178
0
    va_start(arg, fmt);
179
0
    fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
180
0
    va_end(arg);
181
182
0
    if (fmt_len < 0)
183
0
        die("BUG: Unable to convert number");  /* This should never happen.. */
184
185
0
    s->length += fmt_len;
186
0
}
187
188
/* strbuf_append_fmt_retry() can be used when the there is no known
189
 * upper bound for the output string. */
190
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
191
0
{
192
0
    va_list arg;
193
0
    int fmt_len, try;
194
0
    int empty_len;
195
196
    /* If the first attempt to append fails, resize the buffer appropriately
197
     * and try again */
198
0
    for (try = 0; ; try++) {
199
0
        va_start(arg, fmt);
200
        /* Append the new formatted string */
201
        /* fmt_len is the length of the string required, excluding the
202
         * trailing NULL */
203
0
        empty_len = strbuf_empty_length(s);
204
        /* Add 1 since there is also space to store the terminating NULL. */
205
0
        fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
206
0
        va_end(arg);
207
208
0
        if (fmt_len <= empty_len)
209
0
            break;  /* SUCCESS */
210
0
        if (try > 0)
211
0
            die("BUG: length of formatted string changed");
212
213
0
        strbuf_resize(s, s->length + fmt_len);
214
0
    }
215
216
0
    s->length += fmt_len;
217
0
}
218
219
/* vi:ai et sw=4 ts=4:
220
 */