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