Line data Source code
1 : /***************************************************************************//**
2 :
3 : @file charbuf.c
4 :
5 : @author Stephen Brennan
6 :
7 : @date Saturday, 23 May 2015
8 :
9 : @brief Implementation of "libstephen/cb.h".
10 :
11 : @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the
12 : Revised BSD License. See the LICENSE.txt file for details.
13 :
14 : *******************************************************************************/
15 :
16 : #include <stdarg.h>
17 : #include <wchar.h>
18 : #include <string.h>
19 :
20 : #include "libstephen/base.h"
21 : #include "libstephen/cb.h"
22 :
23 : /*******************************************************************************
24 :
25 : cbuf Functions
26 :
27 : *******************************************************************************/
28 :
29 28 : void cb_init(cbuf *obj, int capacity)
30 : {
31 : // Initialization logic
32 28 : obj->buf = smb_new(char, capacity);
33 28 : obj->buf[0] = '\0';
34 28 : obj->capacity = capacity;
35 28 : obj->length = 0;
36 28 : }
37 :
38 8 : cbuf *cb_create(int capacity)
39 : {
40 8 : cbuf *obj = smb_new(cbuf, 1);
41 8 : cb_init(obj, capacity);
42 8 : return obj;
43 : }
44 :
45 24 : void cb_destroy(cbuf *obj)
46 : {
47 24 : smb_free(obj->buf);
48 24 : obj->buf = NULL;
49 24 : }
50 :
51 8 : void cb_delete(cbuf *obj) {
52 8 : cb_destroy(obj);
53 8 : smb_free(obj);
54 8 : }
55 :
56 : /**
57 : @brief Ensure that the cbuf can fit a certain amount of characters.
58 : @param obj The cbuf to expand (if necessary).
59 : @param minsize The minimum size the cbuf should be able to fit.
60 :
61 : Note that minsize should include the NUL byte as part of the character count.
62 : Therefore, to ensure that the string "four" fits in the buffer, you would
63 : want to run `cb_expand_to_fit(obj, 5)` (assuming the buffer was empty).
64 : */
65 170 : static void cb_expand_to_fit(cbuf *obj, int minsize)
66 : {
67 170 : int newcapacity = obj->capacity;
68 344 : while (newcapacity < minsize) {
69 4 : newcapacity *= 2;
70 : }
71 170 : if (newcapacity != obj->capacity) {
72 3 : obj->buf = smb_renew(char, obj->buf, newcapacity);
73 3 : obj->capacity = newcapacity;
74 : }
75 170 : }
76 :
77 7 : void cb_concat(cbuf *obj, char *buf)
78 : {
79 7 : int length = strlen(buf);
80 7 : cb_expand_to_fit(obj, obj->length + length + 1);
81 7 : strcpy(obj->buf + obj->length, buf);
82 7 : obj->length += length;
83 7 : }
84 :
85 146 : void cb_append(cbuf *obj, char next)
86 : {
87 146 : cb_expand_to_fit(obj, obj->length + 2); // include new character + nul
88 146 : obj->buf[obj->length] = next;
89 146 : obj->length++;
90 146 : obj->buf[obj->length] = '\0';
91 146 : }
92 :
93 5 : void cb_trim(cbuf *obj)
94 : {
95 5 : obj->buf = smb_renew(char, obj->buf, obj->length + 1);
96 5 : obj->capacity = obj->length + 1;
97 5 : }
98 :
99 1 : void cb_clear(cbuf *obj)
100 : {
101 1 : obj->buf[0] = '\0';
102 1 : obj->length = 0;
103 1 : }
104 :
105 17 : void cb_vprintf(cbuf *obj, char *format, va_list va)
106 : {
107 : va_list v2;
108 : int length;
109 17 : va_copy(v2, va);
110 :
111 : // Find the length of the formatted string.
112 17 : length = vsnprintf(NULL, 0, format, va);
113 :
114 : // Make sure we have enough room for everything.
115 17 : cb_expand_to_fit(obj, obj->length + length + 1);
116 :
117 : // Put the formatted string into the buffer.
118 17 : vsnprintf(obj->buf + obj->length, length + 1, format, v2);
119 17 : va_end(v2);
120 17 : }
121 :
122 9 : void cb_printf(cbuf *obj, char *format, ...)
123 : {
124 : va_list va;
125 9 : va_start(va, format);
126 9 : cb_vprintf(obj, format, va);
127 9 : va_end(va); // Have to va_stop() it when you're done using it.
128 9 : }
129 :
130 : /*******************************************************************************
131 :
132 : wcbuf Functions
133 :
134 : *******************************************************************************/
135 :
136 12 : void wcb_init(wcbuf *obj, int capacity)
137 : {
138 : // Initialization logic
139 12 : obj->buf = smb_new(wchar_t, capacity);
140 12 : obj->buf[0] = L'\0';
141 12 : obj->capacity = capacity;
142 12 : obj->length = 0;
143 12 : }
144 :
145 8 : wcbuf *wcb_create(int capacity)
146 : {
147 8 : wcbuf *obj = smb_new(wcbuf, 1);
148 8 : wcb_init(obj, capacity);
149 8 : return obj;
150 : }
151 :
152 8 : void wcb_destroy(wcbuf *obj)
153 : {
154 : // Cleanup logic
155 8 : smb_free(obj->buf);
156 8 : obj->buf = NULL;
157 8 : }
158 :
159 8 : void wcb_delete(wcbuf *obj)
160 : {
161 8 : wcb_destroy(obj);
162 8 : smb_free(obj);
163 8 : }
164 :
165 : /**
166 : @brief Ensure that the wcbuf can fit a certain amount of characters.
167 : @param obj The wcbuf to expand (if necessary).
168 : @param minsize The minimum size the wcbuf should be able to fit.
169 :
170 : Note that minsize should include the NUL character as part of the character
171 : count. Therefore, to ensure that the string L"four" fits in the buffer, you
172 : would want to run `cb_expand_to_fit(obj, 5)` (assuming the buffer was empty).
173 : */
174 154 : static void wcb_expand_to_fit(wcbuf *obj, int minsize)
175 : {
176 154 : int newcapacity = obj->capacity;
177 312 : while (newcapacity < minsize) {
178 4 : newcapacity *= 2;
179 : }
180 154 : if (newcapacity != obj->capacity) {
181 3 : obj->buf = smb_renew(wchar_t, obj->buf, newcapacity);
182 3 : obj->capacity = newcapacity;
183 : }
184 154 : }
185 :
186 7 : void wcb_concat(wcbuf *obj, wchar_t *str)
187 : {
188 7 : int length = wcslen(str);
189 7 : wcb_expand_to_fit(obj, obj->length + length + 1);
190 7 : wcscpy(obj->buf + obj->length, str);
191 7 : obj->length += length;
192 7 : }
193 :
194 146 : void wcb_append(wcbuf *obj, wchar_t next)
195 : {
196 146 : wcb_expand_to_fit(obj, obj->length + 2); // include new character + nul
197 146 : obj->buf[obj->length] = next;
198 146 : obj->length++;
199 146 : obj->buf[obj->length] = L'\0';
200 146 : }
201 :
202 5 : void wcb_trim(wcbuf *obj)
203 : {
204 5 : obj->buf = smb_renew(wchar_t, obj->buf, obj->length + 1);
205 5 : obj->capacity = obj->length + 1;
206 5 : }
207 :
208 1 : void wcb_clear(wcbuf *obj)
209 : {
210 1 : obj->buf[0] = L'\0';
211 1 : obj->length = 0;
212 1 : }
213 :
214 1 : void wcb_vprintf(wcbuf *obj, wchar_t *format, va_list v1)
215 : {
216 : va_list v2;
217 : char *mbformat, *mbout;
218 : size_t mbformat_len, mbout_len, wcout_len;
219 :
220 : // First, convert the wide format string to a multibyte one.
221 1 : mbformat_len = wcstombs(NULL, format, 0);
222 1 : mbformat = smb_new(char, mbformat_len + 1);
223 1 : wcstombs(mbformat, format, mbformat_len+1);
224 :
225 : // Then, use vsnprintf first to find out how many bytes of output to allocate.
226 1 : va_copy(v2, v1);
227 1 : mbout_len = vsnprintf(NULL, 0, mbformat, v1);
228 :
229 : // Now, actually allocate the memory and do the multibyte print.
230 1 : mbout = smb_new(char, mbout_len + 1);
231 1 : vsnprintf(mbout, mbout_len + 1, mbformat, v2);
232 1 : va_end(v2);
233 :
234 : // And now, get the number of wide characters this is.
235 1 : wcout_len = mbstowcs(NULL, mbout, 0);
236 :
237 : // Make sure we have room for everything.
238 1 : wcb_expand_to_fit(obj, obj->length + wcout_len + 1);
239 :
240 : // And, put the output in the buffer.
241 1 : mbstowcs(obj->buf + obj->length, mbout, wcout_len + 1);
242 1 : obj->length += wcout_len;
243 :
244 : // Free up allocated memory, and we're good to go.
245 1 : smb_free(mbformat);
246 1 : smb_free(mbout);
247 1 : }
248 :
249 1 : void wcb_printf(wcbuf *obj, wchar_t *format, ...)
250 : {
251 : va_list va;
252 1 : va_start(va, format);
253 1 : wcb_vprintf(obj, format, va);
254 1 : va_end(va);
255 1 : }
|