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