| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <EAStdC/internal/Config.h>
- #include <EAStdC/internal/SprintfCore.h>
- #include <EAStdC/EASprintfOrdered.h>
- #include <EAStdC/EASprintf.h>
- #include <EAStdC/EABitTricks.h>
- #include <EAAssert/eaassert.h>
- #include <stdarg.h>
- #include <string.h>
- #ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable: 4127) // conditional expression is constant.
- #endif
- // GCC has va_copy, but VC++ does not. However, VC++ implements
- // va_list in such a way that it can simply be memcpyd.
- #ifndef va_copy
- #ifdef __va_copy
- #define va_copy(dest, src) __va_copy((dest), (src))
- #else
- #define va_copy(dest, src) memcpy(&(dest), &(src), sizeof(va_list))
- #endif
- #endif
- namespace EA
- {
- namespace StdC
- {
- namespace SprintfLocal
- {
- static const int kSpanFormatCapacity = 16;
- struct Span8
- {
- const char8_t* mpBegin; // The first char in the span.
- const char8_t* mpEnd; // One-past the last used char.
- Modifier mType; // This tells us what the type of the argument is (e.g. kModifierInt).
- AllTypes mValue; // This stores the value, which is of type mType.
- char8_t mFormat[kSpanFormatCapacity]; // The format to use (e.g. %5.3f). If empty then this is a string span.
- char8_t mFormatChar; // The last char in the mFormat string.
- int mUserIndex; // The index the user assigned to this format. Negative value if this is a string span.
- bool mbEscapePresent; // True if the span is a string and it has a %% sequence. We can optimize writes if we know that it doesn't.
- Span8() : mpBegin(NULL),
- mpEnd(NULL),
- mType(kModifierNone),
- mValue(),
- mFormatChar(0),
- mUserIndex(0),
- mbEscapePresent(false)
- { mFormat[0] = 0; }
- };
- struct Span16
- {
- const char16_t* mpBegin; // The first char in the span.
- const char16_t* mpEnd; // One-past the last used char.
- Modifier mType; // This tells us what the type of the argument is (e.g. kModifierInt).
- AllTypes mValue; // This stores the value, which is of type mType.
- char16_t mFormat[kSpanFormatCapacity]; // The format to use (e.g. %5.3f). If empty then this is a string span.
- char16_t mFormatChar; // The last char in the mFormat string.
- int mUserIndex; // The index the user assigned to this format. Negative value if this is a string span.
- bool mbEscapePresent; // True if the span is a string and it has a %% sequence. We can optimize writes if we know that it doesn't.
- Span16(): mpBegin(NULL),
- mpEnd(NULL),
- mType(kModifierNone),
- mValue(),
- mFormatChar(0),
- mUserIndex(0),
- mbEscapePresent(false)
- { mFormat[0] = 0; }
- };
- struct Span32
- {
- const char32_t* mpBegin; // The first char in the span.
- const char32_t* mpEnd; // One-past the last used char.
- Modifier mType; // This tells us what the type of the argument is (e.g. kModifierInt).
- AllTypes mValue; // This stores the value, which is of type mType.
- char32_t mFormat[kSpanFormatCapacity]; // The format to use (e.g. %5.3f). If empty then this is a string span.
- char32_t mFormatChar; // The last char in the mFormat string.
- int mUserIndex; // The index the user assigned to this format. Negative value if this is a string span.
- bool mbEscapePresent; // True if the span is a string and it has a %% sequence. We can optimize writes if we know that it doesn't.
- Span32(): mpBegin(NULL),
- mpEnd(NULL),
- mType(kModifierNone),
- mValue(),
- mFormatChar(0),
- mUserIndex(0),
- mbEscapePresent(false)
- { mFormat[0] = 0; }
- };
- // This function exists for the sole purpose of passing an arbitrary argument to VprintfCore
- // along with an existing WriteFunction8/pWriteFunctionContext8.
- static int CallVprintfCore(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pWriteFunctionContext8, const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return VprintfCore(pWriteFunction8, pWriteFunctionContext8, pFormat, arguments);
- }
- static int CallVprintfCore(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pWriteFunctionContext16, const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return VprintfCore(pWriteFunction16, pWriteFunctionContext16, pFormat, arguments);
- }
- static int CallVprintfCore(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pWriteFunctionContext32, const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return VprintfCore(pWriteFunction32, pWriteFunctionContext32, pFormat, arguments);
- }
- // This function is a copy of the 16 bit version below.
- //
- // The way this function works is as follows: We walk through the pFormat string and identify all
- // format spans and non-format spans (i.e. literal text) between format spans. Thus for the string
- // " %1:f %0:d " we have five spans: three non-format spans, and two format spans. The we
- // read the va_list arguments in the user-specified order into a union that can hold any type.
- // It's important that we read the arguments in user-specified order and not format string order.
- // So the %d would be read first as an int, and the %f would be read second as a double.
- // Finally we walk through the format in string order and write out each span to the output.
- // Non-format segments are simply copied to the output. Format segments are written to the output
- // by a call to VPrintfCore each.
- //
- static int OVprintfCore(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pWriteFunctionContext8, const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- const int kArgCapacity = 10; // Currently only single digit ('0'-'9') order values are supported.
- const int kSpanCapacity = 21; // Worst case scenario of 21 spans. For example: " %2:d %7:d %1:d %6:d %3:d %5:d %4:d %0:d %8:d %9:d " or "%0:d%1:d%2:d%3:d%4:d%5:d%6:d%7:d%8:d%9:d"
- Span8 spans[kSpanCapacity];
- int spanArgOrder[kArgCapacity] = { -1 }; // Each entry is the index into 'spans' of that argument. This allows us to quickly find formats in the order the user passed them to this function. For the example directly above, the contents would be: 5, 1, 9, 13, 11, 7, 3, 17, 15. We initialize it to -1 in order to avoid compiler warnings about it being used before set.
- int spanIndex = 0;
- int formattedSpanCount = 0;
- bool bInFormat = false; // State variable indicating if we are within a % format sequence.
- int nFormatLength = 0;
- int nWriteCountSum = 0;
- int startIndex = 1; // This is 1 or 0, and it defaults to 1 (but may change below) in order to mean that user formats start at 1, as in "%1:d". However, we have a feature whereby we detect that the user is using %0 as the start index.
- const char8_t* p;
- const char8_t* pEnd;
- int result;
- static_assert((EAArrayCount(spans) == kSpanCapacity) && (EAArrayCount(spanArgOrder) == kArgCapacity), "spans and spanArgOrder are not using constants for their array size.");
- #ifdef EA_DEBUG
- for(int s = 0; s < kArgCapacity; ++s)
- spanArgOrder[s] = -1;
- #else
- memset(spanArgOrder, 0, sizeof(spanArgOrder));
- #endif
- pWriteFunction8(NULL, 0, pWriteFunctionContext8, kWFSBegin);
- // Initialize the first span. We always have a beginning sequence that is
- // a string, even if it is empty. Actually, there may be an empty string
- // span between any two format spans. We'll ignore them later.
- spans[0].mpBegin = pFormat;
- spans[0].mUserIndex = -1;
- // Build the list of spans.
- // Read each format string character while maintaining a little state machine.
- for(p = pFormat; *p; ++p)
- {
- if(*p == '%')
- {
- // A % char within a format is invalid (though %% is valid), and any '%' char that
- // begins a format must be followed by at least three more chars, two for the user
- // index plus colon and at least one for the actual format (e.g. %4:d).
- EA_ASSERT(!bInFormat && p[1] && p[2] && p[3]);
- if(p[1] == '%')
- {
- spans[spanIndex].mbEscapePresent = true;
- p++; // Skip past the second % char.
- }
- else // else we don't have a %% sequence and thus have the start of a format...
- {
- // Finalize the current span (the one before the % char), before starting a new span for this % sequence.
- spans[spanIndex].mpEnd = p;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = 0; // This is redundant.
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- if((p[1] < '0') || (p[1] > '9'))
- return -1; // Invalid format. User specified a format like "%X:d" or just "%"
- const int userIndex = (int)(p[1] - '0');
- if((userIndex == 0) && (startIndex != 0)) // If it appears that the user is using argument numbering that is 0-based (e.g. "%0:d") instead of 1-based...
- {
- startIndex = 0;
- for(int i = kArgCapacity - 1; i > 0; --i)
- spanArgOrder[i] = spanArgOrder[i - 1]; // Convert any existing indexes from what we originally assumed to be 1-based values 'up' to being 0-based values.
- }
- bInFormat = true;
- nFormatLength = 1; // For the % char we write into mFormat.
- spans[spanIndex].mpBegin = p;
- spans[spanIndex].mFormat[0] = '%';
- spans[spanIndex].mUserIndex = userIndex; // We don't write the user index or the colon into the format string, which is a standard C format specifier sequence.
- spanArgOrder[userIndex - startIndex] = spanIndex; // startIndex is normally 1, because usually users specify argument indexes in a 1:based way.
- formattedSpanCount++;
- EA_ASSERT(p[2] == ':'); // We expect formats to have a N: sequence after the % in order to indicate order (e.g. %4:3.1f means %3.1f as 4th argument)
- if(p[2] != ':')
- return -1; // Invalid format. User specified a format like "%00:d" or just "%0"
- p += 2; // Skip past the user index and the colon.
- }
- }
- else if(bInFormat)
- {
- EA_ASSERT(nFormatLength < kSpanFormatCapacity);
- if(nFormatLength < kSpanFormatCapacity)
- spans[spanIndex].mFormat[nFormatLength++] = *p;
- else
- return -1; // Invalid format. User specified a format like "%0:000000000000001d" (too long a printf format)
- switch(*p)
- {
- case 'b': case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': // If this character is the last posible character of a format specifier...
- case 'g': case 'G': case 'e': case 'E': case 'f': case 'F': case 'a':
- case 'A': case 'p': case 'c': case 'C': case 's': case 'S': case 'n':
- {
- // Finalize the current span.
- spans[spanIndex].mpEnd = p + 1;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = *p;
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- bInFormat = false;
- nFormatLength = 0;
- spans[spanIndex].mpBegin = p + 1;
- spans[spanIndex].mUserIndex = -1; // This is a string span. If a format sequence or end-of-string immediately follows then this will be an empty string span.
- break;
- }
- default:
- // To do: Handle the case of an invalid format character.
- break;
- }
- }
- // Else we are in the middle of a string portion of the format and we need do nothing here.
- }
- // Finalize the last span, which by definition ends at the end of our input.
- EA_ASSERT(spanIndex < kSpanCapacity);
- if((spanIndex == kSpanCapacity) && *p) // If the user somehow specified more format spans than possible...
- return -1; // Invalid format. Too many format spans.
- spans[spanIndex].mpEnd = p; // p Should always point to the terminating 0 char of pFormat.
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spanIndex++;
- // Now we read the arguments into span[s].mValue in the order they were passed by the caller.
- for(int i = 0; i < formattedSpanCount; ++i)
- {
- EA_ASSERT((spanArgOrder[i] >= 0) && (spanArgOrder[i] < kSpanCapacity));
- Span8& span = spans[spanArgOrder[i]];
- FormatData formatData;
- // We call ReadFormat in order to get the argument type. We don't need the other information from FormatData.
- // ReadFormat returns the pointer to the next char after the format sequence. If there is an error then it
- // returns a pointer to where it failed. We can use this to tell if ReadFormat failed by testing *pEnd == 0.
- pEnd = ReadFormat(span.mFormat, &formatData, (va_list*)&arguments);
- if(*pEnd != 0) // If ReadFormat bailed before processing the entire format...
- return -1;
- // Unfortunately, ReadFormat tells us the type only if it is a modified type (e.g. %ld as opposed to %d).
- // We could go and modify ReadFormat to store the full type all the time, but that would be messing with
- // that function and its performance and we'd rather stay away from that. We can solve this with a simple
- // switch statement here.
- if(formatData.mModifier == kModifierNone)
- {
- switch (pEnd[-1])
- {
- case 'b':
- case 'd':
- case 'i':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- formatData.mModifier = kModifierInt;
- break;
- case 'g':
- case 'G':
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'a':
- case 'A':
- formatData.mModifier = kModifierDouble;
- break;
- case 'p':
- case 's':
- case 'S':
- case 'n':
- EA_COMPILETIME_ASSERT(sizeof(size_t) == sizeof(void*)); // If this fails then we need to modify this statement.
- formatData.mModifier = kModifierSize_t;
- break;
- case 'c':
- formatData.mModifier = kModifierChar;
- break;
- case 'C':
- formatData.mModifier = kModifierWChar;
- break;
- default:
- EA_FAIL_M("EAStdC OVprintfCore"); // This shouldn't occur unless ReadFormat started supporting some new format that we aren't yet aware of and it is being used here.
- break;
- }
- }
-
- span.mType = formatData.mModifier;
- switch (span.mType)
- {
- case kModifierChar:
- span.mValue.mChar = (char)va_arg(arguments, int); // Recall that C++ promotes types less than int to int.
- break;
- case kModifierShort:
- span.mValue.mShort = (short)va_arg(arguments, int);
- break;
- case kModifierInt:
- span.mValue.mInt = va_arg(arguments, int);
- break;
- case kModifierLong:
- span.mValue.mLong = va_arg(arguments, long);
- break;
- case kModifierLongLong:
- span.mValue.mLongLong = va_arg(arguments, long long);
- break;
- case kModifierMax_t:
- span.mValue.mMax = va_arg(arguments, intmax_t);
- break;
- case kModifierSize_t:
- span.mValue.mSize = va_arg(arguments, size_t);
- break;
- case kModifierPtrdiff_t:
- span.mValue.mPtrDiff = va_arg(arguments, ptrdiff_t);
- break;
- case kModifierDouble:
- span.mValue.mDouble = va_arg(arguments, double);
- break;
- case kModifierLongDouble:
- span.mValue.mLongDouble = va_arg(arguments, long double);
- break;
- case kModifierWChar:
- span.mValue.mWChar = (wchar_t)va_arg(arguments, unsigned int);
- break;
- case kModifierInt8:
- span.mValue.mInt8 = (int8_t)va_arg(arguments, int);
- break;
- case kModifierInt16:
- span.mValue.mInt16 = (int16_t)va_arg(arguments, int);
- break;
- case kModifierInt32:
- span.mValue.mInt32 = va_arg(arguments, int32_t);
- break;
- case kModifierInt64:
- span.mValue.mInt64 = va_arg(arguments, int64_t);
- break;
- case kModifierInt128:
- span.mValue.mLongLong = 0;
- EA_FAIL_M("EAStdC OVprintfCore"); // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- // If this occurs, then our ReadFormat function seems to have a bug. We already have an assertion failure for this case above.
- span.mValue.mLongLong = 0;
- break;
- }
- }
- // Now we have an array of spans. Now we print the spans one by one.
- for(int s = 0; s < spanIndex; ++s)
- {
- const Span8& span = spans[s];
- if(span.mpEnd != span.mpBegin) // If non-empty...
- {
- if(span.mUserIndex >= 0) // If this is a format span as opposed to a string span...
- {
- switch (span.mType)
- {
- case kModifierChar:
- // We can't call VprintfCore directly because it expects a va_list, whereas we have just a single value.
- // We can't call Sprintf because we need to pass the current pWriteFunction, pWriteFunctionContext.
- // To do: We have a problem here: VprintfCore will call the write function
- // with kWFSBegin, kWFSIntermediate, and kWFSEnd. We need it to use
- // just kFSIntermediate. Currently this affects only writing on
- // some mobile platforms to their custom log formats and it's unlikely
- // anybody will ever use ordered sprintf to such a destination.
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mChar);
- break;
- case kModifierShort:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mShort);
- break;
- case kModifierInt:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mInt);
- break;
- case kModifierLong:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mLong);
- break;
- case kModifierLongLong:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mLongLong);
- break;
- case kModifierMax_t:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mMax);
- break;
- case kModifierSize_t:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mSize);
- break;
- case kModifierPtrdiff_t:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mPtrDiff);
- break;
- case kModifierDouble:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mDouble);
- break;
- case kModifierLongDouble:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mLongDouble);
- break;
- case kModifierWChar:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mWChar);
- break;
- case kModifierInt8:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mInt8);
- break;
- case kModifierInt16:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mInt16);
- break;
- case kModifierInt32:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mInt32);
- break;
- case kModifierInt64:
- result = CallVprintfCore(pWriteFunction8, pWriteFunctionContext8, span.mFormat, span.mValue.mInt64);
- break;
- case kModifierInt128:
- result = -1; // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- result = -1; // If this fails, then our ReadFormat function seems to have a bug.
- break;
- }
- if(result < 0)
- return -1;
- nWriteCountSum += result;
- }
- else
- {
- // We simply copy the span to the pWriteFunction, while taking care of %% escape sequences.
- p = span.mpBegin;
- pEnd = span.mpEnd;
- if(span.mbEscapePresent) // If somewhere in the string span there is at least one %% sequence, we must copy the slow way: one by one.
- {
- for(result = 1; (result >= 0) && (p < pEnd); ++p)
- {
- if(pWriteFunction8(p, 1, pWriteFunctionContext8, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += result;
- }
- }
- else
- {
- if(pWriteFunction8(p, (size_t)(pEnd - p), pWriteFunctionContext8, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += (int)(pEnd - p);
- }
- }
- }
- }
- pWriteFunction8(NULL, 0, pWriteFunctionContext8, kWFSEnd);
- return nWriteCountSum;
- }
- // This function is a copy of the 8 bit version above.
- static int OVprintfCore(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pWriteFunctionContext16, const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- const int kArgCapacity = 10; // Currently only single digit ('0'-'9') order values are supported.
- const int kSpanCapacity = 21; // Worst case scenario of 21 spans. For example: " %2:d %7:d %1:d %6:d %3:d %5:d %4:d %0:d %8:d %9:d " or "%0:d%1:d%2:d%3:d%4:d%5:d%6:d%7:d%8:d%9:d"
- Span16 spans[kSpanCapacity];
- int spanArgOrder[kArgCapacity] = { -1 }; // Each entry is the index into 'spans' of that argument. This allows us to quickly find formats in the order the user passed them to this function. For the example directly above, the contents would be: 5, 1, 9, 13, 11, 7, 3, 17, 15. We initialize it to -1 in order to avoid compiler warnings about it being used before set.
- int spanIndex = 0;
- int formattedSpanCount = 0;
- bool bInFormat = false; // State variable indicating if we are within a % format sequence.
- int nFormatLength = 0;
- int nWriteCountSum = 0;
- int startIndex = 1; // This is 1 or 0, and it defaults to 1 (but may change below) in order to mean that user formats start at 1, as in "%1:d". However, we have a feature whereby we detect that the user is using %0 as the start index.
- const char16_t* p;
- const char16_t* pEnd;
- int result;
- static_assert((EAArrayCount(spans) == kSpanCapacity) && (EAArrayCount(spanArgOrder) == kArgCapacity), "spans and spanArgOrder are not using constants for their array size.");
- #ifdef EA_DEBUG
- for(int s = 0; s < kArgCapacity; ++s)
- spanArgOrder[s] = -1;
- #else
- memset(spanArgOrder, 0, sizeof(spanArgOrder));
- #endif
- pWriteFunction16(NULL, 0, pWriteFunctionContext16, kWFSBegin);
- // Initialize the first span. We always have a beginning sequence that is
- // a string, even if it is empty. Actually, there may be an empty string
- // span between any two format spans. We'll ignore them later.
- spans[0].mpBegin = pFormat;
- spans[0].mUserIndex = -1;
- // Build the list of spans.
- // Read each format string character while maintaining a little state machine.
- for(p = pFormat; *p; ++p)
- {
- if(*p == '%')
- {
- // A % char within a format is invalid (though %% is valid), and any '%' char that
- // begins a format must be followed by at least three more chars, two for the user
- // index plus colon and at least one for the actual format (e.g. %4:d).
- EA_ASSERT(!bInFormat && p[1] && p[2] && p[3]);
- if(p[1] == '%')
- {
- spans[spanIndex].mbEscapePresent = true;
- p++; // Skip past the second % char.
- }
- else // else we don't have a %% sequence and thus have the start of a format...
- {
- // Finalize the current span (the one before the % char), before starting a new span for this % sequence.
- spans[spanIndex].mpEnd = p;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = 0; // This is redundant.
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- if((p[1] < '0') || (p[1] > '9'))
- return -1; // Invalid format. User specified a format like "%X:d" or just "%"
- const int userIndex = (int)(p[1] - '0');
- if((userIndex == 0) && (startIndex != 0)) // If it appears that the user is using argument numbering that is 0-based (e.g. "%0:d") instead of 1-based...
- {
- startIndex = 0;
- for(int i = kArgCapacity - 1; i > 0; --i)
- spanArgOrder[i] = spanArgOrder[i - 1]; // Convert any existing indexes from what we originally assumed to be 1-based values 'up' to being 0-based values.
- }
- bInFormat = true;
- nFormatLength = 1; // For the % char we write into mFormat.
- spans[spanIndex].mpBegin = p;
- spans[spanIndex].mFormat[0] = '%';
- spans[spanIndex].mUserIndex = userIndex; // We don't write the user index or the colon into the format string, which is a standard C format specifier sequence.
- spanArgOrder[userIndex - startIndex] = spanIndex; // startIndex is normally 1, because usually users specify argument indexes in a 1:based way.
- formattedSpanCount++;
- EA_ASSERT(p[2] == ':'); // We expect formats to have a N: sequence after the % in order to indicate order (e.g. %4:3.1f means %3.1f as 4th argument)
- if(p[2] != ':')
- return -1; // Invalid format. User specified a format like "%00:d" or just "%0"
- p += 2; // Skip past the user index and the colon.
- }
- }
- else if(bInFormat)
- {
- EA_ASSERT(nFormatLength < kSpanFormatCapacity);
- if(nFormatLength < kSpanFormatCapacity)
- spans[spanIndex].mFormat[nFormatLength++] = *p;
- else
- return -1; // Invalid format. User specified a format like "%0:000000000000001d" (too long a printf format)
- switch(*p)
- {
- case 'b': case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': // If this character is the last posible character of a format specifier...
- case 'g': case 'G': case 'e': case 'E': case 'f': case 'F': case 'a':
- case 'A': case 'p': case 'c': case 'C': case 's': case 'S': case 'n':
- {
- // Finalize the current span.
- spans[spanIndex].mpEnd = p + 1;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = *p;
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- bInFormat = false;
- nFormatLength = 0;
- spans[spanIndex].mpBegin = p + 1;
- spans[spanIndex].mUserIndex = -1; // This is a string span. If a format sequence or end-of-string immediately follows then this will be an empty string span.
- break;
- }
- default:
- // To do: Handle the case of an invalid format character.
- break;
- }
- }
- // Else we are in the middle of a string portion of the format and we need do nothing here.
- }
- // Finalize the last span, which by definition ends at the end of our input.
- EA_ASSERT(spanIndex < kSpanCapacity);
- if((spanIndex == kSpanCapacity) && *p) // If the user somehow specified more format spans than possible...
- return -1; // Invalid format. Too many format spans.
- spans[spanIndex].mpEnd = p; // p Should always point to the terminating 0 char of pFormat.
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spanIndex++;
- // Now we read the arguments into span[s].mValue in the order they were passed by the caller.
- for(int i = 0; i < formattedSpanCount; ++i)
- {
- EA_ASSERT((spanArgOrder[i] >= 0) && (spanArgOrder[i] < kSpanCapacity));
- Span16& span = spans[spanArgOrder[i]];
- FormatData formatData;
- // We call ReadFormat in order to get the argument type. We don't need the other information from FormatData.
- // ReadFormat returns the pointer to the next char after the format sequence. If there is an error then it
- // returns a pointer to where it failed. We can use this to tell if ReadFormat failed by testing *pEnd == 0.
- pEnd = ReadFormat(span.mFormat, &formatData, (va_list*)&arguments);
- if(*pEnd != 0) // If ReadFormat bailed before processing the entire format...
- return -1;
- // Unfortunately, ReadFormat tells us the type only if it is a modified type (e.g. %ld as opposed to %d).
- // We could go and modify ReadFormat to store the full type all the time, but that would be messing with
- // that function and its performance and we'd rather stay away from that. We can solve this with a simple
- // switch statement here.
- if(formatData.mModifier == kModifierNone)
- {
- switch (pEnd[-1])
- {
- case 'b':
- case 'd':
- case 'i':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- formatData.mModifier = kModifierInt;
- break;
- case 'g':
- case 'G':
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'a':
- case 'A':
- formatData.mModifier = kModifierDouble;
- break;
- case 'p':
- case 's':
- case 'S':
- case 'n':
- EA_COMPILETIME_ASSERT(sizeof(size_t) == sizeof(void*)); // If this fails then we need to modify this statement.
- formatData.mModifier = kModifierSize_t;
- break;
- case 'c':
- formatData.mModifier = kModifierChar;
- break;
- case 'C':
- formatData.mModifier = kModifierWChar;
- break;
- default:
- EA_FAIL_M("EAStdC OVprintfCore"); // This shouldn't occur unless ReadFormat started supporting some new format that we aren't yet aware of and it is being used here.
- break;
- }
- }
-
- span.mType = formatData.mModifier;
- switch (span.mType)
- {
- case kModifierChar:
- span.mValue.mChar = (char)va_arg(arguments, int); // Recall that C++ promotes types less than int to int.
- break;
- case kModifierShort:
- span.mValue.mShort = (short)va_arg(arguments, int);
- break;
- case kModifierInt:
- span.mValue.mInt = va_arg(arguments, int);
- break;
- case kModifierLong:
- span.mValue.mLong = va_arg(arguments, long);
- break;
- case kModifierLongLong:
- span.mValue.mLongLong = va_arg(arguments, long long);
- break;
- case kModifierMax_t:
- span.mValue.mMax = va_arg(arguments, intmax_t);
- break;
- case kModifierSize_t:
- span.mValue.mSize = va_arg(arguments, size_t);
- break;
- case kModifierPtrdiff_t:
- span.mValue.mPtrDiff = va_arg(arguments, ptrdiff_t);
- break;
- case kModifierDouble:
- span.mValue.mDouble = va_arg(arguments, double);
- break;
- case kModifierLongDouble:
- span.mValue.mLongDouble = va_arg(arguments, long double);
- break;
- case kModifierWChar:
- span.mValue.mWChar = (wchar_t)va_arg(arguments, unsigned int);
- break;
- case kModifierInt8:
- span.mValue.mInt8 = (int8_t)va_arg(arguments, int);
- break;
- case kModifierInt16:
- span.mValue.mInt16 = (int16_t)va_arg(arguments, int);
- break;
- case kModifierInt32:
- span.mValue.mInt32 = va_arg(arguments, int32_t);
- break;
- case kModifierInt64:
- span.mValue.mInt64 = va_arg(arguments, int64_t);
- break;
- case kModifierInt128:
- span.mValue.mLongLong = 0;
- EA_FAIL_M("EAStdC OVprintfCore"); // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- // If this occurs, then our ReadFormat function seems to have a bug. We already have an assertion failure for this case above.
- span.mValue.mLongLong = 0;
- break;
- }
- }
- // Now we have an array of spans. Now we print the spans one by one.
- for(int s = 0; s < spanIndex; ++s)
- {
- const Span16& span = spans[s];
- if(span.mpEnd != span.mpBegin) // If non-empty...
- {
- if(span.mUserIndex >= 0) // If this is a format span as opposed to a string span...
- {
- switch (span.mType)
- {
- case kModifierChar:
- // We can't call VprintfCore directly because it expects a va_list, whereas we have just a single value.
- // We can't call Sprintf because we need to pass the current pWriteFunction, pWriteFunctionContext.
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mChar);
- break;
- case kModifierShort:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mShort);
- break;
- case kModifierInt:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mInt);
- break;
- case kModifierLong:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mLong);
- break;
- case kModifierLongLong:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mLongLong);
- break;
- case kModifierMax_t:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mMax);
- break;
- case kModifierSize_t:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mSize);
- break;
- case kModifierPtrdiff_t:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mPtrDiff);
- break;
- case kModifierDouble:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mDouble);
- break;
- case kModifierLongDouble:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mLongDouble);
- break;
- case kModifierWChar:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mWChar);
- break;
- case kModifierInt8:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mInt8);
- break;
- case kModifierInt16:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mInt16);
- break;
- case kModifierInt32:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mInt32);
- break;
- case kModifierInt64:
- result = CallVprintfCore(pWriteFunction16, pWriteFunctionContext16, span.mFormat, span.mValue.mInt64);
- break;
- case kModifierInt128:
- result = -1; // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- result = -1; // If this fails, then our ReadFormat function seems to have a bug.
- break;
- }
- if(result < 0)
- return -1;
- nWriteCountSum += result;
- }
- else
- {
- // We simply copy the span to the pWriteFunction, while taking care of %% escape sequences.
- p = span.mpBegin;
- pEnd = span.mpEnd;
- if(span.mbEscapePresent) // If somewhere in the string span there is at least one %% sequence, we must copy the slow way: one by one.
- {
- for(result = 1; (result >= 0) && (p < pEnd); ++p)
- {
- if(pWriteFunction16(p, 1, pWriteFunctionContext16, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += result;
- }
- }
- else
- {
- if(pWriteFunction16(p, (size_t)(pEnd - p), pWriteFunctionContext16, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += (int)(pEnd - p);
- }
- }
- }
- }
- pWriteFunction16(NULL, 0, pWriteFunctionContext16, kWFSEnd);
- return nWriteCountSum;
- }
- // This function is a copy of the 8/16 bit versions above.
- static int OVprintfCore(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pWriteFunctionContext32, const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- const int kArgCapacity = 10; // Currently only single digit ('0'-'9') order values are supported.
- const int kSpanCapacity = 21; // Worst case scenario of 21 spans. For example: " %2:d %7:d %1:d %6:d %3:d %5:d %4:d %0:d %8:d %9:d " or "%0:d%1:d%2:d%3:d%4:d%5:d%6:d%7:d%8:d%9:d"
- Span32 spans[kSpanCapacity];
- int spanArgOrder[kArgCapacity] = { -1 }; // Each entry is the index into 'spans' of that argument. This allows us to quickly find formats in the order the user passed them to this function. For the example directly above, the contents would be: 5, 1, 9, 13, 11, 7, 3, 17, 15. We initialize it to -1 in order to avoid compiler warnings about it being used before set.
- int spanIndex = 0;
- int formattedSpanCount = 0;
- bool bInFormat = false; // State variable indicating if we are within a % format sequence.
- int nFormatLength = 0;
- int nWriteCountSum = 0;
- int startIndex = 1; // This is 1 or 0, and it defaults to 1 (but may change below) in order to mean that user formats start at 1, as in "%1:d". However, we have a feature whereby we detect that the user is using %0 as the start index.
- const char32_t* p;
- const char32_t* pEnd;
- int result;
- static_assert((EAArrayCount(spans) == kSpanCapacity) && (EAArrayCount(spanArgOrder) == kArgCapacity), "spans and spanArgOrder are not using constants for their array size.");
- #ifdef EA_DEBUG
- for(int s = 0; s < kArgCapacity; ++s)
- spanArgOrder[s] = -1;
- #else
- memset(spanArgOrder, 0, sizeof(spanArgOrder));
- #endif
- pWriteFunction32(NULL, 0, pWriteFunctionContext32, kWFSBegin);
- // Initialize the first span. We always have a beginning sequence that is
- // a string, even if it is empty. Actually, there may be an empty string
- // span between any two format spans. We'll ignore them later.
- spans[0].mpBegin = pFormat;
- spans[0].mUserIndex = -1;
- // Build the list of spans.
- // Read each format string character while maintaining a little state machine.
- for(p = pFormat; *p; ++p)
- {
- if(*p == '%')
- {
- // A % char within a format is invalid (though %% is valid), and any '%' char that
- // begins a format must be followed by at least three more chars, two for the user
- // index plus colon and at least one for the actual format (e.g. %4:d).
- EA_ASSERT(!bInFormat && p[1] && p[2] && p[3]);
- if(p[1] == '%')
- {
- spans[spanIndex].mbEscapePresent = true;
- p++; // Skip past the second % char.
- }
- else // else we don't have a %% sequence and thus have the start of a format...
- {
- // Finalize the current span (the one before the % char), before starting a new span for this % sequence.
- spans[spanIndex].mpEnd = p;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = 0; // This is redundant.
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- if((p[1] < '0') || (p[1] > '9'))
- return -1; // Invalid format. User specified a format like "%X:d" or just "%"
- const int userIndex = (int)(p[1] - '0');
- if((userIndex == 0) && (startIndex != 0)) // If it appears that the user is using argument numbering that is 0-based (e.g. "%0:d") instead of 1-based...
- {
- startIndex = 0;
- for(int i = kArgCapacity - 1; i > 0; --i)
- spanArgOrder[i] = spanArgOrder[i - 1]; // Convert any existing indexes from what we originally assumed to be 1-based values 'up' to being 0-based values.
- }
- bInFormat = true;
- nFormatLength = 1; // For the % char we write into mFormat.
- spans[spanIndex].mpBegin = p;
- spans[spanIndex].mFormat[0] = '%';
- spans[spanIndex].mUserIndex = userIndex; // We don't write the user index or the colon into the format string, which is a standard C format specifier sequence.
- spanArgOrder[userIndex - startIndex] = spanIndex; // startIndex is normally 1, because usually users specify argument indexes in a 1:based way.
- formattedSpanCount++;
- EA_ASSERT(p[2] == ':'); // We expect formats to have a N: sequence after the % in order to indicate order (e.g. %4:3.1f means %3.1f as 4th argument)
- if(p[2] != ':')
- return -1; // Invalid format. User specified a format like "%00:d" or just "%0"
- p += 2; // Skip past the user index and the colon.
- }
- }
- else if(bInFormat)
- {
- EA_ASSERT(nFormatLength < kSpanFormatCapacity);
- if(nFormatLength < kSpanFormatCapacity)
- spans[spanIndex].mFormat[nFormatLength++] = *p;
- else
- return -1; // Invalid format. User specified a format like "%0:000000000000001d" (too long a printf format)
- switch(*p)
- {
- case 'b': case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': // If this character is the last posible character of a format specifier...
- case 'g': case 'G': case 'e': case 'E': case 'f': case 'F': case 'a':
- case 'A': case 'p': case 'c': case 'C': case 's': case 'S': case 'n':
- {
- // Finalize the current span.
- spans[spanIndex].mpEnd = p + 1;
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spans[spanIndex].mFormatChar = *p;
- if(++spanIndex == kSpanCapacity)
- break;
- // Intialize the next span.
- bInFormat = false;
- nFormatLength = 0;
- spans[spanIndex].mpBegin = p + 1;
- spans[spanIndex].mUserIndex = -1; // This is a string span. If a format sequence or end-of-string immediately follows then this will be an empty string span.
- break;
- }
- default:
- // To do: Handle the case of an invalid format character.
- break;
- }
- }
- // Else we are in the middle of a string portion of the format and we need do nothing here.
- }
- // Finalize the last span, which by definition ends at the end of our input.
- EA_ASSERT(spanIndex < kSpanCapacity);
- if((spanIndex == kSpanCapacity) && *p) // If the user somehow specified more format spans than possible...
- return -1; // Invalid format. Too many format spans.
- spans[spanIndex].mpEnd = p; // p Should always point to the terminating 0 char of pFormat.
- spans[spanIndex].mFormat[nFormatLength] = 0;
- spanIndex++;
- // Now we read the arguments into span[s].mValue in the order they were passed by the caller.
- for(int i = 0; i < formattedSpanCount; ++i)
- {
- EA_ASSERT((spanArgOrder[i] >= 0) && (spanArgOrder[i] < kSpanCapacity));
- Span32& span = spans[spanArgOrder[i]];
- FormatData formatData;
- // We call ReadFormat in order to get the argument type. We don't need the other information from FormatData.
- // ReadFormat returns the pointer to the next char after the format sequence. If there is an error then it
- // returns a pointer to where it failed. We can use this to tell if ReadFormat failed by testing *pEnd == 0.
- pEnd = ReadFormat(span.mFormat, &formatData, (va_list*)&arguments);
- if(*pEnd != 0) // If ReadFormat bailed before processing the entire format...
- return -1;
- // Unfortunately, ReadFormat tells us the type only if it is a modified type (e.g. %ld as opposed to %d).
- // We could go and modify ReadFormat to store the full type all the time, but that would be messing with
- // that function and its performance and we'd rather stay away from that. We can solve this with a simple
- // switch statement here.
- if(formatData.mModifier == kModifierNone)
- {
- switch (pEnd[-1])
- {
- case 'b':
- case 'd':
- case 'i':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- formatData.mModifier = kModifierInt;
- break;
- case 'g':
- case 'G':
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'a':
- case 'A':
- formatData.mModifier = kModifierDouble;
- break;
- case 'p':
- case 's':
- case 'S':
- case 'n':
- EA_COMPILETIME_ASSERT(sizeof(size_t) == sizeof(void*)); // If this fails then we need to modify this statement.
- formatData.mModifier = kModifierSize_t;
- break;
- case 'c':
- formatData.mModifier = kModifierChar;
- break;
- case 'C':
- formatData.mModifier = kModifierWChar;
- break;
- default:
- EA_FAIL_M("EAStdC OVprintfCore"); // This shouldn't occur unless ReadFormat started supporting some new format that we aren't yet aware of and it is being used here.
- break;
- }
- }
-
- span.mType = formatData.mModifier;
- switch (span.mType)
- {
- case kModifierChar:
- span.mValue.mChar = (char)va_arg(arguments, int); // Recall that C++ promotes types less than int to int.
- break;
- case kModifierShort:
- span.mValue.mShort = (short)va_arg(arguments, int);
- break;
- case kModifierInt:
- span.mValue.mInt = va_arg(arguments, int);
- break;
- case kModifierLong:
- span.mValue.mLong = va_arg(arguments, long);
- break;
- case kModifierLongLong:
- span.mValue.mLongLong = va_arg(arguments, long long);
- break;
- case kModifierMax_t:
- span.mValue.mMax = va_arg(arguments, intmax_t);
- break;
- case kModifierSize_t:
- span.mValue.mSize = va_arg(arguments, size_t);
- break;
- case kModifierPtrdiff_t:
- span.mValue.mPtrDiff = va_arg(arguments, ptrdiff_t);
- break;
- case kModifierDouble:
- span.mValue.mDouble = va_arg(arguments, double);
- break;
- case kModifierLongDouble:
- span.mValue.mLongDouble = va_arg(arguments, long double);
- break;
- case kModifierWChar:
- span.mValue.mWChar = (wchar_t)va_arg(arguments, unsigned int);
- break;
- case kModifierInt8:
- span.mValue.mInt8 = (int8_t)va_arg(arguments, int);
- break;
- case kModifierInt16:
- span.mValue.mInt16 = (int16_t)va_arg(arguments, int);
- break;
- case kModifierInt32:
- span.mValue.mInt32 = va_arg(arguments, int32_t);
- break;
- case kModifierInt64:
- span.mValue.mInt64 = va_arg(arguments, int64_t);
- break;
- case kModifierInt128:
- span.mValue.mLongLong = 0;
- EA_FAIL_M("EAStdC OVprintfCore"); // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- // If this occurs, then our ReadFormat function seems to have a bug. We already have an assertion failure for this case above.
- span.mValue.mLongLong = 0;
- break;
- }
- }
- // Now we have an array of spans. Now we print the spans one by one.
- for(int s = 0; s < spanIndex; ++s)
- {
- const Span32& span = spans[s];
- if(span.mpEnd != span.mpBegin) // If non-empty...
- {
- if(span.mUserIndex >= 0) // If this is a format span as opposed to a string span...
- {
- switch (span.mType)
- {
- case kModifierChar:
- // We can't call VprintfCore directly because it expects a va_list, whereas we have just a single value.
- // We can't call Sprintf because we need to pass the current pWriteFunction, pWriteFunctionContext.
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mChar);
- break;
- case kModifierShort:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mShort);
- break;
- case kModifierInt:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mInt);
- break;
- case kModifierLong:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mLong);
- break;
- case kModifierLongLong:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mLongLong);
- break;
- case kModifierMax_t:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mMax);
- break;
- case kModifierSize_t:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mSize);
- break;
- case kModifierPtrdiff_t:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mPtrDiff);
- break;
- case kModifierDouble:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mDouble);
- break;
- case kModifierLongDouble:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mLongDouble);
- break;
- case kModifierWChar:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mWChar);
- break;
- case kModifierInt8:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mInt8);
- break;
- case kModifierInt16:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mInt16);
- break;
- case kModifierInt32:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mInt32);
- break;
- case kModifierInt64:
- result = CallVprintfCore(pWriteFunction32, pWriteFunctionContext32, span.mFormat, span.mValue.mInt64);
- break;
- case kModifierInt128:
- result = -1; // We don't currently support 128 bit types in this function. We do have an int128_t class in this package which could be used, though.
- break;
- case kModifierNone:
- default:
- result = -1; // If this fails, then our ReadFormat function seems to have a bug.
- break;
- }
- if(result < 0)
- return -1;
- nWriteCountSum += result;
- }
- else
- {
- // We simply copy the span to the pWriteFunction, while taking care of %% escape sequences.
- p = span.mpBegin;
- pEnd = span.mpEnd;
- if(span.mbEscapePresent) // If somewhere in the string span there is at least one %% sequence, we must copy the slow way: one by one.
- {
- for(result = 1; (result >= 0) && (p < pEnd); ++p)
- {
- if(pWriteFunction32(p, 1, pWriteFunctionContext32, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += result;
- }
- }
- else
- {
- if(pWriteFunction32(p, (size_t)(pEnd - p), pWriteFunctionContext32, kWFSIntermediate) < 0)
- return -1;
- nWriteCountSum += (int)(pEnd - p);
- }
- }
- }
- }
- pWriteFunction32(NULL, 0, pWriteFunctionContext32, kWFSEnd);
- return nWriteCountSum;
- }
- } // SprintfLocal
- ///////////////////////////////////////////////////////////////////////////////
- // char8_t
- ///////////////////////////////////////////////////////////////////////////////
- EASTDC_API int OVcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(pWriteFunction8, pContext, pFormat, arguments);
- }
- EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter8, pFile, pFormat, arguments);
- }
- EASTDC_API int OVprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter8, stdout, pFormat, arguments);
- }
- EASTDC_API int OVsprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return OVsnprintf(pDestination, (size_t)-1, pFormat, arguments);
- }
- EASTDC_API int OVsnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- SprintfLocal::SnprintfContext8 sc(pDestination, 0, pDestination ? n : 0);
- const int nRequiredLength = SprintfLocal::OVprintfCore(SprintfLocal::StringWriter8, &sc, pFormat, arguments);
- #if EASPRINTF_SNPRINTF_C99_RETURN
- if(pDestination && (nRequiredLength >= 0))
- {
- if((size_t)nRequiredLength < n) // If there was enough space...
- pDestination[nRequiredLength] = 0;
- else if(n > 0)
- pDestination[n - 1] = 0;
- } // Else an encoding error has occurred and we can do nothing.
- return nRequiredLength;
- #else
- if((size_t)nRequiredLength < n)
- {
- if(pDestination)
- pDestination[nRequiredLength] = 0;
- return n;
- }
- else if((n > 0) && pDestination)
- pDestination[n - 1] = 0;
- return -1;
- #endif
- }
- EASTDC_API int OVscprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments)
- {
- // vscprintf returns the number of chars that are needed for a printf operation.
- return OVsnprintf(NULL, 0, pFormat, arguments);
- }
- EASTDC_API int OCprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(pWriteFunction, pContext, pFormat, arguments);
- }
- EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter8, pFile, pFormat, arguments);
- }
- EASTDC_API int OPrintf(const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter8, stdout, pFormat, arguments);
- }
- EASTDC_API int OSprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
- }
- EASTDC_API int OSnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, n, pFormat, arguments);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // char16_t
- ///////////////////////////////////////////////////////////////////////////////
- EASTDC_API int OVcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(pWriteFunction16, pContext, pFormat, arguments);
- }
- EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter16, pFile, pFormat, arguments);
- }
- EASTDC_API int OVprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter16, stdout, pFormat, arguments);
- }
- EASTDC_API int OVsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return OVsnprintf(pDestination, (size_t)-1, pFormat, arguments);
- }
- EASTDC_API int OVsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- SprintfLocal::SnprintfContext16 sc(pDestination, 0, pDestination ? n : 0);
- const int nRequiredLength = SprintfLocal::OVprintfCore(SprintfLocal::StringWriter16, &sc, pFormat, arguments);
- #if EASPRINTF_SNPRINTF_C99_RETURN
- if(pDestination && (nRequiredLength >= 0))
- {
- if((size_t)nRequiredLength < n) // If there was enough space...
- pDestination[nRequiredLength] = 0;
- else if(n > 0)
- pDestination[n - 1] = 0;
- } // Else an encoding error has occurred and we can do nothing.
- return nRequiredLength;
- #else
- if((size_t)nRequiredLength < n)
- {
- if(pDestination)
- pDestination[nRequiredLength] = 0;
- return n;
- }
- else if((n > 0) && pDestination)
- pDestination[n - 1] = 0;
- return -1;
- #endif
- }
- EASTDC_API int OVscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments)
- {
- // vscprintf returns the number of chars that are needed for a printf operation.
- return OVsnprintf(NULL, 0, pFormat, arguments);
- }
- EASTDC_API int OCprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(pWriteFunction, pContext, pFormat, arguments);
- }
- EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter16, pFile, pFormat, arguments);
- }
- EASTDC_API int OPrintf(const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter16, stdout, pFormat, arguments);
- }
- EASTDC_API int OSprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
- }
- EASTDC_API int OSnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, n, pFormat, arguments);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // char32_t
- ///////////////////////////////////////////////////////////////////////////////
- EASTDC_API int OVcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(pWriteFunction32, pContext, pFormat, arguments);
- }
- EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter32, pFile, pFormat, arguments);
- }
- EASTDC_API int OVprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter32, stdout, pFormat, arguments);
- }
- EASTDC_API int OVsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- return OVsnprintf(pDestination, (size_t)-1, pFormat, arguments);
- }
- EASTDC_API int OVsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- SprintfLocal::SnprintfContext32 sc(pDestination, 0, pDestination ? n : 0);
- const int nRequiredLength = SprintfLocal::OVprintfCore(SprintfLocal::StringWriter32, &sc, pFormat, arguments);
- #if EASPRINTF_SNPRINTF_C99_RETURN
- if(pDestination && (nRequiredLength >= 0))
- {
- if((size_t)nRequiredLength < n) // If there was enough space...
- pDestination[nRequiredLength] = 0;
- else if(n > 0)
- pDestination[n - 1] = 0;
- } // Else an encoding error has occurred and we can do nothing.
- return nRequiredLength;
- #else
- if((size_t)nRequiredLength < n)
- {
- if(pDestination)
- pDestination[nRequiredLength] = 0;
- return n;
- }
- else if((n > 0) && pDestination)
- pDestination[n - 1] = 0;
- return -1;
- #endif
- }
- EASTDC_API int OVscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments)
- {
- // vscprintf returns the number of chars that are needed for a printf operation.
- return OVsnprintf(NULL, 0, pFormat, arguments);
- }
- EASTDC_API int OCprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(pWriteFunction, pContext, pFormat, arguments);
- }
- EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter32, pFile, pFormat, arguments);
- }
- EASTDC_API int OPrintf(const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return SprintfLocal::OVprintfCore(SprintfLocal::FILEWriter32, stdout, pFormat, arguments);
- }
- EASTDC_API int OSprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
- }
- EASTDC_API int OSnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...)
- {
- va_list arguments;
- va_start(arguments, pFormat);
- return OVsnprintf(pDestination, n, pFormat, arguments);
- }
- } // namespace StdC
- } // namespace EA
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
|