Line data Source code
1 : #include <stdio.h>
2 : #include <stdlib.h>
3 : #include <stdarg.h>
4 : #include "../fd_util.h"
5 : #include "fd_textstream.h"
6 :
7 : struct fd_textstream_blk {
8 : struct fd_textstream_blk * next;
9 : ulong used;
10 : };
11 : typedef struct fd_textstream_blk fd_textstream_blk_t;
12 :
13 : fd_textstream_t * fd_textstream_new( fd_textstream_t * strm,
14 : fd_valloc_t valloc,
15 0 : ulong alloc_sz) {
16 0 : strm->valloc = valloc;
17 0 : strm->alloc_sz = alloc_sz;
18 0 : fd_textstream_blk_t * blk = (fd_textstream_blk_t *)
19 0 : fd_valloc_malloc(valloc, alignof(fd_textstream_blk_t), sizeof(fd_textstream_blk_t) + alloc_sz);
20 0 : if ( blk == NULL )
21 0 : return NULL;
22 0 : blk->next = NULL;
23 0 : blk->used = 0;
24 0 : strm->first_blk = strm->last_blk = blk;
25 0 : return strm;
26 0 : }
27 :
28 0 : void fd_textstream_destroy( fd_textstream_t * strm ) {
29 0 : for ( fd_textstream_blk_t * blk = strm->first_blk; blk; ) {
30 0 : fd_textstream_blk_t * next = blk->next;
31 0 : fd_valloc_free(strm->valloc, blk);
32 0 : blk = next;
33 0 : }
34 0 : }
35 :
36 0 : void fd_textstream_clear( fd_textstream_t * strm ) {
37 0 : for ( fd_textstream_blk_t * blk = strm->first_blk->next; blk; ) {
38 0 : fd_textstream_blk_t * next = blk->next;
39 0 : fd_valloc_free(strm->valloc, blk);
40 0 : blk = next;
41 0 : }
42 0 : fd_textstream_blk_t * blk = strm->first_blk;
43 0 : blk->next = NULL;
44 0 : blk->used = 0;
45 0 : strm->last_blk = blk;
46 0 : }
47 :
48 : static fd_textstream_blk_t *
49 0 : fd_textstream_new_blk( fd_textstream_t * strm ) {
50 0 : fd_textstream_blk_t * blk = (fd_textstream_blk_t *)
51 0 : fd_valloc_malloc(strm->valloc, alignof(fd_textstream_blk_t), sizeof(fd_textstream_blk_t) + strm->alloc_sz);
52 0 : if ( blk == NULL )
53 0 : return NULL;
54 0 : blk->next = NULL;
55 0 : blk->used = 0;
56 0 : strm->last_blk->next = blk;
57 0 : strm->last_blk = blk;
58 0 : return blk;
59 0 : }
60 :
61 : int fd_textstream_append( fd_textstream_t * strm,
62 : const char * text,
63 0 : ulong text_sz ) {
64 0 : fd_textstream_blk_t * blk = strm->last_blk;
65 0 : if ( FD_LIKELY( blk->used + text_sz <= strm->alloc_sz ) ) {
66 : /* pass */
67 0 : } else if ( text_sz > strm->alloc_sz ) {
68 0 : return -1;
69 0 : } else {
70 0 : blk = fd_textstream_new_blk( strm );
71 0 : if ( blk == NULL )
72 0 : return -1;
73 0 : }
74 0 : char* buf = (char*)(blk + 1);
75 0 : fd_memcpy(buf + blk->used, text, text_sz);
76 0 : blk->used += text_sz;
77 0 : return 0;
78 0 : }
79 :
80 0 : ulong fd_textstream_total_size( fd_textstream_t * strm ) {
81 0 : ulong tot = 0;
82 0 : for ( fd_textstream_blk_t * blk = strm->first_blk; blk; blk = blk->next )
83 0 : tot += blk->used;
84 0 : return tot;
85 0 : }
86 :
87 : int fd_textstream_get_output( fd_textstream_t * strm,
88 0 : char * outbuf) {
89 0 : ulong tot = 0;
90 0 : for ( fd_textstream_blk_t * blk = strm->first_blk; blk; blk = blk->next ) {
91 0 : fd_memcpy(outbuf + tot, blk+1, blk->used);
92 0 : tot += blk->used;
93 0 : }
94 0 : return 0;
95 0 : }
96 :
97 0 : ulong fd_textstream_get_iov_count( fd_textstream_t * strm ) {
98 0 : ulong tot = 0;
99 0 : for ( fd_textstream_blk_t * blk = strm->first_blk; blk; blk = blk->next )
100 0 : tot++;
101 0 : return tot;
102 0 : }
103 :
104 : int fd_textstream_get_iov( fd_textstream_t * strm,
105 0 : struct fd_iovec * iov) {
106 0 : ulong tot = 0;
107 0 : for ( fd_textstream_blk_t * blk = strm->first_blk; blk; blk = blk->next ) {
108 0 : iov[tot].iov_base = blk+1;
109 0 : iov[tot].iov_len = blk->used;
110 0 : tot++;
111 0 : }
112 0 : return 0;
113 0 : }
114 :
115 : int fd_textstream_encode_utf8( fd_textstream_t * strm,
116 : const uint * chars,
117 0 : ulong chars_sz ) {
118 0 : ulong out_sz = 0;
119 0 : for ( ulong i = 0; i < chars_sz; ++i ) {
120 0 : uint ch = chars[i];
121 0 : if (ch < 0x80)
122 0 : out_sz += 1;
123 0 : else if (ch < 0x800)
124 0 : out_sz += 2;
125 0 : else if (ch < 0x10000)
126 0 : out_sz += 3;
127 0 : else if (ch < 0x110000)
128 0 : out_sz += 4;
129 0 : else
130 0 : return -1;
131 0 : }
132 :
133 0 : fd_textstream_blk_t * blk = strm->last_blk;
134 0 : if ( FD_LIKELY( blk->used + out_sz <= strm->alloc_sz ) ) {
135 : /* pass */
136 0 : } else if ( out_sz > strm->alloc_sz ) {
137 0 : return -1;
138 0 : } else {
139 0 : blk = fd_textstream_new_blk( strm );
140 0 : if ( blk == NULL )
141 0 : return -1;
142 0 : }
143 0 : char* dest = (char*)(blk + 1) + blk->used;
144 :
145 0 : ulong j = 0;
146 0 : for ( ulong i = 0; i < chars_sz; ++i ) {
147 0 : uint ch = chars[i];
148 0 : if (ch < 0x80) {
149 0 : dest[j++] = (char)ch;
150 0 : } else if (ch < 0x800) {
151 0 : dest[j++] = (char)((ch>>6) | 0xC0);
152 0 : dest[j++] = (char)((ch & 0x3F) | 0x80);
153 0 : } else if (ch < 0x10000) {
154 0 : dest[j++] = (char)((ch>>12) | 0xE0);
155 0 : dest[j++] = (char)(((ch>>6) & 0x3F) | 0x80);
156 0 : dest[j++] = (char)((ch & 0x3F) | 0x80);
157 0 : } else if (ch < 0x110000) {
158 0 : dest[j++] = (char)((ch>>18) | 0xF0);
159 0 : dest[j++] = (char)(((ch>>12) & 0x3F) | 0x80);
160 0 : dest[j++] = (char)(((ch>>6) & 0x3F) | 0x80);
161 0 : dest[j++] = (char)((ch & 0x3F) | 0x80);
162 0 : }
163 0 : }
164 :
165 0 : blk->used += j;
166 0 : return 0;
167 0 : }
168 :
169 : static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
170 :
171 : int fd_textstream_encode_base58( fd_textstream_t * strm,
172 : const void * data,
173 0 : ulong data_sz ) {
174 : /* Prevent explosive growth in computation */
175 0 : if (data_sz > 400U)
176 0 : return -1;
177 :
178 0 : const uchar* bin = (const uchar*)data;
179 0 : ulong carry;
180 0 : ulong i, j, high, zcount = 0;
181 0 : ulong size;
182 :
183 0 : while (zcount < data_sz && !bin[zcount])
184 0 : ++zcount;
185 :
186 : /* Temporary buffer size */
187 0 : size = (data_sz - zcount) * 138 / 100 + 1;
188 0 : uchar buf[size];
189 0 : fd_memset(buf, 0, size);
190 :
191 0 : for (i = zcount, high = size - 1; i < data_sz; ++i, high = j) {
192 0 : for (carry = bin[i], j = size - 1; (j > high) || carry; --j) {
193 0 : carry += 256UL * (ulong)buf[j];
194 0 : buf[j] = (uchar)(carry % 58);
195 0 : carry /= 58UL;
196 0 : if (!j) {
197 : // Otherwise j wraps to maxint which is > high
198 0 : break;
199 0 : }
200 0 : }
201 0 : }
202 :
203 0 : for (j = 0; j < size && !buf[j]; ++j) ;
204 :
205 0 : ulong out_sz = zcount + size - j;
206 0 : fd_textstream_blk_t * blk = strm->last_blk;
207 0 : if ( FD_LIKELY( blk->used + out_sz <= strm->alloc_sz ) ) {
208 : /* pass */
209 0 : } else if ( out_sz > strm->alloc_sz ) {
210 0 : return -1;
211 0 : } else {
212 0 : blk = fd_textstream_new_blk( strm );
213 0 : if ( blk == NULL )
214 0 : return -1;
215 0 : }
216 0 : char* b58 = (char*)(blk + 1) + blk->used;
217 :
218 0 : if (zcount)
219 0 : fd_memset(b58, '1', zcount);
220 0 : for (i = zcount; j < size; ++i, ++j)
221 0 : b58[i] = b58digits_ordered[buf[j]];
222 :
223 0 : blk->used += i;
224 :
225 0 : return 0;
226 0 : }
227 :
228 : static char base64_encoding_table[] = {
229 : 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
230 : 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
231 : 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
232 : 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
233 : 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
234 : 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
235 : 'w', 'x', 'y', 'z', '0', '1', '2', '3',
236 : '4', '5', '6', '7', '8', '9', '+', '/'
237 : };
238 :
239 : int fd_textstream_encode_base64( fd_textstream_t * strm,
240 : const void * data,
241 0 : ulong data_sz ) {
242 0 : ulong out_sz = 4 * ((data_sz + 2) / 3);
243 0 : fd_textstream_blk_t * blk = strm->last_blk;
244 0 : if ( FD_LIKELY( blk->used + out_sz <= strm->alloc_sz ) ) {
245 : /* pass */
246 0 : } else if ( out_sz > strm->alloc_sz ) {
247 0 : return -1;
248 0 : } else {
249 0 : blk = fd_textstream_new_blk( strm );
250 0 : if ( blk == NULL )
251 0 : return -1;
252 0 : }
253 0 : char* out_data = (char*)(blk + 1) + blk->used;
254 :
255 0 : ulong j = 0;
256 0 : for (ulong i = 0; i < data_sz; ) {
257 0 : switch (data_sz - i) {
258 0 : default: { /* 3 and above */
259 0 : uint octet_a = ((uchar*)data)[i++];
260 0 : uint octet_b = ((uchar*)data)[i++];
261 0 : uint octet_c = ((uchar*)data)[i++];
262 0 : uint triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
263 0 : out_data[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F];
264 0 : out_data[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F];
265 0 : out_data[j++] = base64_encoding_table[(triple >> 1 * 6) & 0x3F];
266 0 : out_data[j++] = base64_encoding_table[(triple >> 0 * 6) & 0x3F];
267 0 : break;
268 0 : }
269 0 : case 2: {
270 0 : uint octet_a = ((uchar*)data)[i++];
271 0 : uint octet_b = ((uchar*)data)[i++];
272 0 : uint triple = (octet_a << 0x10) + (octet_b << 0x08);
273 0 : out_data[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F];
274 0 : out_data[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F];
275 0 : out_data[j++] = base64_encoding_table[(triple >> 1 * 6) & 0x3F];
276 0 : out_data[j++] = '=';
277 0 : break;
278 0 : }
279 0 : case 1: {
280 0 : uint octet_a = ((uchar*)data)[i++];
281 0 : uint triple = (octet_a << 0x10);
282 0 : out_data[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F];
283 0 : out_data[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F];
284 0 : out_data[j++] = '=';
285 0 : out_data[j++] = '=';
286 0 : break;
287 0 : }
288 0 : }
289 0 : }
290 :
291 0 : blk->used += j;
292 0 : return 0;
293 0 : }
294 :
295 : static const char hex_encoding_table[] = "0123456789ABCDEF";
296 :
297 : int fd_textstream_encode_hex( fd_textstream_t * strm,
298 : const void * data,
299 0 : ulong data_sz ) {
300 0 : ulong out_sz = 2 * data_sz;
301 0 : fd_textstream_blk_t * blk = strm->last_blk;
302 0 : if ( FD_LIKELY( blk->used + out_sz <= strm->alloc_sz ) ) {
303 : /* pass */
304 0 : } else if ( out_sz > strm->alloc_sz ) {
305 0 : return -1;
306 0 : } else {
307 0 : blk = fd_textstream_new_blk( strm );
308 0 : if ( blk == NULL )
309 0 : return -1;
310 0 : }
311 0 : char* out_data = (char*)(blk + 1) + blk->used;
312 :
313 0 : ulong j = 0;
314 0 : for (ulong i = 0; i < data_sz; ) {
315 0 : uint octet = ((uchar*)data)[i++];
316 0 : out_data[j++] = hex_encoding_table[(octet >> 4) & 0xF];
317 0 : out_data[j++] = hex_encoding_table[octet & 0xF];
318 0 : }
319 :
320 0 : blk->used += j;
321 0 : return 0;
322 0 : }
323 :
324 0 : int fd_textstream_sprintf( fd_textstream_t * strm, const char* format, ... ) {
325 0 : fd_textstream_blk_t * blk = strm->last_blk;
326 0 : ulong remain = strm->alloc_sz - blk->used;
327 0 : char* buf = (char*)(blk + 1) + blk->used;
328 0 : va_list ap;
329 0 : va_start(ap, format);
330 0 : int r = vsnprintf(buf, remain, format, ap);
331 0 : va_end(ap);
332 0 : if (r >= 0 && (uint)r < remain) {
333 0 : blk->used += (uint)r;
334 0 : return 0;
335 0 : }
336 :
337 0 : blk = fd_textstream_new_blk( strm );
338 0 : if ( blk == NULL )
339 0 : return -1;
340 :
341 0 : remain = strm->alloc_sz;
342 0 : buf = (char*)(blk + 1);
343 0 : va_start(ap, format);
344 0 : r = vsnprintf(buf, remain, format, ap);
345 0 : va_end(ap);
346 0 : if (r >= 0 && (uint)r < remain) {
347 0 : blk->used = (uint)r;
348 0 : return 0;
349 0 : }
350 :
351 0 : return -1;
352 0 : }
|