| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <EAStdC/internal/Config.h>
- #include <EAStdC/EAScanf.h>
- #include <EAStdC/internal/ScanfCore.h>
- #include <EAStdC/EAString.h>
- #include <EAStdC/EACType.h>
- #include <EAStdC/EAMathHelp.h>
- #include <EAAssert/eaassert.h>
- EA_DISABLE_ALL_VC_WARNINGS()
- #include <math.h>
- #include <float.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <limits.h>
- EA_RESTORE_ALL_VC_WARNINGS()
- EA_DISABLE_VC_WARNING(4127 4146) // C4127: conditional expression is constant.
- // C4146: unary minus operator applied to unsigned type, result still unsigned
- #if defined(EA_PLATFORM_WINDOWS)
- // Users sometimes get link problems when mixing DLL and non-DLL builds under Windows
- // due to HUGE_VAL. This is due to how they are building the app, though we can help
- // avoid the problem by using a different source for HUGE_VAL.
- #define EASTDC_HUGE_VAL EA::StdC::kFloat64Infinity
- #else
- #define EASTDC_HUGE_VAL HUGE_VAL
- #endif
- // EA_ENABLE_PRECISE_FP / EA_RESTORE_PRECISE_FP
- //
- // Allows you to force the usage of precise floating point support by the compiler, whereas the
- // compiler may have been set to default to another form which is faster but doesn't strictly
- // follow conventions. For example, see:
- // http://connect.microsoft.com/VisualStudio/feedback/details/754839/signed-double-0-0-vs-0-0-msvs2012-visual-c-bug-in-x64-mode-when-compiled-with-fp-fast-o2
- // Reference:
- // http://msdn.microsoft.com/en-us/library/45ec64h6.aspx
- //
- // Example usage:
- // EA_ENABLE_PRECISE_FP()
- // void SomeFunction(){ ... }
- // EA_RESTORE_PRECISE_FP()
- //
- #if !defined(EA_ENABLE_PRECISE_FP)
- #if defined(EA_COMPILER_MSVC)
- #define EA_ENABLE_PRECISE_FP() __pragma(float_control(precise, on, push))
- #else
- #define EA_ENABLE_PRECISE_FP()
- #endif
- #endif
- #if !defined(EA_RESTORE_PRECISE_FP)
- #if defined(EA_COMPILER_MSVC)
- #define EA_RESTORE_PRECISE_FP() __pragma(float_control(pop))
- #else
- #define EA_RESTORE_PRECISE_FP()
- #endif
- #endif
- namespace EA
- {
- namespace StdC
- {
- extern uint8_t utf8lengthTable[256];
- namespace ScanfLocal
- {
- int FILEReader8(ReadAction readAction, int value, void* pContext)
- {
- // We verify that the FILE functions work as desired. If this fails to
- // be so for some compiler/platform then we can modify this code.
- EA_COMPILETIME_ASSERT(EOF == kReadError);
- FILE* const pFile = (FILE*)pContext;
- switch(readAction)
- {
- case kReadActionBegin:
- {
- #if (!defined(__GNUC__) || (__GNUC__ >= 4)) // Older versions of GCC don't support fwide().
- // Question: Is this doing the right thing? Or do we need to be doing something else?
- if(value == 1) // "The value param will be 1 for UTF8 and 2 for UCS2."
- return (fwide(pFile, -1) < 0) ? 1 : 0; // Set the file to be interpreted as UTF8.
- else
- {
- // Problem: wide is 2 bytes for some platforms and 4 for others. The only way for us
- // to fix this problem properly is to handle 32->16 or 16->32 conversions on our
- // side. But we don't have state information in this FileReader8 function. We would
- // need to revise pContext to provide some space for us to write state.
- return (fwide(pFile, 1) > 0) ? 1 : 0; // Set the file to be interpreted as wide.
- }
- #endif
- }
- case kReadActionEnd:
- // Currently we do nothing, but possibly we should restore the file byte/wide state.
- break;
- case kReadActionRead:
- return fgetc(pFile);
- case kReadActionUnread:
- return ungetc(value, pFile);
- case kReadActionGetAtEnd:
- return feof(pFile);
- case kReadActionGetLastError:
- return ferror(pFile);
- }
- return 0;
- }
- int FILEReader16(ReadAction readAction, int value, void* pContext)
- {
- return FILEReader8(readAction, value, pContext);
- }
- int FILEReader32(ReadAction readAction, int value, void* pContext)
- {
- return FILEReader8(readAction, value, pContext);
- }
- int StringReader8(ReadAction readAction, int /*value*/, void* pContext)
- {
- SscanfContext8* const pSscanfContext8 = (SscanfContext8*)pContext;
- switch(readAction)
- {
- case kReadActionBegin:
- case kReadActionEnd:
- case kReadActionGetLastError:
- break;
- case kReadActionRead:
- if(*pSscanfContext8->mpSource)
- return (uint8_t)*pSscanfContext8->mpSource++;
- else
- {
- pSscanfContext8->mbEndFound = 1;
- return kReadError;
- }
- case kReadActionUnread:
- if(!pSscanfContext8->mbEndFound)
- pSscanfContext8->mpSource--; // We don't error-check this; we currently assume the caller is bug-free.
- else
- pSscanfContext8->mbEndFound = 0;
- break;
- case kReadActionGetAtEnd:
- return pSscanfContext8->mbEndFound;
- }
- return 0;
- }
- int StringReader16(ReadAction readAction, int /*value*/, void* pContext)
- {
- SscanfContext16* const pSscanfContext16 = (SscanfContext16*)pContext;
- switch(readAction)
- {
- case kReadActionBegin:
- case kReadActionEnd:
- case kReadActionGetLastError:
- break;
- case kReadActionRead:
- if(*pSscanfContext16->mpSource)
- {
- EA_COMPILETIME_ASSERT(sizeof(int) >= sizeof(char16_t));
- return (int)(char16_t)*pSscanfContext16->mpSource++;
- }
- else
- {
- pSscanfContext16->mbEndFound = 1;
- return kReadError;
- }
- case kReadActionUnread:
- if(!pSscanfContext16->mbEndFound)
- pSscanfContext16->mpSource--; // We don't error-check this; we currently assume the caller is bug-free.
- else
- pSscanfContext16->mbEndFound = 0;
- break;
- case kReadActionGetAtEnd:
- return pSscanfContext16->mbEndFound;
- }
- return 0;
- }
- int StringReader32(ReadAction readAction, int /*value*/, void* pContext)
- {
- SscanfContext32* const pSscanfContext32 = (SscanfContext32*)pContext;
- switch(readAction)
- {
- case kReadActionBegin:
- case kReadActionEnd:
- case kReadActionGetLastError:
- break;
- case kReadActionRead:
- if(*pSscanfContext32->mpSource)
- {
- EA_COMPILETIME_ASSERT(sizeof(int) >= sizeof(char32_t));
- return (int)(char32_t)*pSscanfContext32->mpSource++;
- }
- else
- {
- pSscanfContext32->mbEndFound = 1;
- return kReadError;
- }
- case kReadActionUnread:
- if(!pSscanfContext32->mbEndFound)
- pSscanfContext32->mpSource--; // We don't error-check this; we currently assume the caller is bug-free.
- else
- pSscanfContext32->mbEndFound = 0;
- break;
- case kReadActionGetAtEnd:
- return pSscanfContext32->mbEndFound;
- }
- return 0;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // ToDouble
- //
- // We have a string of digits and an exponent. We need to convert them to
- // a double.
- //
- // The proper conversion from string to double is not entirely trivial,
- // as documented in the papers:
- // What Every Computer Scientist Should Know About Floating Point Arithmetic
- // How to Read Floating Point Numbers Accurately
- // Correctly Rounded Binary-Decimal and Decimal-Binary Conversions
- //
- const double powerTable[18] = { 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11 };
- double DoubleValue::ToDouble() const
- {
- if(mExponent >= -6 && mExponent <= 11) // If we can handle the result quickly and accurately with a simple implementation...
- {
- // We could handle exponents bigger than this if we used a bigger table or (better) if we
- // wrote code that allowed us to apply a multiplier to the existing table. On the other hand,
- // for our purposes it is uncommon to work with such huge numbers.
- double result = 0.0;
- for(int i = 0; i < mSigLen; ++i)
- result = (result * 10.0) + (float)(mSigStr[i] - '0');
- result *= powerTable[mExponent + 6]; // +6 because our smallest exponent above is 1e-6
- return result;
- }
- else
- {
- // Negative exponents mean the number has a fractional component.
- // However, floating point hardware can't perfectly represent decimal
- // fractions. A result of this, as noted in the above papers, is that
- // a simple loop which multiplies by 10 won't yield an ideal floating
- // point representation for all decimal values. In the absence of
- // FPU hardware support for decimal to binary conversions, the only
- // way to achieve the ideal solution is to use an iterative approximating
- // algorithm.
- //
- // Until we implement that algorithm we do a fallback to the system
- // provided strtod function, which in many cases will implement such
- // an algorithm itself internally.
- char8_t buffer[kMaxSignificandDigits + 12];
- int i;
- for(i = 0; i < mSigLen; ++i)
- buffer[i] = mSigStr[i];
- if(mExponent)
- {
- int multiplier;
- int e = mExponent;
- buffer[i++] = 'e';
- if(e < 0)
- {
- buffer[i++] = '-';
- e = -e;
- }
- if(e >= 100)
- multiplier = 100;
- else if(e >= 10)
- multiplier = 10;
- else
- multiplier = 1;
- while(multiplier)
- {
- buffer[i++] = (char8_t)('0' + (e / multiplier));
- e %= multiplier;
- multiplier /= 10;
- }
- }
- buffer[i] = 0;
- return strtod(buffer, NULL); // This is not a fast function. That's why we want to replace it.
- }
- }
- // Force precise floating support by the compiler. This is necessary under VC++ because otherwise
- // it's possible that the /fp:fast compiler argument was used, which causes the -0.0 code below to
- // be generated by the compiler in a non-conformant way. This generation is by design, but prevents
- // us from having a conforming scanf implementation.
- EA_ENABLE_PRECISE_FP()
- template<typename ReadFunctionT, typename ReadFormatSpanFunctionT, typename CharT>
- class VscanfUtil
- {
- public:
- int VscanfCore(ReadFunctionT pReadFunction, ReadFormatSpanFunctionT pReadFormatSpanFunction, void* pContext, const CharT* pFormat, va_list arguments)
- {
- using namespace ScanfLocal;
- int nAssignmentCount = 0; // Number of assigned fields. This is the return value of this function. -1 in case of error.
- int nConversionCount = 0; // Number of processed fields. Will be >= the number of assigned fields.
- int nReadCount; // Temporary holder.
- int nReadCountSum = 0; // Used to support the %n field.
- const CharT* pFormatCurrent = pFormat; // Current position within entire fd string.
- char* pArgumentCurrent = NULL; // Pointer to current va_list argument.
- FormatData fd; //
- int c = 0; // Temporary character.
- uintmax_t uintMaxValue = 0; //
- intmax_t intMaxValue = 0; //
- long double ldValue; //
- int bNegative; //
- int bIntegerOverflow; //
- int nBase; //
- pReadFunction(kReadActionBegin, sizeof(*pFormat), pContext);
- while(*pFormatCurrent)
- {
- CharT cFormat = *pFormatCurrent;
- if(Isspace(cFormat))
- {
- do{
- cFormat = *++pFormatCurrent;
- }
- while(Isspace(cFormat));
- while(Isspace((CharT)(c = pReadFunction(kReadActionRead, 0, pContext))))
- ++nReadCountSum;
- pReadFunction(kReadActionUnread, c, pContext);
- continue;
- }
- if(cFormat != '%')
- {
- if((c = pReadFunction(kReadActionRead, 0, pContext)) != (int)cFormat)
- {
- pReadFunction(kReadActionUnread, c, pContext);
- goto Done;
- }
- ++nReadCountSum;
- ++pFormatCurrent;
- continue;
- }
- pFormatCurrent = ReadFormat(pFormatCurrent, &fd);
- if((fd.mnType == '%') || fd.mbSkipAssignment)
- pArgumentCurrent = NULL;
- else
- pArgumentCurrent = (char*)va_arg(arguments, void*); // User arguments are always passed as pointers.
- if((fd.mnType != 'n') && (pReadFunction(kReadActionGetLastError, 0, pContext) || pReadFunction(kReadActionGetAtEnd, 0, pContext)))
- break;
- switch (fd.mnType)
- {
- case '%':
- {
- while(Isspace((CharT)(c = pReadFunction(kReadActionRead, 0, pContext))))
- ++nReadCountSum;
- if(c != '%')
- {
- pReadFunction(kReadActionUnread, c, pContext);
- goto Done;
- }
- ++nReadCountSum;
- break;
- }
- case 'n':
- {
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- switch (fd.mModifier)
- {
- // There are some other modifier types we could support here.
- case kModifierMax_t: *(intmax_t*) pArgumentCurrent = (intmax_t) nReadCountSum; break;
- case kModifierSize_t: *(size_t*) pArgumentCurrent = (size_t) nReadCountSum; break;
- case kModifierPtrdiff_t:*(ptrdiff_t*)pArgumentCurrent = (ptrdiff_t)nReadCountSum; break;
- case kModifierInt64: *(int64_t*) pArgumentCurrent = (int64_t) nReadCountSum; break;
- case kModifierLongLong: *(long long*)pArgumentCurrent = (long long)nReadCountSum; break;
- case kModifierInt32: *(int32_t*) pArgumentCurrent = (int32_t) nReadCountSum; break;
- case kModifierLong: *(long*) pArgumentCurrent = (long) nReadCountSum; break;
- case kModifierInt16:
- case kModifierShort: *(short*) pArgumentCurrent = (short) nReadCountSum; break;
- case kModifierInt8:
- case kModifierChar: *(char*) pArgumentCurrent = (char) nReadCountSum; break;
- case kModifierNone: *(int*) pArgumentCurrent = (int) nReadCountSum; break;
- default: break;
- }
- }
- continue;
- }
- case 'b': // 'b' means binary. This is a convenient extension that we provide.
- case 'o':
- case 'u':
- case 'i':
- case 'd':
- case 'x':
- case 'X':
- {
- // To consider: replace if/else usage below with a switch or something with less branching.
- if(fd.mnType == 'b')
- nBase = 2;
- else if(fd.mnType == 'o')
- nBase = 8;
- else if(fd.mnType == 'u' || fd.mnType == 'd')
- nBase = 10;
- else if(fd.mnType == 'i')
- nBase = 0;
- else
- nBase = 16;
- switch (fd.mModifier)
- {
- case kModifierMax_t:
- uintMaxValue = (uintmax_t) ReadUint64(pReadFunction, pContext, UINTMAX_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierSize_t:
- uintMaxValue = (size_t) ReadUint64(pReadFunction, pContext, SIZE_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierPtrdiff_t:
- uintMaxValue = (ptrdiff_t) ReadUint64(pReadFunction, pContext, PTRDIFF_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierInt64:
- case kModifierLongLong:
- uintMaxValue = (unsigned long long)ReadUint64(pReadFunction, pContext, UINT64_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierInt32:
- case kModifierLong:
- uintMaxValue = (unsigned long) ReadUint64(pReadFunction, pContext, UINT32_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierInt16:
- case kModifierShort:
- uintMaxValue = (unsigned long) ReadUint64(pReadFunction, pContext, UINT16_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- case kModifierInt8:
- case kModifierChar:
- default:
- uintMaxValue = (unsigned long) ReadUint64(pReadFunction, pContext, UINT8_MAX, nBase, fd.mnWidth, nReadCount, bNegative, bIntegerOverflow);
- break;
- }
- if(!nReadCount)
- goto Done;
- if((fd.mnType == 'i' || fd.mnType == 'd')) // If using a signed type...
- {
- if(bNegative)
- intMaxValue = -uintMaxValue;
- else
- intMaxValue = (intmax_t)uintMaxValue;
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- switch (fd.mModifier)
- {
- case kModifierMax_t: *(intmax_t*) pArgumentCurrent = (intmax_t) intMaxValue; break;
- case kModifierSize_t: *(size_t*) pArgumentCurrent = (size_t) intMaxValue; break;
- case kModifierPtrdiff_t:*(ptrdiff_t*) pArgumentCurrent = (ptrdiff_t) intMaxValue; break;
- case kModifierInt64: *(int64_t*) pArgumentCurrent = (int64_t) intMaxValue; break;
- case kModifierLongLong: *(long long*) pArgumentCurrent = intMaxValue; break;
- case kModifierInt32: *(int32_t*) pArgumentCurrent = (int32_t) intMaxValue; break; // We assume sizeof long >= sizeof int32
- case kModifierLong: *(long*) pArgumentCurrent = (long) intMaxValue; break;
- case kModifierInt16:
- case kModifierShort: *(short*) pArgumentCurrent = (short) intMaxValue; break;
- case kModifierInt8:
- case kModifierChar: *(char*) pArgumentCurrent = (char) intMaxValue; break;
- case kModifierNone: *(int*) pArgumentCurrent = (int) intMaxValue; break;
- default: /* This should never occur. Possibly assert false */ break;
- }
- nAssignmentCount++;
- }
- }
- else
- {
- if(bNegative)
- {
- // It's a little odd to use a negative sign in front of an unsigned value, but it's valid C.
- uintMaxValue = (uintmax_t)-(intmax_t)uintMaxValue;
- }
- // else leave as-is.
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- switch (fd.mModifier)
- {
- case kModifierMax_t: *(uintmax_t*) pArgumentCurrent = (uintmax_t) uintMaxValue; break;
- case kModifierSize_t: *(size_t*) pArgumentCurrent = (size_t) uintMaxValue; break;
- case kModifierPtrdiff_t:*(ptrdiff_t*) pArgumentCurrent = (ptrdiff_t) uintMaxValue; break;
- case kModifierInt64: *(uint64_t*) pArgumentCurrent = (uint64_t) uintMaxValue; break;
- case kModifierLongLong: *(unsigned long long*) pArgumentCurrent = uintMaxValue; break;
- case kModifierInt32: *(uint32_t*) pArgumentCurrent = (uint32_t) uintMaxValue; break; // We assume sizeof ulong >= sizeof uint32
- case kModifierLong: *(unsigned long*) pArgumentCurrent = (unsigned long) uintMaxValue; break;
- case kModifierInt16:
- case kModifierShort: *(unsigned short*) pArgumentCurrent = (unsigned short) uintMaxValue; break;
- case kModifierInt8:
- case kModifierChar: *(unsigned char*) pArgumentCurrent = (unsigned char) uintMaxValue; break;
- case kModifierNone: *(unsigned int*) pArgumentCurrent = (unsigned int) uintMaxValue; break;
- default: /* This should never occur. Possibly assert false */ break;
- }
- nAssignmentCount++;
- }
- }
- nReadCountSum += nReadCount;
- nConversionCount++;
- break;
- }
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'g':
- case 'G':
- case 'a':
- case 'A':
- {
- ldValue = ReadDouble(pReadFunction, pContext, fd.mnWidth, fd.mDecimalPoint, nReadCount, bIntegerOverflow);
- if(!nReadCount)
- goto Done;
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- switch (fd.mModifier)
- {
- case kModifierLongDouble: *(long double*) pArgumentCurrent = ldValue; break;
- case kModifierDouble: *(double*) pArgumentCurrent = (double)ldValue; break;
- case kModifierNone: *(float*) pArgumentCurrent = (float) ldValue; break;
- default: /* This should never occur. Possibly assert false */ break;
- }
- nAssignmentCount++;
- }
- nReadCountSum += nReadCount;
- nConversionCount++;
- break;
- }
- case 's':
- case 'S':
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- // We eat leading whitespace and then fall through to reading characters.
- while(Isspace((CharT)c))
- {
- ++nReadCountSum;
- c = pReadFunction(kReadActionRead, 0, pContext);
- }
- pReadFunction(kReadActionUnread, c, pContext);
- // Fall through, as %[] processing is the same as %s except %[] specifies a filter for what characters to accept or ignore.
- }
- case '[':
- {
- // The user can use %[ab ] to read chars 'a', 'b', ' ' into the string until some other char is encountered.
- nReadCount = 0;
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- int stringTypeSize;
- switch (fd.mModifier)
- {
- case kModifierInt8: // If the user specified %I8s or %I8S
- case kModifierChar: // If the user specified %hs or %hS or kModifierWChar was chosen implicitly for other reasons.
- stringTypeSize = 1;
- break;
- case kModifierInt16: // If the user specified %I16s or %I16S
- stringTypeSize = 2;
- break;
- case kModifierInt32: // If the user specified %I32s or %I32S
- stringTypeSize = 4;
- break;
- case kModifierWChar: // If the user specified %ls or %lS or kModifierWChar was chosen implicitly for other reasons.
- stringTypeSize = sizeof(wchar_t);
- break;
- default: // If the user specified %I64s or %I64S or another invalid size.
- //nAssignmentCount = -1; Should we do this?
- goto Done;
- }
- if (!pReadFormatSpanFunction(fd, c, pReadFunction, pContext, stringTypeSize, pArgumentCurrent, nReadCount))
- {
- nAssignmentCount = -1;
- goto Done;
- }
- if(!nReadCount)
- {
- pReadFunction(kReadActionUnread, c, pContext);
- goto Done;
- }
- // 0-terminate the user's string.
- switch (stringTypeSize)
- {
- case 1:
- *((char8_t*)pArgumentCurrent) = 0;
- break;
- case 2:
- *((char16_t*)pArgumentCurrent) = 0;
- break;
- case 4:
- *((char32_t*)pArgumentCurrent) = 0;
- break;
- }
- nAssignmentCount++;
- }
- else
- {
- pReadFormatSpanFunction(fd, c, pReadFunction, pContext, -1, pArgumentCurrent, nReadCount);
- if(!nReadCount)
- {
- pReadFunction(kReadActionUnread, c, pContext);
- break;
- }
- }
- if(fd.mnWidth >= 0)
- pReadFunction(kReadActionUnread, c, pContext);
- nReadCountSum += nReadCount;
- nConversionCount++;
- break;
- }
- case 'c':
- case 'C': // Actually %C is not a standard scanf format.
- {
- // The user can specify %23c to read 23 chars (including spaces) into an array, with no 0-termination.
- if(!fd.mbWidthSpecified)
- fd.mnWidth = 1;
- nReadCount = 0;
- if(pArgumentCurrent) // If we should write the value to a user argument...
- {
- int charTypeSize;
- switch (fd.mModifier)
- {
- case kModifierInt8: // If the user specified %I8c or %I8C
- case kModifierChar: // If the user specified %hc or %hC or kModifierWChar was chosen implicitly for other reasons.
- charTypeSize = 1;
- break;
- case kModifierInt16: // If the user specified %I16c or %I16C
- charTypeSize = 2;
- break;
- case kModifierInt32: // If the user specified %I32c or %I32C
- charTypeSize = 4;
- break;
- case kModifierWChar: // If the user specified %lc or %lC or kModifierWChar was chosen implicitly for other reasons.
- charTypeSize = sizeof(wchar_t);
- break;
- default: // If the user specified %I64c or %I64C or another invalid size.
- //nAssignmentCount = -1; Should we do this?
- goto Done;
- }
- while(fd.mnWidth-- && ((c = pReadFunction(kReadActionRead, 0, pContext)) != -1))
- {
- switch (charTypeSize)
- {
- case 1:
- // Applicable for 16 and 32 bit character string variant
- #if EASTDC_SCANF_WARNINGS_ENABLED
- //if(c > 127)
- // ReportScanfWarning(loss of information);
- #endif
- *((char8_t*)pArgumentCurrent) = (char8_t)(uint8_t)(unsigned)c;
- break;
- case 2:
- // Applicable for 8bit character string variant
- // To do: Support UTF8 sequences.
- // size_t charLength = UTF8CharSize(&c); Do this only for the first char.
- // if(charLength > 1)
- // read into buffer and do a single char Strlcpy from 8 bit to 16 bit.
- // Applicable for 32bit character string variant
- #if EASTDC_SCANF_WARNINGS_ENABLED
- //if(c > 65535)
- // ReportScanfWarning(loss of information);
- #endif
- *((char16_t*)pArgumentCurrent) = (char16_t)c;
- break;
- case 4:
- // Applicable for 8bit character string variant
- // To do: Support UTF8 sequences.
- *((char32_t*)pArgumentCurrent) = (char32_t)c;
- break;
- }
- ++nReadCount;
- }
- if(!nReadCount)
- goto Done;
- nAssignmentCount++;
- }
- else // else ignore the field
- {
- while(fd.mnWidth-- && ((c = pReadFunction(kReadActionRead, 0, pContext)) != -1))
- ++nReadCount;
-
- if(!nReadCount)
- goto Done;
- }
- nReadCountSum += nReadCount;
- nConversionCount++;
- break;
- }
- case kFormatError:
- default:
- goto Done;
- } // switch (fd.mnType)
- } // while(*pFormatCurrent)
- Done:
- if((nConversionCount == 0) && pReadFunction(kReadActionGetLastError, 0, pContext))
- nAssignmentCount = -1;
- pReadFunction(kReadActionEnd, 0, pContext);
- return nAssignmentCount;
- }
- private:
- const CharT* ReadFormat(const CharT* pFormat, FormatData* pFormatData)
- {
- const CharT* pFormatCurrent = pFormat;
- bool bModifierPresent = true; // True until proven false.
- FormatData fd;
- CharT c;
- c = *++pFormatCurrent;
- if(c == '%') // A %% sequence means to simply treat it as a literal '%'.
- {
- fd.mnType = '%';
- *pFormatData = fd;
- return ++pFormatCurrent;
- }
- if(Isdigit(c)) // If the user is specifying a field width...
- {
- // The standard doesn't say anything special about a field width of zero, so we allow it.
- fd.mbWidthSpecified = 1;
- fd.mnWidth = 0;
- do{
- fd.mnWidth = (int)((fd.mnWidth * 10) + (c - '0'));
- c = *++pFormatCurrent;
- } while(Isdigit(c));
- }
- else if(c == '*') // A * char after % means to skip the assignment but eat it from the source data.
- {
- fd.mbSkipAssignment = true;
- c = *++pFormatCurrent;
- }
- // See if the user specified a modifier, such as h, hh, l, ll, or L.
- switch (c)
- {
- case 'h': // handle h and hh
- {
- if(pFormatCurrent[1] == 'h') // If the fd is hh ...
- {
- fd.mModifier = kModifierChar; // Specifies that a following d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to signed char or unsigned char.
- c = *++pFormatCurrent;
- }
- else
- fd.mModifier = kModifierShort; // Specifies that a following d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to short int or unsigned short int.
- break;
- }
- case 'l': // handle l and ll
- {
- if(pFormatCurrent[1] == 'l') // If the fd is ll ...
- {
- fd.mModifier = kModifierLongLong; // Specifies that a following d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to long long int or unsigned long long int.
- c = *++pFormatCurrent;
- }
- else
- fd.mModifier = kModifierLong; // Specifies that a following d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to long int or unsigned long int; that a following a, A, e, E, f, F, g, or G conversion specifier applies to an argument with type pointer to double; or that a following c, s, or [ conversion specifier applies to an argument with type pointer to wchar_t.
- break;
- }
- case 'j':
- // Specifies that a following d, i, o, u, x, or X conversion specifier applies to an intmax_t or uintmax_t argument; or that a following n conversion specifier applies to a pointer to an intmax_t argument.
- fd.mModifier = kModifierMax_t;
- break;
- case 'z':
- // Specifies that a following d, i, o, u, x, or X conversion specifier applies to a size_t or the corresponding signed integer type argument; or that a following n conversion specifier applies to a pointer to a signed integer type corresponding to size_t argument.
- fd.mModifier = kModifierSize_t;
- break;
- case 't':
- // Specifies that a following d, i, o, u, x, or X conversion specifier applies to a ptrdiff_t or the corresponding unsigned integer type argument; or that a following n conversion specifier applies to a pointer to a ptrdiff_t argument.
- fd.mModifier = kModifierPtrdiff_t;
- break;
- case 'L':
- // Specifies that a following a, A, e, E, f, F, g, or G conversion specifier applies to an argument with type pointer to long double.
- fd.mModifier = kModifierLongDouble;
- break;
- case 'I': // We support Microsoft's extension sized fd specifiers.
- if(pFormatCurrent[1] == '8') // If the user specified %I8 ...
- {
- fd.mModifier = kModifierInt8;
- c = *++pFormatCurrent; // Account for the '8' part of I8. We'll account for the 'I' part below for all formats.
- }
- else if((pFormatCurrent[1] == '1') && (pFormatCurrent[2] == '6'))
- {
- fd.mModifier = kModifierInt16;
- c = *(pFormatCurrent += 2);
- }
- else if((pFormatCurrent[1] == '3') && (pFormatCurrent[2] == '2'))
- {
- fd.mModifier = kModifierInt32;
- c = *(pFormatCurrent += 2);
- }
- else if((pFormatCurrent[1] == '6') && (pFormatCurrent[2] == '4'))
- {
- fd.mModifier = kModifierInt64;
- c = *(pFormatCurrent += 2); // Account for the '64' part of I64. We'll account for the 'I' part below for all formats.
- }
- else if((pFormatCurrent[1] == '1') && (pFormatCurrent[2] == '2') && (pFormatCurrent[3] == '8'))
- {
- fd.mModifier = kModifierInt128;
- c = *(pFormatCurrent += 3);
- }
- else // Else the specified modifier was invalid.
- {
- fd.mnType = kFormatError;
- *pFormatData = fd;
- EA_FAIL_MSG("Scanf: Invalid %I modifier");
- return ++pFormatCurrent;
- }
- break;
- default:
- bModifierPresent = false;
- break;
- }
- if(bModifierPresent)
- c = *++pFormatCurrent;
- fd.mnType = (int)c;
- switch (c)
- {
- case 'b': // 'b' means binary. This is a convenient extension that we provide.
- case 'd':
- case 'u':
- case 'i':
- case 'x':
- case 'X':
- case 'o':
- if(fd.mModifier == kModifierLongDouble)
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Invalid %b/%d/%u/%i/%x/%o modifier");
- }
- break;
- case 'c': // We accept %hc, %c, %lc, %I8c, %I16c, %I32c (regular, regular, wide, char8_t, char16_t, char32_t)
- case 'C': // We accept %hC, %C, %lC, %I8C, %I16C, %I32C (regular, wide, wide, char8_t, char16_t, char32_t)
- case 's': // We accept %hs, %s, %ls, %I8s, %I16s, %I32s (regular, regular, wide, char8_t, char16_t, char32_t)
- case 'S': // We accept %hS, %S, %lS, %I8s, %I16s, %I32s (regular, wide, wide, char8_t, char16_t, char32_t)
- {
- // Microsoft's library goes against the C and C++ standard: %s is
- // not interpreted to mean char8_t string but instead is interpreted
- // to be either char8_t or wchar_t depending on what the output
- // text fd is. This is non-standard but has the convenience
- // of allowing users to migrate between char8_t and wchar_t usage
- // more easily. So we allow EASCANF_MS_STYLE_S_FORMAT to control this.
- if(fd.mModifier == kModifierLong)
- fd.mModifier = kModifierWChar;
- else if(fd.mModifier == kModifierShort)
- fd.mModifier = kModifierChar;
- else if(fd.mModifier == kModifierNone)
- {
- #if EASCANF_MS_STYLE_S_FORMAT
- if((c == 's') || (c == 'c'))
- fd.mModifier = (sizeof(*pFormat) == sizeof(char8_t)) ? kModifierChar : kModifierWChar;
- else
- fd.mModifier = (sizeof(*pFormat) == sizeof(char8_t)) ? kModifierWChar : kModifierChar;
- #else
- if((c == 's') || (c == 'c'))
- fd.mModifier = kModifierChar;
- else
- fd.mModifier = kModifierWChar;
- #endif
- }
- else if((fd.mModifier < kModifierInt8) || (fd.mModifier > kModifierInt32)) // This expression assumes that Int8, Int16, Int32 are contiguous enum values.
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Invalid %s/%c modifier");
- }
- if((c == 's') || (c == 'S'))
- {
- // We make %s be a special case of %[] whereby all non-space characters are accepted.
- // fd.mCharBitmap.SetAll();
- // fd.mCharBitmap.Clear(0x09); // Set tab (0x09),
- // fd.mCharBitmap.Clear(0x0a); // LF (0x0a),
- // fd.mCharBitmap.Clear(0x0b); // VT (0x0b),
- // fd.mCharBitmap.Clear(0x0c); // FF (0x0c),
- // fd.mCharBitmap.Clear(0x0d); // CR (0x0d),
- // fd.mCharBitmap.Clear(0x20); // space (0x20) to zero.
- // Pre-calculated version of above:
- fd.mCharBitmap.mBits[0] = 0xffffc1ff;
- fd.mCharBitmap.mBits[1] = 0xfffffffe;
- fd.mCharBitmap.mBits[2] = 0xffffffff;
- fd.mCharBitmap.mBits[3] = 0xffffffff;
- fd.mCharBitmap.mBits[4] = 0xffffffff;
- fd.mCharBitmap.mBits[5] = 0xffffffff;
- fd.mCharBitmap.mBits[6] = 0xffffffff;
- fd.mCharBitmap.mBits[7] = 0xffffffff;
- }
- break;
- }
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'g':
- case 'G':
- case 'a':
- case 'A':
- // The C99 Standard, section 7.24.2.2, specifies that %l and %L are the only modifiers that
- // affect floating point types. %f = float, %lf = double, %Lf = long double. It doesn't say
- // what the expected result is for using other modifiers with floating point types. We can
- // choose to ignore these types or yield an error. We give an error. The VC++ Standard
- // Library has inconsistent behaviour: it ignores the h in %hf, but in the case of %llf it
- // reinterprets the format to be %lli.
- if(fd.mModifier == kModifierLong) // if %lf ...
- fd.mModifier = kModifierDouble;
- else if((fd.mModifier != kModifierLongDouble) && // if not %Lf ...
- (fd.mModifier != kModifierNone))
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Invalid %e/%f/%g/%a modifier");
- }
- break;
- case 'p':
- if(sizeof(void*) == 2)
- fd.mModifier = kModifierInt16;
- else if(sizeof(void*) == 4)
- fd.mModifier = kModifierInt32;
- else
- fd.mModifier = kModifierInt64;
- fd.mnType = 'x';
- break;
- case '[':
- {
- // The C99 standard states (7.19.6.2 p12):
- // Matches a non-empty sequence of bytes from a set of expected bytes (the scanset). The normal skip over
- // white-space characters shall be suppressed in this case. The application shall ensure that the
- // corresponding argument is a pointer to the initial byte of an array of char, signed char, or unsigned char
- // large enough to accept the sequence and a terminating null byte, which shall be added automatically.
- //
- // If an l (ell) qualifier is present, the input is a sequence of characters that begins in the initial shift state.
- // Each character in the sequence shall be converted to a wide character as if by a call to the mbrtowc() function,
- // with the conversion state described by an mbstate_t object initialized to zero before the first character is converted.
- // The application shall ensure that the corresponding argument is a pointer to an array of wchar_t large enough to
- // accept the sequence and the terminating null wide character, which shall be added automatically.
- //
- // The conversion specification includes all subsequent bytes in the fd string up to and including the matching
- // right square bracket (']'). The bytes between the square brackets (the scanlist) comprise the scanset,
- // unless the byte after the left square bracket is a circumflex ('^'), in which case the scanset contains all
- // bytes that do not appear in the scanlist between the circumflex and the right square bracket. If the conversion
- // specification begins with "[]" or "[^]", the right square bracket is included in the scanlist and the next right
- // square bracket is the matching right square bracket that ends the conversion specification; otherwise, the first
- // right square bracket is the one that ends the conversion specification. If a '-' is in the scanlist and is not
- // the first character, nor the second where the first character is a '^', nor the last character, the behavior is
- // implementation-defined.
- bool bInclusive = true;
- if(fd.mModifier == kModifierShort)
- fd.mModifier = kModifierChar;
- else if(fd.mModifier == kModifierLong)
- fd.mModifier = kModifierWChar;
- else if(fd.mModifier == kModifierNone)
- {
- #if EASCANF_MS_STYLE_S_FORMAT
- fd.mModifier = (sizeof(*pFormat) == sizeof(char8_t)) ? kModifierChar : kModifierWChar;
- #else
- fd.mModifier = (sizeof(*pFormat) == sizeof(char16_t)) ? kModifierWChar : kModifierChar; //TODO: This condition seems odd, needs review.
- #endif
- }
- else if((fd.mModifier < kModifierInt8) || (fd.mModifier > kModifierInt32)) // This expression assumes that Int8, Int16, Int32 are contiguous enum values.
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Invalid %[ modifier");
- }
- c = *++pFormatCurrent;
- if(c == '^')
- {
- bInclusive = false;
- c = *++pFormatCurrent;
- }
- if(c == ']') // The C99 standard requires that if there is an excluded ']' char, then it is the first char after [ or [^.
- {
- fd.mCharBitmap.Set((CharT)']');
- c = *++pFormatCurrent;
- }
- // To do: We need to read UTF8 character sequences here instead of just ascii values.
- EA_ASSERT((sizeof(CharT) != sizeof(char8_t)) || ((uint8_t)(char8_t)c < 128)); // A c >= 128 refers to a UTF8 sequence, which we don't yet support.
- while(c && (c != ']')) // Walk through the characters until we encounter a closing ']' char. Use '-' char to indicate character ranges, as in "a-d"
- {
- fd.mCharBitmap.Set(c);
- if((pFormatCurrent[1] == '-') && pFormatCurrent[2] && (pFormatCurrent[2] != ']')) // If we have a character range specifier...
- {
- while(++c <= pFormatCurrent[2])
- fd.mCharBitmap.Set(c);
- pFormatCurrent += 2;
- }
- c = *++pFormatCurrent;
- }
- if(c) // At this point, c should be ']'
- {
- if(!bInclusive)
- fd.mCharBitmap.NegateAll();
- }
- else
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Missing format ] char");
- }
- break;
- }
- case 'n':
- {
- // The C99 standard states (7.19.6.2 p12): No input is consumed. The application shall ensure
- // that the corresponding argument is a pointer to the integer into which shall be written the
- // number of bytes read from the input so far by this call to the fscanf() functions.
- // Execution of a %n conversion specification shall not increment the assignment nReadCount returned
- // at the completion of execution of the function. No argument shall be converted, but one shall
- // be consumed. If the conversion specification includes an assignment-suppressing character or
- // a field width, the behavior is undefined.
- break;
- }
- default:
- {
- fd.mnType = kFormatError;
- EA_FAIL_MSG("Scanf: Invalid format.");
- break;
- }
- }
- *pFormatData = fd;
- return ++pFormatCurrent;
- }
- uint64_t ReadUint64(ReadFunctionT pReadFunction, void* pContext,
- uint64_t nMaxValue, int nBase, int nMaxFieldWidth,
- int& nReadCount, int& bNegative, int& bIntegerOverflow)
- {
- ReadIntegerState state = kRISError;
- uint64_t nValue = 0;
- int nSpaceCount = 0;
- const int kRISDone = kRISError | kRISEnd;
- const int kRISSuccess = kRISAfterZero | kRISReadDigits | kRISEnd;
- nReadCount = 0;
- bNegative = 0;
- bIntegerOverflow = 0;
- if((nBase != 1) && (nBase <= 36) && (nMaxFieldWidth >= 1))
- {
- uint64_t nMaxValueCheck = 0; // This is what we compare nValue to as we build nValue. It is always equal to nValue / nBase. We need to do this because otherwise we'd compare overflowed values.
- int c;
- state = kRISLeadingSpace;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- if(nBase)
- nMaxValueCheck = (nMaxValue / nBase);
- while((c != kReadError) && (nReadCount <= nMaxFieldWidth) && ((state & kRISDone) == 0))
- {
- switch ((int)state)
- {
- case kRISLeadingSpace:
- {
- if(Isspace((CharT)c))
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nSpaceCount++;
- // Stay in this state and read another char.
- }
- else
- {
- if(c == '-')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- bNegative = 1;
- }
- else if(c == '+')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- }
- state = kRISZeroTest;
- }
- break;
- }
- case kRISZeroTest:
- {
- if(((nBase == 0) || (nBase == 16)) && (c == '0')) // If nBase == 0, then we should expect hex (0x1234) or octal (01234)
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- state = kRISAfterZero;
- }
- else
- {
- if(nBase == 0) // If the base hasn't been determined yet (e.g. by leading "0x"), then it must be 10.
- nBase = 10;
- if(nMaxValueCheck == 0) // If this hasn't been set yet...
- nMaxValueCheck = (nMaxValue / nBase);
- state = kRISReadFirstDigit;
- }
- break;
- }
- case kRISAfterZero:
- {
- if((c == 'x') || (c == 'X'))
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- nBase = 16;
- state = kRISReadFirstDigit;
- }
- else
- {
- if(nBase == 0)
- nBase = 8;
- state = kRISReadDigits;
- }
- if(nMaxValueCheck == 0) // If this hasn't been set yet...
- nMaxValueCheck = (nMaxValue / nBase);
- break;
- }
- case kRISReadFirstDigit:
- case kRISReadDigits:
- {
- const int cDigit = (c - '0');
- if((unsigned)cDigit < 10) // If c is compatible with base 2, 8, 10 ...
- {
- if(cDigit >= nBase)
- {
- if(state == kRISReadDigits)
- state = kRISEnd;
- else
- state = kRISError;
- break;
- }
- c = cDigit;
- }
- else //Else we may have a hex digit, but we only pay attention to it if it's compatible with nBase (e.g. base 16).
- {
- int cHex; // Actually it might be something other than hex if we are using a screwy base such as 18.
- CharT cLower;
- // If the base is > 10 and if c is a char that can represent a digit in the base...
- if((nBase > 10) && ((cLower = Tolower((CharT)c)) >= 'a') && ((cHex = (10 + (int)cLower - 'a')) < nBase))
- c = cHex;
- else
- {
- if(state == kRISReadDigits)
- state = kRISEnd;
- else
- state = kRISError;
- break;
- }
- }
- if(nValue > nMaxValueCheck)
- bIntegerOverflow = 1;
- nValue *= nBase;
- EA_ASSERT(c >= 0);
- if((unsigned)c > (nMaxValue - nValue))
- bIntegerOverflow = 1;
- nValue += c;
- state = kRISReadDigits;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nReadCount++;
- break;
- }
- } // switch()
- } // while()
- // Return the final char back to the stream. It will typically be a 0 (nul terminator) char.
- pReadFunction(kReadActionUnread, c, pContext);
- } // if()
- if(state & kRISSuccess)
- nReadCount += nSpaceCount - 1; // -1 because we un-read the last char above.
- else
- {
- nValue = 0;
- nReadCount = 0;
- }
- return nValue;
- }
- double ReadDouble(ReadFunctionT pReadFunction, void* pContext,
- int nMaxFieldWidth, int cDecimalPoint, int& nReadCount, int& bOverflow)
- {
- int c;
- DoubleValue doubleValue; // The string representation of the value, to be converted to actual value.
- double dValue = 0.0;
- int nSpaceCount = 0;
- int nSignCount = 0; // There's supposed to be just zero or one of these.
- int nFieldCount = 0;
- int nExponent = 0;
- int nExponentAdd = 0;
- bool bNegative = false;
- bool bExponentNegative = false;
- ReadDoubleState state = kRDSLeadingSpace;
- const int kRDSDone = kRDSError | kRDSEnd;
- const int kRDSSuccess = kRDSSignificandLeading | kRDSIntegerDigits |
- kRDSFractionLeading | kRDSFractionDigits |
- kRDSExponentLeading | kRDSExponentDigits |
- kRDSEnd;
- nReadCount = 0;
- bOverflow = 0;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- while((c != kReadError) && (nFieldCount <= nMaxFieldWidth) && !(state & kRDSDone))
- {
- switch((int)state)
- {
- case kRDSLeadingSpace:
- {
- if(Isspace((CharT)c))
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nSpaceCount++;
- break;
- }
- switch(c)
- {
- case '-':
- bNegative = true;
- // Fall through
- case '+':
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- nSignCount++;
- break;
- case 'i': // Start of an "INF" or "INFINITY" sequence.
- case 'I':
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSInfinity;
- break;
- case 'n': // Start of an "NAN" or "NAN(...)" sequence.
- case 'N':
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSNAN;
- break;
- default:
- state = kRDSSignificandBegin;
- break;
- }
- break;
- }
- case kRDSSignificandBegin:
- {
- if(c == cDecimalPoint) // If there is no significand but there is instead the fraction-starting '.' char...
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSFractionBegin;
- }
- else if(c == '0')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSSignificandLeading; // We eat leading zeroes.
- }
- else if(Isdigit((CharT)c))
- state = kRDSIntegerDigits;
- else
- state = kRDSError;
- break;
- }
- case kRDSSignificandLeading:
- {
- if(c == '0')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else
- state = kRDSIntegerDigits;
- break;
- }
- case kRDSIntegerDigits:
- {
- if(Isdigit((CharT)c))
- {
- if(doubleValue.mSigLen < kMaxSignificandDigits) // If we have any more room...
- doubleValue.mSigStr[doubleValue.mSigLen++] = (char8_t)c;
- else
- nExponentAdd++; // Lose significant digits but increase the exponent multiplier, so that the final result is close intended value, though chopped.
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else
- {
- if(c == cDecimalPoint)
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSFractionDigits;
- }
- else
- state = kRDSSignificandEnd;
- }
- break;
- }
- case kRDSFractionBegin:
- {
- if(Isdigit((CharT)c))
- state = kRDSFractionDigits;
- else
- state = kRDSError;
- break;
- }
- case kRDSFractionDigits:
- {
- if(Isdigit((CharT)c))
- {
- if(doubleValue.mSigLen < kMaxSignificandDigits) // If we have any more room...
- {
- nExponentAdd--; // Fractional digits reduce our multiplier.
- if((c != '0') || doubleValue.mSigLen)
- doubleValue.mSigStr[doubleValue.mSigLen++] = (char8_t)c;
- } // Else lose the remaining fractional part.
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else
- state = kRDSSignificandEnd;
- break;
- }
- case kRDSSignificandEnd:
- {
- if(Toupper((CharT)c) == 'E')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSExponentBegin;
- break;
- }
- state = kRDSEnd;
- break;
- }
- case kRDSExponentBegin:
- {
- if(c == '+')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else if(c == '-')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- bExponentNegative = 1;
- }
- state = kRDSExponentBeginDigits;
- break;
- }
- case kRDSExponentBeginDigits:
- {
- if(c == '0')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- state = kRDSExponentLeading;
- }
- else if(Isdigit((CharT)c))
- state = kRDSExponentDigits;
- else
- state = kRDSError;
- break;
- }
- case kRDSExponentLeading:
- {
- if(c == '0')
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else
- state = kRDSExponentDigits;
- break;
- }
- case kRDSExponentDigits:
- {
- if(Isdigit((CharT)c))
- {
- nExponent = (nExponent * 10) + (c - '0');
- if(nExponent > kMaxDoubleExponent)
- bOverflow = 1;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- else
- state = kRDSEnd;
- break;
- }
- case kRDSInfinity:
- {
- // The C99 Standard specifies that we accept "INF" or "INFINITY", ignoring case.
- int i = 1; // We have already matched the first 'I' char.
- while((i < 8) && ((int)Toupper((CharT)c) == (int)"INFINITY"[i]))
- {
- i++;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- }
- if((i == 3) || (i == 8))
- {
- if(bNegative)
- dValue = -kFloat64Infinity;
- else
- dValue = kFloat64Infinity;
- nReadCount = nSpaceCount + nSignCount + i;
- return dValue; // Question: Do some FPUs refuse to accept infinity as-is?
- }
- else
- state = kRDSError;
- break;
- }
- case kRDSNAN:
- {
- // The C99 Standard specifies that we accept "NAN" or "NAN(n-char-sequence)", ignoring case. The n-char-sequence is implementation-defined but is a string specifying a particular NAN.
- int i = 1; // We have already matched the first 'N' char.
- int j = 0;
- //CharT pNANString[24]; pNANString[0] = 0;
- while((i < 4) && ((int)Toupper((CharT)c) == (int)"NAN("[i]))
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- i++;
- }
- if((i == 3) || (i == 4))
- {
- if(i == 4)
- {
- while((j < 32) && (Isdigit((CharT)c) || Isalpha((CharT)c)))
- {
- //pNANString[j] = (char16_t)c;
- c = pReadFunction(kReadActionRead, 0, pContext);
- nFieldCount++;
- j++;
- }
- if(c != ')')
- {
- state = kRDSError;
- break;
- }
- else
- j++;
- }
- // We currently ignore pNANString. To consider: Support some explicit NAN types based on the content of pNANString.
- //pNANString[j] = 0;
- if(bNegative)
- dValue = -kFloat64NAN;
- else
- dValue = kFloat64NAN;
- nReadCount = nSpaceCount + nSignCount + i + j;
- return dValue; // Question: Don't some FPUs refuse to accept NANs as-is?
- }
- else
- state = kRDSError;
- break;
- }
- } // switch()
- } // while()
- // Return the final char back to the stream. It will typically be a 0 (nul terminator) char.
- pReadFunction(kReadActionUnread, c, pContext);
- if(state & kRDSSuccess)
- {
- nFieldCount--;
- nReadCount = nFieldCount + nSpaceCount;
- }
- else
- {
- nFieldCount = 0;
- nReadCount = 0;
- }
- if(bExponentNegative)
- nExponent = -nExponent;
- // We've got a mSigStr/mExponent that is something like "123"/0 for the case of "123"
- // We've got a mSigStr/mExponent that is something like "123456"/-3 for the case of "123.456"
- // We remove trailing zeroes until we have at most just one trailing zero.
- int i = doubleValue.mSigLen - 1;
- while((i > 0) && (doubleValue.mSigStr[i] == '0'))
- {
- nExponentAdd++;
- i--;
- }
- if(i >= 0)
- doubleValue.mSigLen = (int16_t)(i + 1);
- else
- {
- // In this case we have no significand or a significand of all zeroes.
- // Thus we have zero with some exponent, and the result is always zero,
- // even if we had some kind of apparent exponent overflow.
- bOverflow = false;
- return bNegative ? -0.0 : 0.0; // Usage of -0.0 requires that precise floating point be enabled under VC++. We ensure that above via EA_ENABLE_PRECISE_FP().
- // Alternative processing:
- // doubleValue.mSigLen = 1;
- // doubleValue.mSigStr[0] = '0'; // Leave nExponent as it is, which is probably zero.
- }
- doubleValue.mExponent = (int16_t)(nExponent + nExponentAdd);
- if((doubleValue.mExponent < kMinDoubleExponent) || (doubleValue.mExponent > kMaxDoubleExponent))
- bOverflow = 1;
- // Note that it's still possible to have overflow if the exponent is present and less than max.
- if(bOverflow)
- {
- if(bExponentNegative) // If the value is very small, return zero.
- return 0.0;
- else
- {
- // We don't set errno here; instead we set it in the caller.
- // errno = ERANGE; // C99 standard, section 7.20.1.3-10
- if(bNegative) // If the value is a very large negative value...
- return -EASTDC_HUGE_VAL; // The C99 Standard (7.20.1.3-10) specifies that we return HUGE_VAL in the case of overflow.
- else // Else the value is a very large positive value.
- return EASTDC_HUGE_VAL;
- }
- }
- dValue = doubleValue.ToDouble();
- if(dValue > DBL_MAX) // If the value is denormalized too big...
- {
- // We don't set errno here; instead we set it in the caller.
- //errno = ERANGE; // C99 standard, section 7.20.1.3-10
- bOverflow = 1;
- dValue = EASTDC_HUGE_VAL;
- }
- else if((dValue != 0.0) && (dValue < DBL_MIN)) // If the value is denormalized too small...
- {
- // We don't set errno here; instead we set it in the caller.
- //errno = ERANGE; // C99 standard, section 7.20.1.3-10
- bOverflow = 1;
- //dValue = 0.0; // "If the result underflows (7.12.1), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined."
- }
- if(bNegative)
- dValue = -dValue;
- return dValue;
- }
- };
- bool ReadFormatSpan8(FormatData& fd, int& c, ReadFunction8 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount)
- {
- while(fd.mnWidth-- && ((c = pReadFunction(kReadActionRead, 0, pContext)) != -1) && fd.mCharBitmap.Get((char8_t)c))
- {
- uint8_t c8 = (uint8_t)c; // It's easier to work with uint8_t instead of char8_t, which might be signed.
- switch (stringTypeSize)
- {
- case 1:
- *((uint8_t*)pArgumentCurrent) = c8;
- ++pArgumentCurrent;
- break;
- case 2:
- case 4:
- {
- if(c8 < 128)
- {
- if(stringTypeSize == 2)
- *((char16_t*)pArgumentCurrent) = c8;
- else
- *((char32_t*)pArgumentCurrent) = c8;
- }
- else
- {
- // We need to convert from UTF8 to UCS here. However, this can be complicated because
- // multiple UTF8 chars may correspond to a single UCS char. Luckily, the UTF8 format
- // allows us to know how many chars are in a multi-byte sequence based on the char value.
- char8_t buffer[7];
- const size_t utf8Len = utf8lengthTable[c8];
- char16_t c16[2];
- char32_t c32[2];
- int result;
- buffer[0] = (char8_t)c8;
- for(size_t i = 1; i < utf8Len; ++i)
- {
- c = pReadFunction(kReadActionRead, 0, pContext);
- if(c < 0)
- return false;
-
- ++nReadCount;
- buffer[i] = (char8_t)c;
- }
-
- if(stringTypeSize == 2)
- result = Strlcpy(c16, buffer, 2, utf8Len);
- else
- result = Strlcpy(c32, buffer, 2, utf8Len);
- if(result < 0) // If the UTF8 sequence was malformed...
- return false;
- if(stringTypeSize == 2)
- *((char16_t*)pArgumentCurrent) = c16[0];
- else
- *((char32_t*)pArgumentCurrent) = c32[0];
- }
- pArgumentCurrent += stringTypeSize;
- break;
- }
- }
- ++nReadCount;
- }
- return true;
- }
- bool ReadFormatSpan16(FormatData& fd, int& c, ReadFunction16 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount)
- {
- while (fd.mnWidth-- && ((c = pReadFunction(kReadActionRead, 0, pContext)) != -1) && fd.mCharBitmap.Get((char16_t)c))
- {
- char16_t c16 = (char16_t)(unsigned)c;
- switch (stringTypeSize)
- {
- case 1:
- // We need to convert from UCS2 to UTF8 here. One UCS2 char may convert to
- // as many as six UTF8 chars (though usually no more than three).
- // This Strlcpy (16 to 8) can never fail.
- pArgumentCurrent += Strlcpy((char8_t*)pArgumentCurrent, &c16, 7, 1);
- break;
- case 2:
- *((char16_t*)pArgumentCurrent) = (char16_t)c16;
- pArgumentCurrent += sizeof(char16_t);
- break;
- case 4:
- *((char32_t*)pArgumentCurrent) = c16;
- pArgumentCurrent += sizeof(char32_t);
- break;
- }
- ++nReadCount;
- }
- return true;
- }
- bool ReadFormatSpan32(FormatData& fd, int& c, ReadFunction32 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount)
- {
- while (fd.mnWidth-- && ((c = pReadFunction(kReadActionRead, 0, pContext)) != -1) && fd.mCharBitmap.Get((char32_t)c))
- {
- char32_t c32 = (char32_t)(unsigned)c;
- switch (stringTypeSize)
- {
- case 1:
- // We need to convert from UCS4 to UTF8 here. One UCS4 char may convert to
- // as many as six UTF8 chars (though usually no more than three).
- // This Strlcpy (32 to 8) can never fail.
- pArgumentCurrent += Strlcpy((char8_t*)pArgumentCurrent, &c32, 7, 1);
- break;
- case 2:
- *((char16_t*)pArgumentCurrent) = (char16_t)c32;
- pArgumentCurrent += sizeof(char16_t);
- break;
- case 4:
- *((char32_t*)pArgumentCurrent) = c32;
- pArgumentCurrent += sizeof(char32_t);
- break;
- }
- ++nReadCount;
- }
- return true;
- }
- typedef bool(*ReadFormatSpanFunction8)(FormatData& fd, int& c, ReadFunction8 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount);
- typedef bool(*ReadFormatSpanFunction16)(FormatData& fd, int& c, ReadFunction16 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount);
- typedef bool(*ReadFormatSpanFunction32)(FormatData& fd, int& c, ReadFunction32 pReadFunction, void* pContext, int stringTypeSize, char*& pArgumentCurrent, int& nReadCount);
- int VscanfCore(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, va_list arguments)
- {
- VscanfUtil<ReadFunction8, ReadFormatSpanFunction8, char8_t> scanner;
- return scanner.VscanfCore(pReadFunction8, ReadFormatSpan8, pContext, pFormat, arguments);
- }
- int VscanfCore(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments)
- {
- VscanfUtil<ReadFunction16, ReadFormatSpanFunction16, char16_t> scanner;
- return scanner.VscanfCore(pReadFunction16, ReadFormatSpan16, pContext, pFormat, arguments);
- }
- int VscanfCore(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, va_list arguments)
- {
- VscanfUtil<ReadFunction32, ReadFormatSpanFunction32, char32_t> scanner;
- return scanner.VscanfCore(pReadFunction32, ReadFormatSpan32, pContext, pFormat, arguments);
- }
- // Undo the floating point precision statement we made above with EA_ENABLE_PRECISE_FP.
- EA_RESTORE_PRECISE_FP()
- } // namespace ScanfLocal
- } // namespace StdC
- } // namespace EA
- EA_RESTORE_VC_WARNING()
|