inline S change_windows_replacement_parameters_to_fastformat(S const& str) { typedef ss_typename_type_k S::const_iterator iter_t; enum state_t { normal, percent, number } state = normal; S str2; int index = 0; str2.reserve(str.size() + 10); { for(iter_t begin = str.begin(); begin != str.end(); ++begin) { bool isNumber = false; switch(*begin) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': isNumber = true; } if( number == state && !isNumber) { ff_char_t parameter[32]; int cch = fastformat_util_snprintf(¶meter[0], STLSOFT_NUM_ELEMENTS(parameter), FASTFORMAT_LITERAL_STRING("{%d}"), index - 1); str2.append(parameter, size_t(cch)); str2.append(1u, *begin); state = normal; } else { switch(*begin) { case '%': switch(state) { default: case number: FASTFORMAT_CONTRACT_ENFORCE_UNEXPECTED_CONDITION_INTERNAL("unexpected state"); case normal: state = percent; break; case percent: state = normal; str2.append(1, '%'); break; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': switch(state) { default: FASTFORMAT_CONTRACT_ENFORCE_UNEXPECTED_CONDITION_INTERNAL("unexpected state"); case normal: str2.append(1, *begin); break; case percent: state = number; index = (*begin - '0'); break; case number: index = (10 * index) + (*begin - '0'); break; } break; default: state = normal; str2.append(1, *begin); break; } } }} if(number == state) { ff_char_t parameter[32]; int cch = fastformat_util_snprintf(¶meter[0], STLSOFT_NUM_ELEMENTS(parameter), FASTFORMAT_LITERAL_STRING("{%d}"), index - 1); str2.append(parameter, size_t(cch)); } return str2; }
FASTFORMAT_CALL(size_t) fastformat_fillReplacements( string_slice_t* resultElements // Pointer to receiver of result slices , format_element_t const* formatElements // Pointer to format elements , size_t numFormatElements , string_slice_t const* arguments // Pointer to arguments , size_t numArguments , fastformat_mismatchedHandler_t handler , void* param , size_t* pnumResultElements ) { FASTFORMAT_COVER_MARK_ENTRY(); FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_API(NULL != resultElements, "result elements may not be null"); FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_API(NULL != pnumResultElements, "result element count pointer may not be null"); FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_API((NULL != formatElements || 0 == numFormatElements), "pattern elements may not be null, unless numFormatElements is 0"); FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_API((0 != numArguments) == (NULL != arguments), "arguments can only be null when there are 0 arguments, and vice versa"); mismatched_handler_info_t handler_info = { handler, param }; size_t cchTotal = 0; string_slice_t defaultSlice = { 0, NULL }; int handlerReturned = 0; string_slice_t* const resultElementsBase = resultElements; #ifndef FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS stlsoft::auto_buffer<unreferenced_argument_flag_t> argumentReferenceFlags(numArguments); std::fill(argumentReferenceFlags.begin(), argumentReferenceFlags.end(), 0); #endif /* !FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS */ if(NULL == handler_info.handler) { FASTFORMAT_COVER_MARK_ENTRY(); handler_info = fastformat_getThreadMismatchedHandler(); } if(NULL == handler_info.handler) { FASTFORMAT_COVER_MARK_ENTRY(); handler_info = fastformat_getProcessMismatchedHandler(); } if(NULL == handler_info.handler) { FASTFORMAT_COVER_MARK_ENTRY(); handler_info = fastformat_getDefaultMismatchedHandler(); } // Process every format element: // // - if literal, copy over // - if replacement, lookup argument and copy over/report mismatch // - if spacer, evaluate whether spacing needed, and then { for(size_t i = 0; i != numFormatElements; ++i, ++resultElements) { FASTFORMAT_COVER_MARK_ENTRY(); format_element_t const& pattern_element = formatElements[i]; if(FASTFORMAT_INTERNAL_FORMAT_ELEMENT_INDEX_LITERAL_ == pattern_element.index) { // A literal fragment, so just copy into destination FASTFORMAT_COVER_MARK_ENTRY(); resultElements->len = pattern_element.len; resultElements->ptr = pattern_element.ptr; cchTotal += pattern_element.len; } else { // A replacement parameter, so work out whether to truncate or // to insert any fill slices (before, after, or both) // // 1. If a maximum width is specified and the source slice // length is greater than the maximum, then we truncate // 2. If a minimum width is specified and the source slice // length is less than the minimum, then we insert one or // more slices: // 2.a. If left-aligned, then we insert a pad slice after // the current slice // 2.b. If right-aligned, then we insert a pad slice before // the current slice // 2.c. If centre-aligned, then we insert pad slices before // and after the current slice // 3. If none of these are satisfied, then copy over the // source slice as-is // But, first, we need to validate the index if(pattern_element.index >= static_cast<int>(numArguments)) { // Pattern element index is out of range, e.g. fmtln(sink, "val0={2}", v0, v1); FASTFORMAT_COVER_MARK_ENTRY(); FASTFORMAT_CONTRACT_ENFORCE_ASSUMPTION(NULL != handler_info.handler); if(handlerReturned >= 0) { handlerReturned = (*handler_info.handler)(handler_info.param, FF_REPLACEMENTCODE_MISSING_ARGUMENT, numArguments, pattern_element.index, &defaultSlice, NULL, 0, NULL); } // Handler has returned, so no exception has been thrown // // If 0, insert the pattern_element, otherwise insert the // default slice. if(handlerReturned != 0) { FASTFORMAT_COVER_MARK_ENTRY(); #if 0 // We cannot take the default slice, because the application has // not provided any space for us to write it, so ... resultElements->len = defaultSlice.len; resultElements->ptr = defaultSlice.ptr; #else /* ? 0 */ // ... we skip it (by decrementing in anticipation of the increment // as we loop round again). --resultElements; #endif /* 0 */ } else { FASTFORMAT_COVER_MARK_ENTRY(); resultElements->len = pattern_element.len; resultElements->ptr = pattern_element.ptr; } } else { string_slice_t const& src_slice = arguments[pattern_element.index]; ff_char_t const* (FASTFORMAT_CALLCONV* pfnFill)(size_t) = ('#' == pattern_element.fill) ? fastformat_getHashesSlice : fastformat_getSpacesSlice; if( pattern_element.maxWidth >= 0 && size_t(pattern_element.maxWidth) < src_slice.len) { // Truncating, so see if we're truncating left/centre/right resultElements->len = pattern_element.maxWidth; switch(pattern_element.alignment) { default: FASTFORMAT_CONTRACT_ENFORCE_UNEXPECTED_CONDITION_INTERNAL("invalid enumerator"); case FASTFORMAT_ALIGNMENT_NONE: case FASTFORMAT_ALIGNMENT_RIGHT: resultElements->ptr = src_slice.ptr + (src_slice.len - pattern_element.maxWidth); break; case FASTFORMAT_ALIGNMENT_CENTRE: resultElements->ptr = src_slice.ptr + (src_slice.len - pattern_element.maxWidth) / 2; break; case FASTFORMAT_ALIGNMENT_LEFT: resultElements->ptr = src_slice.ptr; break; } cchTotal += pattern_element.maxWidth; } else if(0 != pattern_element.minWidth && size_t(pattern_element.minWidth) > src_slice.len) { switch(pattern_element.alignment) { default: FASTFORMAT_CONTRACT_ENFORCE_UNEXPECTED_CONDITION_INTERNAL("invalid enumerator"); case FASTFORMAT_ALIGNMENT_NONE: case FASTFORMAT_ALIGNMENT_RIGHT: // insert a spaces slice before the source slice resultElements->len = size_t(pattern_element.minWidth) - src_slice.len; resultElements->ptr = pfnFill(resultElements->len); ++resultElements; resultElements->len = src_slice.len; resultElements->ptr = src_slice.ptr; break; case FASTFORMAT_ALIGNMENT_CENTRE: // insert a spaces slice before and after the source slice resultElements->len = (size_t(pattern_element.minWidth) - src_slice.len) / 2; resultElements->ptr = pfnFill(resultElements->len); ++resultElements; resultElements->len = src_slice.len; resultElements->ptr = src_slice.ptr; ++resultElements; resultElements->len = size_t(pattern_element.minWidth) - (resultElements[-1].len + resultElements[-2].len); resultElements->ptr = pfnFill(resultElements->len); break; case FASTFORMAT_ALIGNMENT_LEFT: // insert a spaces slice after the source slice resultElements->len = src_slice.len; resultElements->ptr = src_slice.ptr; ++resultElements; resultElements->len = size_t(pattern_element.minWidth) - src_slice.len; resultElements->ptr = pfnFill(resultElements->len); break; } cchTotal += pattern_element.minWidth; } else { // Not truncating, so just copy over resultElements->len = src_slice.len; resultElements->ptr = src_slice.ptr; cchTotal += src_slice.len; } } #ifndef FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS // Have to do a runtime test here, in case where additional // arguments are specified (and exception suppressed by use of // fastformat::ignore_missing_arguments_scope). if(size_t(pattern_element.index) < argumentReferenceFlags.size()) { argumentReferenceFlags[size_t(pattern_element.index)] = 1; } #endif /* !FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS */ } }} #ifndef FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS stlsoft::auto_buffer<unreferenced_argument_flag_t>::iterator it; if(argumentReferenceFlags.end() != (it = std::find(argumentReferenceFlags.begin(), argumentReferenceFlags.end(), 0))) { int firstMismatchedReplacementIndex = static_cast<int>(it - argumentReferenceFlags.begin()); FASTFORMAT_CONTRACT_ENFORCE_ASSUMPTION(NULL != handler_info.handler); (*handler_info.handler)(handler_info.param, FF_REPLACEMENTCODE_UNREFERENCED_ARGUMENT, numArguments, firstMismatchedReplacementIndex, NULL, NULL, 0, NULL); } #endif /* !FASTFORMAT_DO_NOT_DETECT_UNREFERENCED_ARGUMENTS */ if(0 != handlerReturned) { FASTFORMAT_COVER_MARK_ENTRY(); FASTFORMAT_CONTRACT_ENFORCE_ASSUMPTION(NULL != handler_info.handler); (*handler_info.handler)(handler_info.param, FF_REPLACEMENTCODE_SUCCESS, numArguments, -1, NULL, NULL, 0, NULL); } *pnumResultElements = static_cast<size_t>(resultElements - resultElementsBase); return cchTotal; }