1212 * Note: these can't be freed per thread, since
1313 * DllMain(DLL_THREAD_ATTACH/DLL_THREAD_DETACH) is optional (and apps are
1414 * inconsistent even calling attach-detach for same thread). */
15- static wchar_t * * u16buffs = NULL ;
16- static size_t u16buff_cnt = 0 ;
17- static esodbc_mutex_lt u16buff_mux = ESODBC_MUX_SINIT ;
15+ static void * * utf_buffs = NULL ;
16+ static size_t utf_buff_cnt = 0 ;
17+ static esodbc_mutex_lt utf_buff_mux = ESODBC_MUX_SINIT ;
1818
1919/* advance an iterator of an "entered" JSON-sytle map to the value for the
2020 * given key, if that exists */
@@ -189,31 +189,30 @@ CborError cbor_container_is_empty(CborValue cont, BOOL *empty)
189189 return CborNoError ;
190190}
191191
192- static BOOL enlist_utf16_buffer ( wchar_t * old , wchar_t * new )
192+ static BOOL enlist_utf_buffer ( void * old , void * new )
193193{
194- wchar_t * * r ;
194+ void * * r ;
195195 size_t i ;
196196
197- if (! old ) {
198- /* new entry must be inserted into list */
199- ESODBC_MUX_LOCK (& u16buff_mux );
200- r = realloc (u16buffs , (u16buff_cnt + 1 ) * sizeof (wchar_t * ));
197+ if (! old ) { /* new entry must be inserted into list */
198+ ESODBC_MUX_LOCK (& utf_buff_mux );
199+ r = realloc (utf_buffs , (utf_buff_cnt + 1 ) * sizeof (void * ));
201200 if (r ) {
202- u16buffs = r ;
203- u16buffs [ u16buff_cnt ++ ] = new ;
201+ utf_buffs = r ;
202+ utf_buffs [ utf_buff_cnt ++ ] = new ;
204203 }
205- ESODBC_MUX_UNLOCK (& u16buff_mux );
206- } else {
207- ESODBC_MUX_LOCK (& u16buff_mux );
204+ ESODBC_MUX_UNLOCK (& utf_buff_mux );
205+ } else { /* old entry has be reallocated, store its updated ref */
206+ ESODBC_MUX_LOCK (& utf_buff_mux );
208207 r = NULL ;
209- for (i = 0 ; i < u16buff_cnt ; i ++ ) {
210- if (u16buffs [i ] == old ) {
211- r = & u16buffs [i ];
212- u16buffs [i ] = new ;
208+ for (i = 0 ; i < utf_buff_cnt ; i ++ ) {
209+ if (utf_buffs [i ] == old ) {
210+ r = & utf_buffs [i ];
211+ utf_buffs [i ] = new ;
213212 break ;
214213 }
215214 }
216- ESODBC_MUX_UNLOCK (& u16buff_mux );
215+ ESODBC_MUX_UNLOCK (& utf_buff_mux );
217216 }
218217
219218 return !!r ;
@@ -222,31 +221,82 @@ static BOOL enlist_utf16_buffer(wchar_t *old, wchar_t *new)
222221void tinycbor_cleanup ()
223222{
224223 size_t i ;
225- for (i = 0 ; i < u16buff_cnt ; i ++ ) {
226- free (u16buffs [i ]);
224+ for (i = 0 ; i < utf_buff_cnt ; i ++ ) {
225+ free (utf_buffs [i ]);
227226 }
228227 if (i ) {
229- free (u16buffs );
228+ free (utf_buffs );
230229 }
231230}
232231
232+ static BOOL enlarge_buffer (void * str_ptr , size_t new_cnt , size_t usize )
233+ {
234+ wstr_st r ; /* reallocated */
235+ wstr_st * wbuff = (wstr_st * )str_ptr ;
236+ /* the two string struct types should remain identical (ptr, size_t),
237+ * for the above cast to work also for cstr_st inputs */
238+ assert (sizeof (cstr_st ) == sizeof (wstr_st ));
239+ assert ((void * )wbuff -> str == (void * )((cstr_st * )str_ptr )-> str );
240+ assert (wbuff -> cnt == ((cstr_st * )str_ptr )-> cnt );
241+
242+ /* double scratchpad size until exceeding min needed space.
243+ * condition on equality, to allow for a 0-term */
244+ for (r .cnt = (0 < wbuff -> cnt && wbuff -> cnt < (size_t )-1 ) ? wbuff -> cnt :
245+ ESODBC_BODY_BUF_START_SIZE ; r .cnt <= new_cnt ; r .cnt *= 2 ) {
246+ ;
247+ }
248+ if (! (r .str = realloc (wbuff -> str , r .cnt * usize ))) {
249+ return false;
250+ }
251+ if (! enlist_utf_buffer (wbuff -> str , r .str )) {
252+ /* it should only possibly fail on 1st allocation per-thread (since
253+ * the rest of invocations are to swap the pointers) */
254+ assert (! wbuff -> str );
255+ free (r .str );
256+ return false;
257+ } else {
258+ * wbuff = r ;
259+ }
260+ DBG ("new UTF conv. buffer @0x%p, size %zu, usize: %zu." , wbuff -> str ,
261+ wbuff -> cnt , usize );
262+ return true;
263+ }
264+
233265/* Fetches and converts a(n always UTF8) text string to UTF16 wide char.
234266 * Uses a dynamically allocated thread-local buffer.
235267 * 0-terminates the string */
236268CborError cbor_value_get_utf16_wstr (CborValue * it , wstr_st * utf16 )
237269{
270+ /* .cnt needs to be non-zero, for U8MB_TO_U16WC() to fail on 1st invoc. */
238271 static thread_local wstr_st wbuff = {.str = NULL , .cnt = (size_t )-1 };
239- wstr_st r ; /* reallocated */
272+ static thread_local cstr_st cbuff = { 0 };
240273 cstr_st mb_str ; /* multibyte string */
241274 CborError res ;
242275 int n ;
276+ size_t len ;
243277
244278 assert (cbor_value_is_text_string (it ));
245279 /* get the multibyte string to convert */
246- res = cbor_value_get_string_chunk (it , & mb_str .str , & mb_str .cnt );
247- if (res != CborNoError ) {
248- return res ;
280+ if (cbor_value_is_length_known (it )) { /* str contained in single chunk? */
281+ res = cbor_value_get_string_chunk (it , & mb_str .str , & mb_str .cnt );
282+ if (res != CborNoError ) {
283+ return res ;
284+ }
285+ } else { /* string is spread across multiple chunks */
286+ res = cbor_value_calculate_string_length (it , & len );
287+ if (res != CborNoError ) {
288+ return res ;
289+ }
290+ if (cbuff .cnt <= len && !enlarge_buffer (& cbuff , len , sizeof (char ))) {
291+ return CborErrorOutOfMemory ;
292+ }
293+ mb_str = cbuff ;
294+ res = cbor_value_copy_text_string (it , mb_str .str , & mb_str .cnt , it );
295+ if (res != CborNoError ) {
296+ return res ;
297+ }
249298 }
299+
250300 /* attempt string conversion */
251301 while ((n = U8MB_TO_U16WC (mb_str .str , mb_str .cnt , wbuff .str ,
252302 wbuff .cnt )) <= 0 ) {
@@ -264,7 +314,6 @@ CborError cbor_value_get_utf16_wstr(CborValue *it, wstr_st *utf16)
264314 } /* else: buffer hasn't yet been allocated or is too small */
265315 /* what's the minimum space needed? */
266316 if ((n = U8MB_TO_U16WC (mb_str .str , mb_str .cnt , NULL , 0 )) < 0 ) {
267- TRACE ;
268317 return CborErrorInvalidUtf8TextString ;
269318 }
270319 } else {
@@ -273,28 +322,13 @@ CborError cbor_value_get_utf16_wstr(CborValue *it, wstr_st *utf16)
273322 break ;
274323 }
275324 }
276- /* double scratchpad size until exceeding min needed space.
277- * condition on equality, to allow for a 0-term */
278- for (r .cnt = wbuff .cnt < (size_t )-1 ? wbuff .cnt :
279- ESODBC_BODY_BUF_START_SIZE ; r .cnt <= (size_t )n ; r .cnt *= 2 ) {
280- ;
281- }
282- if (! (r .str = realloc (wbuff .str , r .cnt ))) {
325+ if (! enlarge_buffer (& wbuff , (size_t )n , sizeof (wchar_t ))) {
283326 return CborErrorOutOfMemory ;
284327 }
285- if (! enlist_utf16_buffer (wbuff .str , r .str )) {
286- /* it should only fail on 1st allocation per-thread */
287- assert (! wbuff .str );
288- free (r .str );
289- return CborErrorOutOfMemory ;
290- } else {
291- wbuff = r ;
292- }
293- DBG ("new UTF8/16 conv. buffer @0x%p, size %zu." , wbuff .str , wbuff .cnt );
294328 }
295329
296330 /* U8MB_TO_U16WC() will only convert the 0-term if counted in input*/
297- wbuff .str [n ] = '\0' ; /* set, but not counted */
331+ wbuff .str [n ] = L '\0' ; /* set, but not counted */
298332 utf16 -> str = wbuff .str ;
299333 utf16 -> cnt = n ;
300334 return CborNoError ;
0 commit comments