1 /*
2 * Copyright (C) Tildeslash Ltd. All rights reserved.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Affero General Public License for more details.
11 *
12 * You should have received a copy of the GNU Affero General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * In addition, as a special exception, the copyright holders give
16 * permission to link the code of portions of this program with the
17 * OpenSSL library under certain conditions as described in each
18 * individual source file, and distribute linked combinations
19 * including the two.
20 *
21 * You must obey the GNU Affero General Public License in all respects
22 * for all of the code used other than OpenSSL.
23 */
24
25 #include "Config.h"
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <ctype.h>
32 #ifdef HAVE_ZLIB_H
33 #include <zlib.h>
34 #endif
35
36 #include "Str.h"
37 #include "StringBuffer.h"
38
39
40 /**
41 * Implementation of the StringBuffer interface.
42 *
43 * @author http://www.tildeslash.com/
44 * @see http://www.mmonit.com/
45 * @file
46 */
47
48
49 /* ------------------------------------------------------------ Definitions */
50
51
52 #define T StringBuffer_T
53 struct T {
54 int used;
55 int length;
56 unsigned char *buffer;
57 void *compressedBuffer;
58 };
59
60
61 /* ---------------------------------------------------------------- Private */
62
63
64 __attribute__((format (printf, 2, 0)))
_append(T S,const char * s,va_list ap)65 static inline void _append(T S, const char *s, va_list ap) {
66 va_list ap_copy;
67 while (true) {
68 va_copy(ap_copy, ap);
69 int n = vsnprintf((char *)(S->buffer + S->used), S->length - S->used, s, ap_copy);
70 va_end(ap_copy);
71 if ((S->used + n) < S->length) {
72 S->used += n;
73 break;
74 }
75 S->length += STRLEN + n;
76 RESIZE(S->buffer, S->length);
77 }
78 }
79
80
_ctor(int hint)81 static inline T _ctor(int hint) {
82 T S;
83 NEW(S);
84 S->used = 0;
85 S->length = hint;
86 S->buffer = ALLOC(hint);
87 *S->buffer = 0;
88 return S;
89 }
90
91
92 /* ----------------------------------------------------------------- Public */
93
94
StringBuffer_new(const char * s)95 T StringBuffer_new(const char *s) {
96 return StringBuffer_append(_ctor(STRLEN), "%s", s);
97 }
98
99
StringBuffer_create(int hint)100 T StringBuffer_create(int hint) {
101 if (hint <= 0)
102 THROW(AssertException, "Illegal hint value");
103 return _ctor(hint);
104 }
105
106
StringBuffer_free(T * S)107 void StringBuffer_free(T *S) {
108 assert(S && *S);
109 FREE((*S)->buffer);
110 FREE((*S)->compressedBuffer);
111 FREE(*S);
112 }
113
114
StringBuffer_append(T S,const char * s,...)115 T StringBuffer_append(T S, const char *s, ...) {
116 assert(S);
117 if (STR_DEF(s)) {
118 va_list ap;
119 va_start(ap, s);
120 _append(S, s, ap);
121 va_end(ap);
122 }
123 return S;
124 }
125
126
StringBuffer_vappend(T S,const char * s,va_list ap)127 T StringBuffer_vappend(T S, const char *s, va_list ap) {
128 assert(S);
129 if (STR_DEF(s)) {
130 va_list ap_copy;
131 va_copy(ap_copy, ap);
132 _append(S, s, ap_copy);
133 va_end(ap_copy);
134 }
135 return S;
136 }
137
138
StringBuffer_replace(T S,const char * a,const char * b)139 int StringBuffer_replace(T S, const char *a, const char *b) {
140 int n = 0;
141 assert(S);
142 if (a && b && *a) {
143 int i, j;
144 for (i = 0; S->buffer[i]; i++) {
145 if (S->buffer[i] == *a) {
146 j = 0;
147 do
148 if (! a[++j]) {n++; break;}
149 while (S->buffer[i + j] == a[j]);
150 }
151 }
152 if (n) {
153 int m = n;
154 size_t bl = strlen(b);
155 size_t diff = bl - strlen(a);
156 if (diff > 0) {
157 size_t required = (diff * n) + S->used + 1;
158 if (required >= (size_t)S->length) {
159 S->length = (int)required;
160 RESIZE(S->buffer, S->length);
161 }
162 }
163 for (i = 0; m; i++) {
164 if (S->buffer[i] == *a) {
165 j = 0;
166 do
167 if (! a[++j]) {
168 memmove(S->buffer + i + bl, S->buffer + i + j, (S->used - (i + j)));
169 memcpy(S->buffer + i, b, bl);
170 S->used += diff;
171 i += bl - 1;
172 m--;
173 break;
174 }
175 while (S->buffer[i + j] == a[j]);
176 }
177 }
178 S->buffer[S->used] = 0;
179 }
180 }
181 return n;
182 }
183
184
StringBuffer_trim(T S)185 T StringBuffer_trim(T S) {
186 assert(S);
187 // Right trim
188 while (S->used && isspace(S->buffer[S->used - 1]))
189 S->buffer[--S->used] = 0;
190 // Left trim
191 if (isspace(*S->buffer)) {
192 int i;
193 for (i = 0; isspace(S->buffer[i]); i++) ;
194 memmove(S->buffer, S->buffer + i, S->used - i);
195 S->used -= i;
196 S->buffer[S->used] = 0;
197 }
198 return S;
199 }
200
201
StringBuffer_delete(T S,int index)202 T StringBuffer_delete(T S, int index) {
203 assert(S);
204 if (index < 0 || index > S->used)
205 THROW(AssertException, "Index out of bounds");
206 S->used = index;
207 S->buffer[S->used] = 0;
208 return S;
209 }
210
211
StringBuffer_indexOf(T S,const char * s)212 int StringBuffer_indexOf(T S, const char *s) {
213 assert(S);
214 if (STR_DEF(s)) {
215 int i, j;
216 for (i = 0; i < S->used; i++) {
217 if (S->buffer[i] == *s) {
218 j = 0;
219 do
220 if (! s[++j])
221 return i;
222 while (S->buffer[i + j] == s[j]);
223 }
224 }
225 }
226 return -1;
227 }
228
229
StringBuffer_lastIndexOf(T S,const char * s)230 int StringBuffer_lastIndexOf(T S, const char *s) {
231 assert(S);
232 if (STR_DEF(s)) {
233 int i, j;
234 for (i = S->used - 1; i >= 0; i--) {
235 if (S->buffer[i] == *s) {
236 j = 0;
237 do
238 if (! s[++j])
239 return i;
240 while (S->buffer[i + j] == s[j]);
241 }
242 }
243 }
244 return -1;
245 }
246
247
StringBuffer_substring(T S,int index)248 const char *StringBuffer_substring(T S, int index) {
249 assert(S);
250 if (index < 0 || index > S->used)
251 THROW(AssertException, "Index out of bounds");
252 return (const char *)(S->buffer + index);
253 }
254
255
StringBuffer_length(T S)256 int StringBuffer_length(T S) {
257 assert(S);
258 return S->used;
259 }
260
261
StringBuffer_clear(T S)262 T StringBuffer_clear(T S) {
263 assert(S);
264 S->used = 0;
265 *S->buffer = 0;
266 FREE(S->compressedBuffer);
267 return S;
268 }
269
270
StringBuffer_toString(T S)271 const char *StringBuffer_toString(T S) {
272 assert(S);
273 return (const char *)S->buffer;
274 }
275
276
StringBuffer_toCompressed(T S,int level,size_t * length)277 const void *StringBuffer_toCompressed(T S, int level, size_t *length) {
278 assert(S);
279 assert(length);
280 assert(level >= 0 && level <= 9);
281 #ifdef HAVE_LIBZ
282 *length = 0;
283 if (S->used > 0) {
284 z_stream zstream = {};
285 zstream.next_in = S->buffer;
286 zstream.avail_in = S->used;
287 int status = deflateInit2(&zstream, level, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
288 if (status == Z_OK) {
289 int need = (int)deflateBound(&zstream, S->used);
290 RESIZE(S->compressedBuffer, need);
291 zstream.next_out = S->compressedBuffer;
292 zstream.avail_out = need;
293 status = deflate(&zstream, Z_FINISH);
294 deflateEnd(&zstream);
295 if (status == Z_STREAM_END) {
296 *length = need - zstream.avail_out;
297 return (const void *)S->compressedBuffer;
298 }
299 }
300 FREE(S->compressedBuffer);
301 THROW(AssertException, "compression failed: %s", zError(status));
302 }
303 #else
304 THROW(AssertException, "compression not supported");
305 #endif
306 return NULL;
307 }
308
309