1 /*
2  * Copyright 2010-2019 Branimir Karadzic. All rights reserved.
3  * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
4  */
5 
6 #include "bx_p.h"
7 #include <bx/allocator.h>
8 #include <bx/file.h>
9 #include <bx/hash.h>
10 #include <bx/string.h>
11 
12 namespace bx
13 {
isInRange(char _ch,char _from,char _to)14 	inline bool isInRange(char _ch, char _from, char _to)
15 	{
16 		return unsigned(_ch - _from) <= unsigned(_to-_from);
17 	}
18 
isSpace(char _ch)19 	bool isSpace(char _ch)
20 	{
21 		return ' '  == _ch
22 			|| '\t' == _ch
23 			|| '\n' == _ch
24 			|| '\v' == _ch
25 			|| '\f' == _ch
26 			|| '\r' == _ch
27 			;
28 	}
29 
isUpper(char _ch)30 	bool isUpper(char _ch)
31 	{
32 		return isInRange(_ch, 'A', 'Z');
33 	}
34 
isLower(char _ch)35 	bool isLower(char _ch)
36 	{
37 		return isInRange(_ch, 'a', 'z');
38 	}
39 
isAlpha(char _ch)40 	bool isAlpha(char _ch)
41 	{
42 		return isLower(_ch) || isUpper(_ch);
43 	}
44 
isNumeric(char _ch)45 	bool isNumeric(char _ch)
46 	{
47 		return isInRange(_ch, '0', '9');
48 	}
49 
isAlphaNum(char _ch)50 	bool isAlphaNum(char _ch)
51 	{
52 		return false
53 			|| isAlpha(_ch)
54 			|| isNumeric(_ch)
55 			;
56 	}
57 
isHexNum(char _ch)58 	bool isHexNum(char _ch)
59 	{
60 		return false
61 			|| isInRange(toLower(_ch), 'a', 'f')
62 			|| isNumeric(_ch)
63 			;
64 	}
65 
isPrint(char _ch)66 	bool isPrint(char _ch)
67 	{
68 		return isInRange(_ch, ' ', '~');
69 	}
70 
71 	typedef bool (*CharTestFn)(char _ch);
72 
73 	template<CharTestFn fn>
isCharTest(const StringView & _str)74 	inline bool isCharTest(const StringView& _str)
75 	{
76 		bool result = true;
77 
78 		for (const char* ptr = _str.getPtr(), *term = _str.getTerm()
79 			; ptr != term && result
80 			; ++ptr
81 			)
82 		{
83 			result &= fn(*ptr);
84 		}
85 
86 		return result;
87 	}
88 
isSpace(const StringView & _str)89 	bool isSpace(const StringView& _str)
90 	{
91 		return isCharTest<isSpace>(_str);
92 	}
93 
isUpper(const StringView & _str)94 	bool isUpper(const StringView& _str)
95 	{
96 		return isCharTest<isUpper>(_str);
97 	}
98 
isLower(const StringView & _str)99 	bool isLower(const StringView& _str)
100 	{
101 		return isCharTest<isLower>(_str);
102 	}
103 
isAlpha(const StringView & _str)104 	bool isAlpha(const StringView& _str)
105 	{
106 		return isCharTest<isAlpha>(_str);
107 	}
108 
isNumeric(const StringView & _str)109 	bool isNumeric(const StringView& _str)
110 	{
111 		return isCharTest<isNumeric>(_str);
112 	}
113 
isAlphaNum(const StringView & _str)114 	bool isAlphaNum(const StringView& _str)
115 	{
116 		return isCharTest<isAlphaNum>(_str);
117 	}
118 
isHexNum(const StringView & _str)119 	bool isHexNum(const StringView& _str)
120 	{
121 		return isCharTest<isHexNum>(_str);
122 	}
123 
isPrint(const StringView & _str)124 	bool isPrint(const StringView& _str)
125 	{
126 		return isCharTest<isPrint>(_str);
127 	}
128 
toLower(char _ch)129 	char toLower(char _ch)
130 	{
131 		return _ch + (isUpper(_ch) ? 0x20 : 0);
132 	}
133 
toLowerUnsafe(char * _inOutStr,int32_t _len)134 	void toLowerUnsafe(char* _inOutStr, int32_t _len)
135 	{
136 		for (int32_t ii = 0; ii < _len; ++ii)
137 		{
138 			*_inOutStr = toLower(*_inOutStr);
139 		}
140 	}
141 
toLower(char * _inOutStr,int32_t _max)142 	void toLower(char* _inOutStr, int32_t _max)
143 	{
144 		const int32_t len = strLen(_inOutStr, _max);
145 		toLowerUnsafe(_inOutStr, len);
146 	}
147 
toUpper(char _ch)148 	char toUpper(char _ch)
149 	{
150 		return _ch - (isLower(_ch) ? 0x20 : 0);
151 	}
152 
toUpperUnsafe(char * _inOutStr,int32_t _len)153 	void toUpperUnsafe(char* _inOutStr, int32_t _len)
154 	{
155 		for (int32_t ii = 0; ii < _len; ++ii)
156 		{
157 			*_inOutStr = toUpper(*_inOutStr);
158 		}
159 	}
160 
toUpper(char * _inOutStr,int32_t _max)161 	void toUpper(char* _inOutStr, int32_t _max)
162 	{
163 		const int32_t len = strLen(_inOutStr, _max);
164 		toUpperUnsafe(_inOutStr, len);
165 	}
166 
167 	typedef char (*CharFn)(char _ch);
168 
toNoop(char _ch)169 	inline char toNoop(char _ch)
170 	{
171 		return _ch;
172 	}
173 
174 	template<CharFn fn>
strCmp(const char * _lhs,int32_t _lhsMax,const char * _rhs,int32_t _rhsMax)175 	inline int32_t strCmp(const char* _lhs, int32_t _lhsMax, const char* _rhs, int32_t _rhsMax)
176 	{
177 		int32_t max = min(_lhsMax, _rhsMax);
178 
179 		for (
180 			; 0 < max && fn(*_lhs) == fn(*_rhs)
181 			; ++_lhs, ++_rhs, --max
182 			)
183 		{
184 			if (*_lhs == '\0'
185 			||  *_rhs == '\0')
186 			{
187 				break;
188 			}
189 		}
190 
191 		if (0 == max)
192 		{
193 			return _lhsMax == _rhsMax ? 0 : _lhsMax > _rhsMax ? 1 : -1;
194 		}
195 
196 		return fn(*_lhs) - fn(*_rhs);
197 	}
198 
strCmp(const StringView & _lhs,const StringView & _rhs,int32_t _max)199 	int32_t strCmp(const StringView& _lhs, const StringView& _rhs, int32_t _max)
200 	{
201 		return strCmp<toNoop>(
202 			  _lhs.getPtr()
203 			, min(_lhs.getLength(), _max)
204 			, _rhs.getPtr()
205 			, min(_rhs.getLength(), _max)
206 			);
207 	}
208 
strCmpI(const StringView & _lhs,const StringView & _rhs,int32_t _max)209 	int32_t strCmpI(const StringView& _lhs, const StringView& _rhs, int32_t _max)
210 	{
211 		return strCmp<toLower>(
212 			  _lhs.getPtr()
213 			, min(_lhs.getLength(), _max)
214 			, _rhs.getPtr()
215 			, min(_rhs.getLength(), _max)
216 			);
217 	}
218 
strCmpV(const char * _lhs,int32_t _lhsMax,const char * _rhs,int32_t _rhsMax)219 	inline int32_t strCmpV(const char* _lhs, int32_t _lhsMax, const char* _rhs, int32_t _rhsMax)
220 	{
221 		int32_t max  = min(_lhsMax, _rhsMax);
222 		int32_t ii   = 0;
223 		int32_t idx  = 0;
224 		bool    zero = true;
225 
226 		for (
227 			; 0 < max && _lhs[ii] == _rhs[ii]
228 			; ++ii, --max
229 			)
230 		{
231 			const uint8_t ch = _lhs[ii];
232 			if ('\0' == ch
233 			||  '\0' == _rhs[ii])
234 			{
235 				break;
236 			}
237 
238 			if (!isNumeric(ch) )
239 			{
240 				idx  = ii+1;
241 				zero = true;
242 			}
243 			else if ('0' != ch)
244 			{
245 				zero = false;
246 			}
247 		}
248 
249 		if (0 == max)
250 		{
251 			return _lhsMax == _rhsMax ? 0 : _lhs[ii] - _rhs[ii];
252 		}
253 
254 		if ('0' != _lhs[idx]
255 		&&  '0' != _rhs[idx])
256 		{
257 			int32_t jj = 0;
258 			for (jj = ii
259 				; 0 < max && isNumeric(_lhs[jj])
260 				; ++jj, --max
261 				)
262 			{
263 				if (!isNumeric(_rhs[jj]) )
264 				{
265 					return 1;
266 				}
267 			}
268 
269 			if (isNumeric(_rhs[jj]))
270 			{
271 				return -1;
272 			}
273 		}
274 		else if (zero
275 			 &&  idx < ii
276 			 && (isNumeric(_lhs[ii]) || isNumeric(_rhs[ii]) ) )
277 		{
278 			return (_lhs[ii] - '0') - (_rhs[ii] - '0');
279 		}
280 
281 		return 0 == max && _lhsMax == _rhsMax ? 0 : _lhs[ii] - _rhs[ii];
282 	}
283 
strCmpV(const StringView & _lhs,const StringView & _rhs,int32_t _max)284 	int32_t strCmpV(const StringView& _lhs, const StringView& _rhs, int32_t _max)
285 	{
286 		return strCmpV(
287 			  _lhs.getPtr()
288 			, min(_lhs.getLength(), _max)
289 			, _rhs.getPtr()
290 			, min(_rhs.getLength(), _max)
291 			);
292 	}
293 
strLen(const char * _str,int32_t _max)294 	int32_t strLen(const char* _str, int32_t _max)
295 	{
296 		if (NULL == _str)
297 		{
298 			return 0;
299 		}
300 
301 		const char* ptr = _str;
302 		for (; 0 < _max && *ptr != '\0'; ++ptr, --_max) {};
303 		return int32_t(ptr - _str);
304 	}
305 
strLen(const StringView & _str,int32_t _max)306 	int32_t strLen(const StringView& _str, int32_t _max)
307 	{
308 		return strLen(_str.getPtr(), min(_str.getLength(), _max) );
309 	}
310 
strCopy(char * _dst,int32_t _dstSize,const char * _src,int32_t _num)311 	inline int32_t strCopy(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
312 	{
313 		BX_CHECK(NULL != _dst, "_dst can't be NULL!");
314 		BX_CHECK(NULL != _src, "_src can't be NULL!");
315 		BX_CHECK(0 < _dstSize, "_dstSize can't be 0!");
316 
317 		const int32_t len = strLen(_src, _num);
318 		const int32_t max = _dstSize-1;
319 		const int32_t num = (len < max ? len : max);
320 		memCopy(_dst, _src, num);
321 		_dst[num] = '\0';
322 
323 		return num;
324 	}
325 
strCopy(char * _dst,int32_t _dstSize,const StringView & _str,int32_t _num)326 	int32_t strCopy(char* _dst, int32_t _dstSize, const StringView& _str, int32_t _num)
327 	{
328 		return strCopy(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) );
329 	}
330 
strCat(char * _dst,int32_t _dstSize,const char * _src,int32_t _num)331 	inline int32_t strCat(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
332 	{
333 		BX_CHECK(NULL != _dst, "_dst can't be NULL!");
334 		BX_CHECK(NULL != _src, "_src can't be NULL!");
335 		BX_CHECK(0 < _dstSize, "_dstSize can't be 0!");
336 
337 		const int32_t max = _dstSize;
338 		const int32_t len = strLen(_dst, max);
339 		return strCopy(&_dst[len], max-len, _src, _num);
340 	}
341 
strCat(char * _dst,int32_t _dstSize,const StringView & _str,int32_t _num)342 	int32_t strCat(char* _dst, int32_t _dstSize, const StringView& _str, int32_t _num)
343 	{
344 		return strCat(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) );
345 	}
346 
strFindUnsafe(const char * _str,int32_t _len,char _ch)347 	inline const char* strFindUnsafe(const char* _str, int32_t _len, char _ch)
348 	{
349 		for (int32_t ii = 0; ii < _len; ++ii)
350 		{
351 			if (_str[ii] == _ch)
352 			{
353 				return &_str[ii];
354 			}
355 		}
356 
357 		return NULL;
358 	}
359 
strFind(const char * _str,int32_t _max,char _ch)360 	inline const char* strFind(const char* _str, int32_t _max, char _ch)
361 	{
362 		return strFindUnsafe(_str, strLen(_str, _max), _ch);
363 	}
364 
strFind(const StringView & _str,char _ch)365 	StringView strFind(const StringView& _str, char _ch)
366 	{
367 		const char* ptr = strFindUnsafe(_str.getPtr(), _str.getLength(), _ch);
368 		if (NULL == ptr)
369 		{
370 			return StringView(_str.getTerm(), _str.getTerm() );
371 		}
372 
373 		return StringView(ptr, ptr+1);
374 	}
375 
strRFindUnsafe(const char * _str,int32_t _len,char _ch)376 	inline const char* strRFindUnsafe(const char* _str, int32_t _len, char _ch)
377 	{
378 		for (int32_t ii = _len-1; 0 <= ii; --ii)
379 		{
380 			if (_str[ii] == _ch)
381 			{
382 				return &_str[ii];
383 			}
384 		}
385 
386 		return NULL;
387 	}
388 
strRFind(const StringView & _str,char _ch)389 	StringView strRFind(const StringView& _str, char _ch)
390 	{
391 		const char* ptr = strRFindUnsafe(_str.getPtr(), _str.getLength(), _ch);
392 		if (NULL == ptr)
393 		{
394 			return StringView(_str.getTerm(), _str.getTerm() );
395 		}
396 
397 		return StringView(ptr, ptr+1);
398 	}
399 
400 	template<CharFn fn>
strFind(const char * _str,int32_t _strMax,const char * _find,int32_t _findMax)401 	inline const char* strFind(const char* _str, int32_t _strMax, const char* _find, int32_t _findMax)
402 	{
403 		const char* ptr = _str;
404 
405 		int32_t       stringLen = _strMax;
406 		const int32_t findLen   = _findMax;
407 
408 		for (; stringLen >= findLen; ++ptr, --stringLen)
409 		{
410 			// Find start of the string.
411 			while (fn(*ptr) != fn(*_find) )
412 			{
413 				++ptr;
414 				--stringLen;
415 
416 				// Search pattern lenght can't be longer than the string.
417 				if (findLen > stringLen)
418 				{
419 					return NULL;
420 				}
421 			}
422 
423 			// Set pointers.
424 			const char* string = ptr;
425 			const char* search = _find;
426 
427 			// Start comparing.
428 			while (fn(*string++) == fn(*search++) )
429 			{
430 				// If end of the 'search' string is reached, all characters match.
431 				if ('\0' == *search)
432 				{
433 					return ptr;
434 				}
435 			}
436 		}
437 
438 		return NULL;
439 	}
440 
strFind(const StringView & _str,const StringView & _find,int32_t _num)441 	StringView strFind(const StringView& _str, const StringView& _find, int32_t _num)
442 	{
443 		int32_t len = min(_find.getLength(), _num);
444 
445 		const char* ptr = strFind<toNoop>(
446 			  _str.getPtr()
447 			, _str.getLength()
448 			, _find.getPtr()
449 			, len
450 			);
451 
452 		if (NULL == ptr)
453 		{
454 			return StringView(_str.getTerm(), _str.getTerm() );
455 		}
456 
457 		return StringView(ptr, len);
458 	}
459 
strFindI(const StringView & _str,const StringView & _find,int32_t _num)460 	StringView strFindI(const StringView& _str, const StringView& _find, int32_t _num)
461 	{
462 		int32_t len = min(_find.getLength(), _num);
463 
464 		const char* ptr = strFind<toLower>(
465 			  _str.getPtr()
466 			, _str.getLength()
467 			, _find.getPtr()
468 			, len
469 			);
470 
471 		if (NULL == ptr)
472 		{
473 			return StringView(_str.getTerm(), _str.getTerm() );
474 		}
475 
476 		return StringView(ptr, len);
477 	}
478 
strLTrim(const StringView & _str,const StringView & _chars)479 	StringView strLTrim(const StringView& _str, const StringView& _chars)
480 	{
481 		const char* ptr   = _str.getPtr();
482 		const char* chars = _chars.getPtr();
483 		const uint32_t charsLen = _chars.getLength();
484 
485 		for (uint32_t ii = 0, len = _str.getLength(); ii < len; ++ii)
486 		{
487 			if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) )
488 			{
489 				return StringView(ptr + ii, len-ii);
490 			}
491 		}
492 
493 		return _str;
494 	}
495 
strLTrimSpace(const StringView & _str)496 	StringView strLTrimSpace(const StringView& _str)
497 	{
498 		for (const char* ptr = _str.getPtr(), *term = _str.getTerm(); ptr != term; ++ptr)
499 		{
500 			if (!isSpace(*ptr) )
501 			{
502 				return StringView(ptr, term);
503 			}
504 		}
505 
506 		return StringView(_str.getTerm(), _str.getTerm() );
507 	}
508 
strLTrimNonSpace(const StringView & _str)509 	StringView strLTrimNonSpace(const StringView& _str)
510 	{
511 		for (const char* ptr = _str.getPtr(), *term = _str.getTerm(); ptr != term; ++ptr)
512 		{
513 			if (isSpace(*ptr) )
514 			{
515 				return StringView(ptr, term);
516 			}
517 		}
518 
519 		return StringView(_str.getTerm(), _str.getTerm() );
520 	}
521 
strRTrim(const StringView & _str,const StringView & _chars)522 	StringView strRTrim(const StringView& _str, const StringView& _chars)
523 	{
524 		if (!_str.isEmpty() )
525 		{
526 			const char* ptr = _str.getPtr();
527 			const char* chars = _chars.getPtr();
528 			const uint32_t charsLen = _chars.getLength();
529 
530 			for (int32_t len = _str.getLength(), ii = len - 1; 0 <= ii; --ii)
531 			{
532 				if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]))
533 				{
534 					return StringView(ptr, ii + 1);
535 				}
536 			}
537 		}
538 
539 		return _str;
540 	}
541 
strTrim(const StringView & _str,const StringView & _chars)542 	StringView strTrim(const StringView& _str, const StringView& _chars)
543 	{
544 		return strLTrim(strRTrim(_str, _chars), _chars);
545 	}
546 
547 	constexpr uint32_t kFindStep = 1024;
548 
strFindNl(const StringView & _str)549 	StringView strFindNl(const StringView& _str)
550 	{
551 		StringView str(_str);
552 
553 		for (; str.getPtr() != _str.getTerm()
554 			; str = StringView(min(str.getPtr() + kFindStep, _str.getTerm() ), min(str.getPtr() + kFindStep*2, _str.getTerm() ) )
555 			)
556 		{
557 			StringView eol = strFind(str, "\r\n");
558 			if (!eol.isEmpty() )
559 			{
560 				return StringView(eol.getTerm(), _str.getTerm() );
561 			}
562 
563 			eol = strFind(str, '\n');
564 			if (!eol.isEmpty() )
565 			{
566 				return StringView(eol.getTerm(), _str.getTerm() );
567 			}
568 		}
569 
570 		return StringView(_str.getTerm(), _str.getTerm() );
571 	}
572 
strFindEol(const StringView & _str)573 	StringView strFindEol(const StringView& _str)
574 	{
575 		StringView str(_str);
576 
577 		for (; str.getPtr() != _str.getTerm()
578 			 ; str = StringView(min(str.getPtr() + kFindStep, _str.getTerm() ), min(str.getPtr() + kFindStep*2, _str.getTerm() ) )
579 			)
580 		{
581 			StringView eol = strFind(str, "\r\n");
582 			if (!eol.isEmpty() )
583 			{
584 				return StringView(eol.getPtr(), _str.getTerm() );
585 			}
586 
587 			eol = strFind(str, '\n');
588 			if (!eol.isEmpty() )
589 			{
590 				return StringView(eol.getPtr(), _str.getTerm() );
591 			}
592 		}
593 
594 		return StringView(_str.getTerm(), _str.getTerm() );
595 	}
596 
strSkipWord(const char * _str,int32_t _max)597 	static const char* strSkipWord(const char* _str, int32_t _max)
598 	{
599 		for (char ch = *_str++; 0 < _max && (isAlphaNum(ch) || '_' == ch); ch = *_str++, --_max) {};
600 		return _str-1;
601 	}
602 
strWord(const StringView & _str)603 	StringView strWord(const StringView& _str)
604 	{
605 		const char* ptr  = _str.getPtr();
606 		const char* term = strSkipWord(ptr, _str.getLength() );
607 		return StringView(ptr, term);
608 	}
609 
strFindBlock(const StringView & _str,char _open,char _close)610 	StringView strFindBlock(const StringView& _str, char _open, char _close)
611 	{
612 		const char* curr  = _str.getPtr();
613 		const char* term  = _str.getTerm();
614 		const char* start = NULL;
615 
616 		int32_t count = 0;
617 		for (char ch = *curr; curr != term && count >= 0; ch = *(++curr) )
618 		{
619 			if (ch == _open)
620 			{
621 				if (0 == count)
622 				{
623 					start = curr;
624 				}
625 
626 				++count;
627 			}
628 			else if (ch == _close)
629 			{
630 				--count;
631 
632 				if (NULL == start)
633 				{
634 					break;
635 				}
636 
637 				if (0 == count)
638 				{
639 					return StringView(start, curr+1);
640 				}
641 			}
642 		}
643 
644 		return StringView(term, term);
645 	}
646 
normalizeEolLf(char * _out,int32_t _size,const StringView & _str)647 	StringView normalizeEolLf(char* _out, int32_t _size, const StringView& _str)
648 	{
649 		const char* start = _out;
650 		const char* end   = _out;
651 
652 		if (0 < _size)
653 		{
654 			const char* curr = _str.getPtr();
655 			const char* term = _str.getTerm();
656 			end  = _out + _size;
657 			for (char ch = *curr; curr != term && _out < end; ch = *(++curr) )
658 			{
659 				if ('\r' != ch)
660 				{
661 					*_out++ = ch;
662 				}
663 			}
664 
665 			end = _out;
666 		}
667 
668 		return StringView(start, end);
669 	}
670 
findIdentifierMatch(const StringView & _str,const StringView & _word)671 	StringView findIdentifierMatch(const StringView& _str, const StringView& _word)
672 	{
673 		const int32_t len = _word.getLength();
674 		StringView ptr = strFind(_str, _word);
675 		for (; !ptr.isEmpty(); ptr = strFind(StringView(ptr.getPtr() + len, _str.getTerm() ), _word) )
676 		{
677 			char ch = *(ptr.getPtr() - 1);
678 			if (isAlphaNum(ch) || '_' == ch)
679 			{
680 				continue;
681 			}
682 
683 			ch = *(ptr.getPtr() + len);
684 			if (isAlphaNum(ch) || '_' == ch)
685 			{
686 				continue;
687 			}
688 
689 			return ptr;
690 		}
691 
692 		return StringView(_str.getTerm(), _str.getTerm() );
693 	}
694 
findIdentifierMatch(const StringView & _str,const char ** _words,int32_t _num)695 	StringView findIdentifierMatch(const StringView& _str, const char** _words, int32_t _num)
696 	{
697 		int32_t ii = 0;
698 		for (StringView word = *_words; ii < _num && !word.isEmpty(); ++ii, ++_words, word = *_words)
699 		{
700 			StringView match = findIdentifierMatch(_str, word);
701 			if (!match.isEmpty() )
702 			{
703 				return match;
704 			}
705 		}
706 
707 		return StringView(_str.getTerm(), _str.getTerm() );
708 	}
709 
710 	namespace
711 	{
712 		struct Param
713 		{
Parambx::__anon6c734e5a0111::Param714 			Param()
715 				: width(0)
716 				, base(10)
717 				, prec(INT32_MAX)
718 				, fill(' ')
719 				, bits(0)
720 				, left(false)
721 				, upper(false)
722 				, spec(false)
723 				, sign(false)
724 			{
725 			}
726 
727 			int32_t width;
728 			int32_t base;
729 			int32_t prec;
730 			char fill;
731 			uint8_t bits;
732 			bool left;
733 			bool upper;
734 			bool spec;
735 			bool sign;
736 		};
737 
write(WriterI * _writer,const char * _str,int32_t _len,const Param & _param,Error * _err)738 		static int32_t write(WriterI* _writer, const char* _str, int32_t _len, const Param& _param, Error* _err)
739 		{
740 			int32_t size = 0;
741 			int32_t len = (int32_t)strLen(_str, _len);
742 			int32_t padding = _param.width > len ? _param.width - len : 0;
743 			bool sign = _param.sign && len > 1 && _str[0] != '-';
744 			padding = padding > 0 ? padding - sign : 0;
745 
746 			if (!_param.left)
747 			{
748 				size += writeRep(_writer, _param.fill, padding, _err);
749 			}
750 
751 			if (NULL == _str)
752 			{
753 				size += write(_writer, "(null)", 6, _err);
754 			}
755 			else if (_param.upper)
756 			{
757 				for (int32_t ii = 0; ii < len; ++ii)
758 				{
759 					size += write(_writer, toUpper(_str[ii]), _err);
760 				}
761 			}
762 			else if (sign)
763 			{
764 				size += write(_writer, '+', _err);
765 				size += write(_writer, _str, len, _err);
766 			}
767 			else
768 			{
769 				size += write(_writer, _str, len, _err);
770 			}
771 
772 			if (_param.left)
773 			{
774 				size += writeRep(_writer, _param.fill, padding, _err);
775 			}
776 
777 			return size;
778 		}
779 
write(WriterI * _writer,char _ch,const Param & _param,Error * _err)780 		static int32_t write(WriterI* _writer, char _ch, const Param& _param, Error* _err)
781 		{
782 			return write(_writer, &_ch, 1, _param, _err);
783 		}
784 
write(WriterI * _writer,const char * _str,const Param & _param,Error * _err)785 		static int32_t write(WriterI* _writer, const char* _str, const Param& _param, Error* _err)
786 		{
787 			return write(_writer, _str, _param.prec, _param, _err);
788 		}
789 
write(WriterI * _writer,int32_t _i,const Param & _param,Error * _err)790 		static int32_t write(WriterI* _writer, int32_t _i, const Param& _param, Error* _err)
791 		{
792 			char str[33];
793 			int32_t len = toString(str, sizeof(str), _i, _param.base);
794 
795 			if (len == 0)
796 			{
797 				return 0;
798 			}
799 
800 			return write(_writer, str, len, _param, _err);
801 		}
802 
write(WriterI * _writer,int64_t _i,const Param & _param,Error * _err)803 		static int32_t write(WriterI* _writer, int64_t _i, const Param& _param, Error* _err)
804 		{
805 			char str[33];
806 			int32_t len = toString(str, sizeof(str), _i, _param.base);
807 
808 			if (len == 0)
809 			{
810 				return 0;
811 			}
812 
813 			return write(_writer, str, len, _param, _err);
814 		}
815 
write(WriterI * _writer,uint32_t _u,const Param & _param,Error * _err)816 		static int32_t write(WriterI* _writer, uint32_t _u, const Param& _param, Error* _err)
817 		{
818 			char str[33];
819 			int32_t len = toString(str, sizeof(str), _u, _param.base);
820 
821 			if (len == 0)
822 			{
823 				return 0;
824 			}
825 
826 			return write(_writer, str, len, _param, _err);
827 		}
828 
write(WriterI * _writer,uint64_t _u,const Param & _param,Error * _err)829 		static int32_t write(WriterI* _writer, uint64_t _u, const Param& _param, Error* _err)
830 		{
831 			char str[33];
832 			int32_t len = toString(str, sizeof(str), _u, _param.base);
833 
834 			if (len == 0)
835 			{
836 				return 0;
837 			}
838 
839 			return write(_writer, str, len, _param, _err);
840 		}
841 
write(WriterI * _writer,double _d,const Param & _param,Error * _err)842 		static int32_t write(WriterI* _writer, double _d, const Param& _param, Error* _err)
843 		{
844 			char str[1024];
845 			int32_t len = toString(str, sizeof(str), _d);
846 
847 			if (len == 0)
848 			{
849 				return 0;
850 			}
851 
852 			if (_param.upper)
853 			{
854 				toUpperUnsafe(str, len);
855 			}
856 
857 			const char* dot = strFind(str, INT32_MAX, '.');
858 			if (NULL != dot)
859 			{
860 				const int32_t prec = INT32_MAX == _param.prec ? 6 : _param.prec;
861 				const int32_t precLen = int32_t(
862 						dot
863 						+ uint32_min(prec + _param.spec, 1)
864 						+ prec
865 						- str
866 						);
867 				if (precLen > len)
868 				{
869 					for (int32_t ii = len; ii < precLen; ++ii)
870 					{
871 						str[ii] = '0';
872 					}
873 					str[precLen] = '\0';
874 				}
875 				len = precLen;
876 			}
877 
878 			return write(_writer, str, len, _param, _err);
879 		}
880 
write(WriterI * _writer,const void * _ptr,const Param & _param,Error * _err)881 		static int32_t write(WriterI* _writer, const void* _ptr, const Param& _param, Error* _err)
882 		{
883 			char str[35] = "0x";
884 			int32_t len = toString(str + 2, sizeof(str) - 2, uint32_t(uintptr_t(_ptr) ), 16);
885 
886 			if (len == 0)
887 			{
888 				return 0;
889 			}
890 
891 			len += 2;
892 			return write(_writer, str, len, _param, _err);
893 		}
894 	} // anonymous namespace
895 
write(WriterI * _writer,const StringView & _format,va_list _argList,Error * _err)896 	int32_t write(WriterI* _writer, const StringView& _format, va_list _argList, Error* _err)
897 	{
898 		MemoryReader reader(_format.getPtr(), _format.getLength() );
899 
900 		int32_t size = 0;
901 
902 		while (_err->isOk() )
903 		{
904 			char ch = '\0';
905 
906 			Error err;
907 			read(&reader, ch, &err);
908 
909 			if (!_err->isOk()
910 			||  !err.isOk() )
911 			{
912 				break;
913 			}
914 			else if ('%' == ch)
915 			{
916 				// %[flags][width][.precision][length sub-specifier]specifier
917 				read(&reader, ch);
918 
919 				Param param;
920 
921 				// flags
922 				while (' ' == ch
923 				||     '-' == ch
924 				||     '+' == ch
925 				||     '0' == ch
926 				||     '#' == ch)
927 				{
928 					switch (ch)
929 					{
930 						default:
931 						case ' ': param.fill = ' ';  break;
932 						case '-': param.left = true; break;
933 						case '+': param.sign = true; break;
934 						case '0': param.fill = '0';  break;
935 						case '#': param.spec = true; break;
936 					}
937 
938 					read(&reader, ch);
939 				}
940 
941 				if (param.left)
942 				{
943 					param.fill = ' ';
944 				}
945 
946 				// width
947 				if ('*' == ch)
948 				{
949 					read(&reader, ch);
950 					param.width = va_arg(_argList, int32_t);
951 
952 					if (0 > param.width)
953 					{
954 						param.left  = true;
955 						param.width = -param.width;
956 					}
957 
958 				}
959 				else
960 				{
961 					while (isNumeric(ch) )
962 					{
963 						param.width = param.width * 10 + ch - '0';
964 						read(&reader, ch);
965 					}
966 				}
967 
968 				// .precision
969 				if ('.' == ch)
970 				{
971 					read(&reader, ch);
972 
973 					if ('*' == ch)
974 					{
975 						read(&reader, ch);
976 						param.prec = va_arg(_argList, int32_t);
977 					}
978 					else
979 					{
980 						param.prec = 0;
981 						while (isNumeric(ch) )
982 						{
983 							param.prec = param.prec * 10 + ch - '0';
984 							read(&reader, ch);
985 						}
986 					}
987 				}
988 
989 				// length sub-specifier
990 				while ('h' == ch
991 				||     'I' == ch
992 				||     'l' == ch
993 				||     'j' == ch
994 				||     't' == ch
995 				||     'z' == ch)
996 				{
997 					switch (ch)
998 					{
999 						default: break;
1000 
1001 						case 'j': param.bits = sizeof(intmax_t )*8; break;
1002 						case 't': param.bits = sizeof(size_t   )*8; break;
1003 						case 'z': param.bits = sizeof(ptrdiff_t)*8; break;
1004 
1005 						case 'h': case 'I': case 'l':
1006 							switch (ch)
1007 							{
1008 								case 'h': param.bits = sizeof(short int)*8; break;
1009 								case 'l': param.bits = sizeof(long int )*8; break;
1010 								default: break;
1011 							}
1012 
1013 							read(&reader, ch);
1014 							switch (ch)
1015 							{
1016 								case 'h': param.bits = sizeof(signed char  )*8; break;
1017 								case 'l': param.bits = sizeof(long long int)*8; break;
1018 								case '3':
1019 								case '6':
1020 									read(&reader, ch);
1021 									switch (ch)
1022 									{
1023 										case '2': param.bits = sizeof(int32_t)*8; break;
1024 										case '4': param.bits = sizeof(int64_t)*8; break;
1025 										default: break;
1026 									}
1027 									break;
1028 
1029 								default: seek(&reader, -1); break;
1030 							}
1031 							break;
1032 					}
1033 
1034 					read(&reader, ch);
1035 				}
1036 
1037 				// specifier
1038 				switch (toLower(ch) )
1039 				{
1040 					case 'c':
1041 						size += write(_writer, char(va_arg(_argList, int32_t) ), param, _err);
1042 						break;
1043 
1044 					case 's':
1045 						size += write(_writer, va_arg(_argList, const char*), param, _err);
1046 						break;
1047 
1048 					case 'o':
1049 						param.base = 8;
1050 						switch (param.bits)
1051 						{
1052 						default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break;
1053 						case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break;
1054 						}
1055 						break;
1056 
1057 					case 'i':
1058 					case 'd':
1059 						param.base = 10;
1060 						switch (param.bits)
1061 						{
1062 						default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break;
1063 						case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break;
1064 						};
1065 						break;
1066 
1067 					case 'e':
1068 					case 'f':
1069 					case 'g':
1070 						param.upper = isUpper(ch);
1071 						size += write(_writer, va_arg(_argList, double), param, _err);
1072 						break;
1073 
1074 					case 'p':
1075 						size += write(_writer, va_arg(_argList, void*), param, _err);
1076 						break;
1077 
1078 					case 'x':
1079 						param.base  = 16;
1080 						param.upper = isUpper(ch);
1081 						switch (param.bits)
1082 						{
1083 						default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break;
1084 						case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break;
1085 						}
1086 						break;
1087 
1088 					case 'u':
1089 						param.base = 10;
1090 						switch (param.bits)
1091 						{
1092 						default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break;
1093 						case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break;
1094 						}
1095 						break;
1096 
1097 					default:
1098 						size += write(_writer, ch, _err);
1099 						break;
1100 				}
1101 			}
1102 			else
1103 			{
1104 				size += write(_writer, ch, _err);
1105 			}
1106 		}
1107 
1108 		return size;
1109 	}
1110 
write(WriterI * _writer,Error * _err,const StringView * _format,...)1111 	int32_t write(WriterI* _writer, Error* _err, const StringView* _format, ...)
1112 	{
1113 		va_list argList;
1114 		va_start(argList, _format);
1115 		int32_t total = write(_writer, *_format, argList, _err);
1116 		va_end(argList);
1117 		return total;
1118 	}
1119 
write(WriterI * _writer,Error * _err,const char * _format,...)1120 	int32_t write(WriterI* _writer, Error* _err, const char* _format, ...)
1121 	{
1122 		va_list argList;
1123 		va_start(argList, _format);
1124 		int32_t total = write(_writer, _format, argList, _err);
1125 		va_end(argList);
1126 		return total;
1127 	}
1128 
vsnprintf(char * _out,int32_t _max,const char * _format,va_list _argList)1129 	int32_t vsnprintf(char* _out, int32_t _max, const char* _format, va_list _argList)
1130 	{
1131 		if (1 < _max)
1132 		{
1133 			StaticMemoryBlockWriter writer(_out, uint32_t(_max) );
1134 
1135 			Error err;
1136 			va_list argListCopy;
1137 			va_copy(argListCopy, _argList);
1138 			int32_t size = write(&writer, _format, argListCopy, &err);
1139 			va_end(argListCopy);
1140 
1141 			if (err.isOk() )
1142 			{
1143 				size += write(&writer, '\0', &err);
1144 				return size - 1 /* size without '\0' terminator */;
1145 			}
1146 			else
1147 			{
1148 				_out[_max-1] = '\0';
1149 			}
1150 		}
1151 
1152 		Error err;
1153 		SizerWriter sizer;
1154 		va_list argListCopy;
1155 		va_copy(argListCopy, _argList);
1156 		int32_t total = write(&sizer, _format, argListCopy, &err);
1157 		va_end(argListCopy);
1158 
1159 		return total;
1160 	}
1161 
snprintf(char * _out,int32_t _max,const char * _format,...)1162 	int32_t snprintf(char* _out, int32_t _max, const char* _format, ...)
1163 	{
1164 		va_list argList;
1165 		va_start(argList, _format);
1166 		int32_t total = vsnprintf(_out, _max, _format, argList);
1167 		va_end(argList);
1168 
1169 		return total;
1170 	}
1171 
vprintf(const char * _format,va_list _argList)1172 	int32_t vprintf(const char* _format, va_list _argList)
1173 	{
1174 		Error err;
1175 		va_list argListCopy;
1176 		va_copy(argListCopy, _argList);
1177 		int32_t total = write(getStdOut(), _format, argListCopy, &err);
1178 		va_end(argListCopy);
1179 
1180 		return total;
1181 	}
1182 
printf(const char * _format,...)1183 	int32_t printf(const char* _format, ...)
1184 	{
1185 		va_list argList;
1186 		va_start(argList, _format);
1187 		int32_t total = vprintf(_format, argList);
1188 		va_end(argList);
1189 
1190 		return total;
1191 	}
1192 
1193 	static const char s_units[] = { 'B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
1194 
1195 	template<uint32_t Kilo, char KiloCh0, char KiloCh1, CharFn fn>
prettify(char * _out,int32_t _count,uint64_t _value)1196 	inline int32_t prettify(char* _out, int32_t _count, uint64_t _value)
1197 	{
1198 		uint8_t idx = 0;
1199 		double value = double(_value);
1200 		while (_value != (_value&0x7ff)
1201 		&&     idx < BX_COUNTOF(s_units) )
1202 		{
1203 			_value /= Kilo;
1204 			value  *= 1.0/double(Kilo);
1205 			++idx;
1206 		}
1207 
1208 		return snprintf(_out, _count, "%0.2f %c%c%c", value
1209 			, fn(s_units[idx])
1210 			, idx > 0 ? KiloCh0 : '\0'
1211 			, KiloCh1
1212 			);
1213 	}
1214 
prettify(char * _out,int32_t _count,uint64_t _value,Units::Enum _units)1215 	int32_t prettify(char* _out, int32_t _count, uint64_t _value, Units::Enum _units)
1216 	{
1217 		if (Units::Kilo == _units)
1218 		{
1219 			return prettify<1000, 'B', '\0', toNoop>(_out, _count, _value);
1220 		}
1221 
1222 		return prettify<1024, 'i', 'B', toUpper>(_out, _count, _value);
1223 	}
1224 
1225 } // namespace bx
1226