Roberto Parolin 6 rokov pred
commit
ee6fcc3c98
99 zmenil súbory, kde vykonal 72821 pridanie a 0 odobranie
  1. 49 0
      .gitignore
  2. 110 0
      doc/EAAlignment.html
  3. 151 0
      doc/EABitTricks.html
  4. 107 0
      doc/EAByteCrackers.html
  5. 235 0
      doc/EACType.html
  6. 353 0
      doc/EADateTime.html
  7. 514 0
      doc/EAFixedPoint.html
  8. 157 0
      doc/EAGlobal.html
  9. 65 0
      doc/EAHash.html
  10. 232 0
      doc/EAMathHelp.html
  11. 144 0
      doc/EAMemory.html
  12. 85 0
      doc/EAOSGlobal.html
  13. 374 0
      doc/EARandom.html
  14. 146 0
      doc/EAScanf.html
  15. 279 0
      doc/EASprintf.html
  16. BIN
      doc/EAStdC Index.pdf
  17. 81 0
      doc/EAStdC.html
  18. 289 0
      doc/EAStopwatch.html
  19. 298 0
      doc/EAString.html
  20. 724 0
      doc/EATextUtil.html
  21. 219 0
      doc/Int128_t.html
  22. 83 0
      doc/UTFDoc.css
  23. 698 0
      include/EAStdC/EAAlignment.h
  24. 1570 0
      include/EAStdC/EABitTricks.h
  25. 99 0
      include/EAStdC/EAByteCrackers.h
  26. 536 0
      include/EAStdC/EACType.h
  27. 556 0
      include/EAStdC/EACallback.h
  28. 720 0
      include/EAStdC/EADateTime.h
  29. 1124 0
      include/EAStdC/EAEndian.h
  30. 1018 0
      include/EAStdC/EAFixedPoint.h
  31. 806 0
      include/EAStdC/EAGlobal.h
  32. 197 0
      include/EAStdC/EAHashCRC.h
  33. 247 0
      include/EAStdC/EAHashString.h
  34. 480 0
      include/EAStdC/EAMathHelp.h
  35. 681 0
      include/EAStdC/EAMemory.h
  36. 385 0
      include/EAStdC/EAProcess.h
  37. 583 0
      include/EAStdC/EARandom.h
  38. 445 0
      include/EAStdC/EARandomDistribution.h
  39. 217 0
      include/EAStdC/EAScanf.h
  40. 198 0
      include/EAStdC/EASingleton.h
  41. 326 0
      include/EAStdC/EASprintf.h
  42. 125 0
      include/EAStdC/EASprintfOrdered.h
  43. 51 0
      include/EAStdC/EAStdC.h
  44. 866 0
      include/EAStdC/EAStopwatch.h
  45. 2804 0
      include/EAStdC/EAString.h
  46. 501 0
      include/EAStdC/EATextUtil.h
  47. 544 0
      include/EAStdC/Int128_t.h
  48. 261 0
      include/EAStdC/Win32/EAMathHelpWin32.inl
  49. 722 0
      include/EAStdC/internal/Config.h
  50. 158 0
      include/EAStdC/internal/EAMemory.inl
  51. 706 0
      include/EAStdC/internal/IntrusiveList.h
  52. 321 0
      include/EAStdC/internal/ScanfCore.h
  53. 305 0
      include/EAStdC/internal/SprintfCore.h
  54. 85 0
      include/EAStdC/internal/Thread.h
  55. 44 0
      include/EAStdC/internal/stdioEA.h
  56. 15 0
      include/EAStdC/version.h
  57. 121 0
      source/EACType.cpp
  58. 874 0
      source/EACallback.cpp
  59. 2403 0
      source/EADateTime.cpp
  60. 547 0
      source/EAFixedPoint.cpp
  61. 910 0
      source/EAGlobal.cpp
  62. 401 0
      source/EAHashCRC.cpp
  63. 326 0
      source/EAHashString.cpp
  64. 1010 0
      source/EAMemory.cpp
  65. 1075 0
      source/EAProcess.cpp
  66. 431 0
      source/EARandom.cpp
  67. 292 0
      source/EAScanf.cpp
  68. 1947 0
      source/EAScanfCore.cpp
  69. 657 0
      source/EASprintf.cpp
  70. 1959 0
      source/EASprintfCore.cpp
  71. 1560 0
      source/EASprintfOrdered.cpp
  72. 44 0
      source/EAStdC.cpp
  73. 723 0
      source/EAStopwatch.cpp
  74. 6103 0
      source/EAString.cpp
  75. 1840 0
      source/EATextUtil.cpp
  76. 3879 0
      source/Int128_t.cpp
  77. 200 0
      test/include/EAStdCTest/EAStdCTest.h
  78. 206 0
      test/source/EAStdCTest.cpp
  79. 490 0
      test/source/TestAlignment.cpp
  80. 1309 0
      test/source/TestBitTricks.cpp
  81. 108 0
      test/source/TestByteCrackers.cpp
  82. 233 0
      test/source/TestCType.cpp
  83. 446 0
      test/source/TestCallback.cpp
  84. 1837 0
      test/source/TestDateTime.cpp
  85. 836 0
      test/source/TestEndian.cpp
  86. 220 0
      test/source/TestFixedPoint.cpp
  87. 148 0
      test/source/TestGlobal.cpp
  88. 314 0
      test/source/TestHash.cpp
  89. 1122 0
      test/source/TestInt128.cpp
  90. 484 0
      test/source/TestMathHelp.cpp
  91. 1309 0
      test/source/TestMemory.cpp
  92. 213 0
      test/source/TestProcess.cpp
  93. 1166 0
      test/source/TestRandom.cpp
  94. 3001 0
      test/source/TestScanf.cpp
  95. 77 0
      test/source/TestSingleton.cpp
  96. 2784 0
      test/source/TestSprintf.cpp
  97. 411 0
      test/source/TestStopwatch.cpp
  98. 4304 0
      test/source/TestString.cpp
  99. 1412 0
      test/source/TestTextUtil.cpp

+ 49 - 0
.gitignore

@@ -0,0 +1,49 @@
+tags
+cscope.out
+**/*.swp
+**/*.swo
+.swp
+*.swp
+.swo
+.TMP
+-.d
+eastl_build_out
+build_bench
+bench.bat
+build.bat
+.p4config
+
+## CMake generated files
+CMakeCache.txt
+cmake_install.cmake
+
+## Patch files
+*.patch
+
+## For Visual Studio Generated projects
+*.sln
+**/*.vcxproj
+**/*.vcxproj.filters
+*.VC.opendb
+*.sdf
+**/*.suo
+**/*.user
+.vs/*
+**/Debug/*
+CMakeFiles/*
+EASTL.dir/**
+RelWithDebInfo/*
+Release/*
+Win32/*
+x64/*
+MinSizeRel/*
+build*/*
+Testing/*
+%ALLUSERSPROFILE%/*
+
+# Buck
+/buck-out/
+/.buckd/
+/buckaroo/
+.buckconfig.local
+BUCKAROO_DEPS

+ 110 - 0
doc/EAAlignment.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAAlignment</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAAlignment</h1>
+
+<h2>Introduction</h2>
+<p>EAAlignment provides a number of utilities for working with variable alignment. These include:</p>
+<blockquote>
+  <table border="1">
+    <tr>
+      <td><strong>Entity</strong></td>
+      <td><strong>Description</strong></td>
+    </tr>
+    <tr>
+      <td>EAAlignOf(type)</td>
+      <td valign="top">Macro which returns the alignment of the given type as a constant expression. </td>
+    </tr>
+    <tr>
+      <td>AlignOf&lt;T&gt;</td>
+      <td valign="top">Template which returns the alignment of the given type. </td>
+    </tr>
+    <tr>
+      <td>AlignAddressUp<br>
+      AlignAddressDown</td>
+      <td valign="top">Function which aligns an arbitrary address up or down to the next user-supplied power of two. </td>
+    </tr>
+    <tr>
+      <td>AlignObjectUp<br>
+      AlignObjectDown</td>
+      <td valign="top">Function which aligns an arbitrary object up or down to the next user-supplied power of two. </td>
+    </tr>
+    <tr>
+      <td>GetAlignment<br>
+        IsAddressAligned<br>
+        IsObjectAligned&lt;T&gt;<br>
+      IsAligned&lt;T&gt;<br></td>
+      <td valign="top">Gets information about alignment. </td>
+    </tr>
+    <tr>
+      <td>AlignedType</td>
+      <td valign="top">Template which portably allows the re-typing of a class to have a specific alignment. </td>
+    </tr>
+    <tr>
+      <td>AlignedArray</td>
+      <td valign="top">Template which implements an array of an arbitrary class with a given alignment.</td>
+    </tr>
+    <tr>
+      <td>AlignedObject</td>
+      <td valign="top">Template which implements an instance of an arbitrary class with a given alignment.</td>
+    </tr>
+    <tr>
+      <td>ReadMisalignedUint16<br>
+      ReadMisalignedUint32<br>
+      ReadMisalignedUint64</td>
+      <td valign="top">Function which safely and portably reads potentially misaligned memory. </td>
+    </tr>
+    <tr>
+      <td>WriteMisalignedUint16<br>
+      WriteMisalignedUint32<br>
+      WriteMisalignedUint64</td>
+      <td valign="top">Function which safely and portably writes potentially misaligned memory. </td>
+    </tr>
+  </table>
+</blockquote>
+<p>We'll discuss some of these briefly.</p>
+<h2>EAAlignOf</h2>
+<p>EAAlignOf is your basic macro for retrieving the alignment of an object by its type. Recent versions of EABase define EA_ALIGN_OF, so EAAlignOf is currently a duplication of the EABase functionality. </p>
+<p>Example usage: </p>
+<pre class="code-example">printf(&quot;Alignment of type 'int' is %u.\n&quot;, (unsigned)EAAlignOf(int)); </pre>
+<h2>AlignedType</h2>
+<p> This class makes an aligned typedef for a given class based on a user-supplied class and alignment. This class exists because of a weakness in VC++ whereby it can only accept __declspec(align) and thus EA_PREFIX_ALIGN usage via an integer literal (e.g. &quot;16&quot;) and not an otherwise equivalent constant integral expression (e.g. sizeof Foo).</p>
+<p>Example usage: </p>
+<pre class="code-example">const size_t kAlignment = 32; <span class="code-example-comment">// Note that in this case the alignment is defined elsewhere as a non-literal.</span><br>
+AlignedType&lt;Widget, kAlignment&gt;::Type widgetAlign128;<br>
+widgetAlign128.DoSomething();</pre>
+<h2>ReadMisaligned16/32/64</h2>
+<p>ReadMisaligned reads  an unsigned integer from a possibly non-aligned address. The MIPS processor on the PS2, for example, cannot read a 32-bit value from an unaligned address. This function can be used to make reading such misaligned data portable.. </p>
+<p>Example usage: </p>
+<pre class="code-example">void DoSomeReading(const char* pData)
+{
+    uint16_t x = ReadMisalignedUint16(pData);
+    pData += sizeof(uint16_t);
+ 
+    uint32_t y = ReadMisalignedUint32(pData);
+    pData += sizeof(uint32_t);
+ 
+    uint64_t z = ReadMisalignedUint64(pData);
+    pData += sizeof(uint64_t);
+    
+    ...
+}</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 151 - 0
doc/EABitTricks.html

@@ -0,0 +1,151 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    
+<title>EABitTricks</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EABitTricks</h1>
+
+<h2>Introduction</h2>
+<p>EABitTricks provides a number of integer bit-manipulation utilities that are 
+  very fast and simple. For example, it is well-known that (x * 2) can be also 
+  accomplished with (x << 1). And while this example may not be useful in practice, 
+  there are many more such tricks which have real use, particularly for speeding 
+  up code. Some of these tricks may seem odd or even useless, but it turns out 
+  that there are surprising uses for many of these.</p>
+<h2>Notes</h2>
+<ul>
+  <li>Twos-complement integer storage is assumed. Nearly all modern processors 
+    use twos-complement storage.</li>
+  <li>Some tricks assume that right shifts of signed values preserve the sign. 
+    While nearly all modern processors and C/C++ compilers support this, some 
+    primitive processors don't. By preserving sign, we mean that signed binary 
+    (10000000 >> 1) gives (11000000). </li>
+  <li>Only 'tricky' efficient solutions are provided. Obvious brute force loops 
+    to do useful things aren't included. We attempt to use branchless and loopless 
+    logic where possible.</li>
+  <li>We don't cover magic number tricks for simplifying multiplication and division 
+    by constants. For example (x * 17) can also be quickly accomplished with ((x 
+    << 4) + x). Optimized integer multiplication and division tricks such as this 
+    is something for a separate library.</li>
+  <li>We don't cover floating point tricks. That is something for a separate library. 
+  </li>
+  <li>Implementations here are written as standalone functions for readability. 
+    However, the user may find that it's better in some cases to copy the implementation 
+    to a macro or to simply copy the implementation directly inline into source 
+    code. EABitTricks is meant to be a reference for copy and paste as much as 
+    it is meant to be used as-is.</li>
+  <li>Many of these functions are templated instead of taking a given integer 
+    type such as uint32_t. The reason for this is that we want 64 bit support 
+    and that can be had automatically in most cases by the use of templates. The 
+    generated code will be exactly as fast as the case when templates are not 
+  used.</li>
+</ul>
+<h2>Example usage </h2>
+<p>All examples presume a <font face="Courier New, Courier, mono" size="-1">#include 
+  &quot;Foundation/EABitTricks.h&quot;</font> and assume the using of <font face="Courier New, Courier, mono" size="-1">namespace 
+  EA</font>.</p>
+<p>TurnOffLowestBit</p>
+<pre class="code-example">int16_t i16 = 0x0005;           <span class="code-example-comment">// i16 =&gt; 000000101</span>
+i16 = TurnOffLowestBit(i16);    <span class="code-example-comment">// i16 =&gt; 000000100</span></pre>
+IsolateSingle0And1Bits
+<pre class="code-example">uint32_t u32 = 0xabababab;               <span class="code-example-comment"> // u32 =&gt; 10101011101010111010101110101011</span>
+u32 = IsolateSingle0And1Bits(0xabababab); <span class="code-example-comment">// u32 =&gt; 11111100011111000111110001111100</span></pre>
+RoundUpToPowerOf2
+<pre class="code-example">uint32_t u32 = 66;                <span class="code-example-comment">// u32 =&gt; 66  (01000010)</span>
+u32 = RoundUpToPowerOf2(u32);     <span class="code-example-comment">// u32 =&gt; 128 (10000000)</span></pre>
+<p> UnsignedMultiplyWouldOverflow</p>
+<pre class="code-example">bool b = UnsignedMultiplyWouldOverflow(0xffffffff, 0xffffffff);  <span class="code-example-comment">// b =&gt; true</span> </pre>
+<h2>Interface</h2>
+<p>The following is a simple listing of EABitTricks functions as of January 2006. 
+  For an up to date listing of current functionality, see EABitTricks.h. Also, 
+  the EABitTricks.h file has documentation and example usage for each function.</p>
+<p>Bit manipulation</p>
+<pre class="code-example">T        TurnOffLowestBit(T x);
+T        IsolateLowestBit(T x);
+T        IsolateLowest0Bit(T x);
+T        GetTrailing0Bits(T x);
+T        GetTrailing1And0Bits(T x);
+T        PropogateLowestBitDownward(T x);
+T        TurnOffLowestContiguousBits(T x);
+T        TurnOnLowest0Bit(T x);
+uint32_t GetNextWithEqualBitCount(uint32_t x);
+uint32_t IsolateSingleBits(uint32_t x);
+uint32_t IsolateSingle0Bits(uint32_t x);
+uint32_t IsolateSingle0And1Bits(uint32_t x);
+int32_t  ShiftRightSigned(int32_t x, uint32_t n);
+uint32_t CountTrailing0Bits(uint32_t x);
+uint32_t CountLeading0Bits(uint32_t x);
+uint32_t CountBits(uint32_t x);
+uint32_t CountBits64(uint64_t x);
+uint32_t RotateLeft(uint32_t x, uint32_t n);
+uint32_t RotateRight(uint32_t x, uint32_t n);
+uint32_t ReverseBits(uint32_t x);
+uint32_t IsolateHighestBit(uint32_t x);
+uint32_t IsolateHighest0Bit(uint32_t x);
+uint32_t PropogateHighestBitDownward(uint32_t x);
+uint32_t GetHighestContiguous0Bits(uint32_t x);
+T        GetBitwiseEquivalence(T x, T y);
+bool     AreLessThan2BitsSet(int32_t x);
+T        GetHighestBit(T t);
+  </pre>
+<p>Alignment / Power of 2</p>
+<pre class="code-example">bool     IsPowerOf2(T x);
+uint32_t RoundUpToPowerOf2(uint32_t x);
+bool     IsMultipleOf(T x);
+bool     IsPowerOf2Minus1(T x);
+uint32_t GetHighestBitPowerOf2(uint32_t x);
+bool     CrossesPowerOf2(T x, T y, T n);
+bool     CrossesPowerOf2(T x, T y);
+T        GetNextGreaterEven(T x);<br>T        GetNextGreaterOdd(T x);
+T        RoundUpTo(T x);
+int32_t  RoundUpToEx(T x);
+T        RoundDownTo(T x);
+T        RoundDownToEx(T x);
+uint32_t Log2(uint32_t x);</pre>
+<p>Overflow</p>
+<pre class="code-example">bool SignedAdditionWouldOverflow(T x, T y);
+bool SignedSubtractionWouldOverflow(T x, T y);
+bool UnsignedAdditionWouldOverflow(T x, T y);
+bool UnsignedSubtractionWouldOverflow(T x, T y);
+bool UnsignedMultiplyWouldOverflow(uint32_t x, uint32_t y);
+bool SignedMultiplyWouldOverflow(int32_t x, int32_t y);
+bool UnsignedDivisionWouldOverflow(uint32_t x, uint32_t y);
+bool SignedDivisionWouldOverflow(int32_t x, int32_t y);
+int  GetAverage(int32_t x, int32_t y);
+int  GetAverage_Ceiling(int32_t x, int32_t y);</pre>
+<p>Miscellaneous</p>
+<pre class="code-example">int32_t GetParity(uint32_t32_t x);
+bool    GetIsBigEndian();
+int32_t ToggleBetween0And1(int32_t x);
+T       ToggleBetweenint32_tegers(T x, T a, T b);
+bool    IsBetween0AndValue(int32_t x, int32_t a);
+void    ExchangeValues(T& x, T& y);
+T       FloorMod(T n, T mod);
+int32_t GetSign(int32_t32_t x);
+int32_t GetSignEx(int32_t32_t x);
+int32_t SignExtend12(int32_t32_t x);
+int32_t SignExtend24(int32_t32_t x);
+bool    IsUnsigned(T x);
+#define EAIsUnsigned(x)
+bool    IsTwosComplement();
+bool    IsOnesComplement();
+bool    IsSignMagnitude();
+bool    IsOffsetBinary();
+#define EAArrayCount(array);</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 107 - 0
doc/EAByteCrackers.html

@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAByteCrackers</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAByteCrackers</h1>
+
+<h2>Introduction</h2>
+<p>EAByteCrackers is a small utility header file that provides macros for breaking down integers into component bytes and for building up integers from component 
+    bytes. These are particularly useful because working with signed values creates opportunities for mistakes, and the macros present in EAByteCrackers remove such 
+    possibilities for error. You can run into this need for dealing with signed values when working with Windows &quot;winproc&quot; message packing, for example.</p>
+<h2>Conventions</h2>
+<p>Components within an integer are numbered 0-N with 0 referring to the least significant component. Thus, the 32 bit value 0x12345678 consists of byte 0 = 0x78, 
+    byte 1 = 0x56, byte 2 = 0x34, and byte 3 = 0x12. This is done like so because the index of the byte is thus equal to its numerical shift within the integer. It 
+    also means that byte 2 of a uint32_t refers to the same value as byte 2 of a uint64_t. For example:</p>
+<pre class="code-example">uint16_t u16 = UINT16_5_FROM_UINT64(0x887766554433221100);  <font color="#0066FF">// u16 becomes 0x55</font></pre>
+<p>Similarly, we have this:</p>
+<pre class="code-example">uint32_t u32 = UINT32_FROM_UINT8(0x33, 0x22, 0x11, 0x00);   <font color="#0066FF">// u32 becomes 0x33221100</font></pre>
+<p>In the macros, the terms b, w, d, and q are used.</p>
+<ul>
+    <li>b means 8 bit byte</li>
+    <li>w means 16 bit word</li>
+    <li>d means 32 bit dword</li>
+    <li>q means 64 bit quadword</li>
+</ul>
+<h2>Example usage </h2>
+<p>Basic usage:</p>
+<pre class="code-example">// Integer breakdown
+uint8_t  u8  = UINT8_0_FROM_UINT16(0x1100);               <font color="#0066FF">// u8 becomes 0x00</font>
+uint8_t  u8  = UINT8_2_FROM_UINT64(0x7766554433221100);   <font color="#0066FF">// u8 becomes 0x22</font>
+uint16_t u16 = UINT16_3_FROM_UINT64(0x7766554433221100);  <font color="#0066FF">// u16 becomes 0x7766</font>
+
+// Integer build up
+uint16_t u16 = UINT16_FROM_UINT8(0x11, 0x00);             <font color="#0066FF">// u16 becomes 0x1100</font>
+uint32_t u32 = UINT32_FROM_UINT8(0x33, 0x22, 0x11, 0x00); <font color="#0066FF">// u32 becomes 0x33221100</font></pre>
+<p></p>
+<p>How to safely convert a 32 bit value to two signed 16 bit values:</p>
+<pre class="code-example">int16_t n0 = (int16_t)UINT16_0_FROM_UINT32(0x1000ffff);   <font color="#0066FF">// n0 becomes -1</font>
+int16_t n1 = (int16_t)UINT16_1_FROM_UINT32(0x1000ffff);   <font color="#0066FF">// n1 becomes 4096</font></pre>
+<p>How to build up a uint64_t from a uint32_t and two uint16_t values:</p>
+<pre class="code-example">uint64_t u64 = UINT64_FROM_UINT32(0x77665544, UINT32_FROM_UINT16(0x3322, 0x1100)); <font color="#0066FF">// u64 becomes 0x7766554433221100</font>
+</pre>
+<h2>Interface</h2>
+<pre class="code-example"><span class="code-example-comment">// uint8_t byte manipulators</span>
+#define UINT8_0_FROM_UINT16(w)
+#define UINT8_1_FROM_UINT16(w)
+
+#define UINT8_0_FROM_UINT32(d)
+#define UINT8_1_FROM_UINT32(d)
+#define UINT8_2_FROM_UINT32(d)
+#define UINT8_3_FROM_UINT32(d)
+
+#define UINT8_0_FROM_UINT64(q)
+#define UINT8_1_FROM_UINT64(q)
+#define UINT8_2_FROM_UINT64(q)
+#define UINT8_3_FROM_UINT64(q)
+#define UINT8_4_FROM_UINT64(q)
+#define UINT8_5_FROM_UINT64(q)
+#define UINT8_6_FROM_UINT64(q)
+#define UINT8_7_FROM_UINT64(q)
+
+<span class="code-example-comment">// uint16_t byte manipulators</span>
+#define UINT16_0_FROM_UINT32(d)
+#define UINT16_1_FROM_UINT32(d)
+
+#define UINT16_0_FROM_UINT64(q)
+#define UINT16_1_FROM_UINT64(q)
+#define UINT16_2_FROM_UINT64(q)
+#define UINT16_3_FROM_UINT64(q)
+
+#define UINT16_FROM_UINT8(b1, b0)
+
+<span class="code-example-comment">// uint32_t byte manipulators</span>
+#define UINT32_2_FROM_UINT64(q)
+#define UINT32_1_FROM_UINT64(q)
+
+#define UINT32_FROM_UINT8(b3, b2, b1, b0)
+#define UINT32_FROM_UINT16(w1, w0)
+
+<span class="code-example-comment">// uint64_t byte manipulators</span>
+#define UINT64_FROM_UINT8(b7, b6, b5, b4, b3, b2, b1, b0)
+#define UINT64_FROM_UINT16(w3, w2, w1, w0)
+#define UINT64_FROM_UINT32(d1, d0)
+</pre>
+<p></p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+
+</body></html>
+
+
+

+ 235 - 0
doc/EACType.html

@@ -0,0 +1,235 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAString</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAString</h1>
+
+<h2>Introduction</h2>
+<p>EACRT stands for EA C Runtime. EACRT implements an extensive suite of C string functionality in a portable way. Some of this functionality is the same as what 
+    is present in the C standard and in common (but non-portable) extensions to the C standard. EACRT presents a consistent portable interface to its functionality 
+    in both 8 bit and 16 bit string form.</p>
+<p>The C language provides various functions which work with non-portable data types, such as long. EACRT defines all significant data types in terms of portable 
+    types such as int32_t and int64_t. </p>
+<p>Additionally, the EACRT versions of functions are usually faster than the C runtime library versions, usually because the EACRT versions minimize cache misses 
+    and branching, whereas typical C runtime libraries optimize for the smallest memory footprint with little or no regard to other performance characteristics.</p>
+<p>C-style printf functionality is separate from EACRT and is found in <a href="EASprintf.html">EASprintf</a>. Memcpy functionality (which comes from the C string.h 
+    header file) is present in <a href="Memcpy.html">EAMemcpy</a>. However, EACRT has a few mem functions itself.</p>
+<p>In some cases, EACRT simply #defines its function to be the same as the equivalent C function. This is done when the equivalent C function is known to be present 
+    and known to conform to the C/C++ language standard correctly.</p>
+<h2>Extension functions</h2>
+<p> EACRT provides extended functionality that isn't found in conventional C libraries, but it useful nevertheless.</p>
+<table width="100%" border="1" cellpadding="3">
+    <tr> 
+        <td><b>Function</b></td>
+        <td><b>Description</b></td>
+        <td><b>Signature</b></td>
+    </tr>
+    <tr> 
+        <td>Strlcat</td>
+        <td>Safe variation of strncat</td>
+        <td><font size="-1">char_t* Strlcat(char_t* pDestination, const char_t* pSource, size_t n); </font></td>
+    </tr>
+    <tr> 
+        <td>Strlcpy</td>
+        <td>Safe variation of strcpy</td>
+        <td><font size="-1">char_t* Strlcpy(char_t* pDestination, const char_t* pSource, size_t n); </font></td>
+    </tr>
+    <tr> 
+        <td>Stristr</td>
+        <td>Case insensitive version of strstr (find string within string)</td>
+        <td><font size="-1">char_t* Stristr(const char_t* pString, const char_t* pSubString);</font></td>
+    </tr>
+    <tr> 
+        <td>FtoaEnglish</td>
+        <td> 
+            <p>Float to ascii; always use English numeric format, regardless of the current locale.</p>
+        </td>
+        <td><font size="-1">char_t* FtoaEnglish(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled); </font></td>
+    </tr>
+    <tr> 
+        <td>AtofEnglish</td>
+        <td>Ascii to float; always use English numeric format, regardless of the current locale.</td>
+        <td><font size="-1">double AtofEnglish(const char_t* pString); </font></td>
+    </tr>
+    <tr> 
+        <td> StrtodEnglish</td>
+        <td>Same as strtod, but always use English numeric format, regardless of the current locale.</td>
+        <td><font size="-1">double StrtodEnglish(const char_t* pString, char_t** ppStringEnd);</font></td>
+    </tr>
+    <tr> 
+        <td>Memset16</td>
+        <td>Sets 16 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1">uint16_t* Memset16(void* pDestination, uint16_t c, size_t count); </font></td>
+    </tr>
+    <tr> 
+        <td>Memset32</td>
+        <td>Sets 32 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1"> uint32_t* Memset32(void* pDestination, uint32_t c, size_t count);</font></td>
+    </tr>
+    <tr> 
+        <td>Memset64</td>
+        <td>Sets 64 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1">uint64_t* Memset64(void* pDestination, uint64_t c, size_t count);</font></td>
+    </tr>
+    <tr> 
+        <td>MemsetN</td>
+        <td>Sets arbitrarily sized values in memory.</td>
+        <td><font size="-1">void* MemsetN (void* pDestination, const void* pSource, size_t sourceBytes, size_t count); </font></td>
+    </tr>
+    <tr> 
+        <td>EcvtBuf</td>
+        <td>Base function for converting a float to a %e string.</td>
+        <td> 
+            <p><font size="-1">char_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char_t* buffer); </font></p>
+        </td>
+    </tr>
+    <tr> 
+        <td>FcvtBuf</td>
+        <td>Base function for converting a float to a %f string.</td>
+        <td><font size="-1">char_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char_t* buffer); </font></td>
+    </tr>
+</table>
+<p>&nbsp;</p>
+<h2>Example usage </h2>
+<p>We provide a random smattering of example code here.</p>
+<pre class="code-example">char16_t buffer[32] = L"hello ";
+
+Strcat16(buffer, L"world");
+</pre>
+<p>Strlcat:</p>
+
+<pre class="code-example">char buffer[32];
+
+Strlcpy8(buffer, "Hello ", sizeof(buffer));
+Strlcat8(buffer, "world", sizeof(buffer));
+</pre>
+<p>U64toa:</p>
+
+<pre class="code-example">char buffer[32];
+
+U64toa8(UINT64_C(12345678901234567890), buffer, 16);
+</pre>
+<p>AtoI32:</p>
+
+<pre class="code-example">int32_t x = AtoI328(&quot;1234567890&quot;);
+</pre>
+<p>StrtodEnglish:</p>
+<pre class="code-example">const char16_t* pString = L"12345.678 123.456 1.23456";
+
+while(*pString)
+{
+    double value = StrtodEnglish16(pString, &pString);
+    printf("Field = %f\n, value);
+}
+</pre>
+<p>Memset16:</p>
+
+<pre class="code-example">uint16_t buffer[50];
+
+Memset16(buffer, 0x1234, 50);
+</pre>
+<h2>Interface</h2>
+<p>In each of the functions below, there is an char8_t and char16_t version. The actual function that EACRT implements is one with an 8 or 16 appended to the function names listed below. Thus below we have <span class="code-example-span">Strcat(char_t*, char_t*)</span>, and thus EACRT presents <span class="code-example-span">Strcat8(char8_t*, char8_t*)</span> and <span class="code-example-span">Strcat16(char16_t*, char16_t*)</span>.</p>
+<p>See EACRT.h for per-function documentation.</p>
+<pre class="code-example">char_t*       Strcat(char_t* pDestination, const char_t* pSource);
+char_t*       Strncat(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strlcat(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strcpy(char_t* pDestination, const char_t* pSource);
+char_t*       Strncpy(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strlcpy(char_t* pDestination, const char_t* pSource, size_t n);
+size_t        Strlen(const char_t* pString);
+size_t        Strxfrm(char_t* pDest, const char_t* pSource, size_t n);
+char_t*       Strdup(const char_t* pString);
+
+int           Isalnum(char_t c);
+int           Isalpha(char_t c);
+int           Isdigit(char_t c);
+int           Isxdigit(char_t c);
+int           Isgraph(char_t c);
+int           Islower(char_t c);
+int           Isprint(char_t c);
+int           Ispunct(char_t c);
+int           Isspace(char_t c);
+int           Iscntrl(char_t c);
+int           Isascii(char_t c);
+int           Toupper(char_t c);
+int           Tolower(char_t c);
+
+char_t*       Strlwr(char_t* pString);
+char_t*       Strupr(char_t* pString);
+char_t*       Strchr(const char_t* pString, char_t c);
+size_t        Strcspn(const char_t* pString1, const char_t* pString2);
+char_t*       Strpbrk(const char_t* pString1, const char_t* pString2);
+char_t*       Strrchr(const char_t* pString, char_t c);
+size_t        Strspn(const char_t* pString, const char_t* pSubString);
+char_t*       Strstr(const char_t* pString, const char_t* pSubString);
+char_t*       Stristr(const char_t* pString, const char_t* pSubString);
+char_t*       Strtok(char_t* pString, const char_t*  pTokens, char_t** pContext);
+char_t*       Strset(char_t* pString, char_t c);
+char_t*       Strnset(char_t* pString, char_t c, size_t n);
+char_t*       Strrev(char_t* pString);
+int           Strcmp(const char_t* pString1, const char_t* pString2);
+int           Strncmp(const char_t* pString1, const char_t* pString2, size_t n);
+int           Stricmp(const char_t*  pString1, const char_t* pString2);
+int           Strnicmp(const char_t* pString1, const char_t* pString2, size_t n);
+int           Strcoll(const char_t*  pString1, const char_t* pString2);
+int           Strncoll(const char_t* pString1, const char_t* pString2, size_t n);
+int           Stricoll(const char_t* pString1, const char_t* pString2);
+int           Strnicoll(const char_t* pString1, const char_t* pString1, size_t n);
+
+char_t*       EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char_t* buffer);
+char_t*       FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char_t* buffer);
+
+char_t*       I32toa(int32_t nValue, char_t* pResult, int nBase);
+char_t*       U32toa(uint32_t nValue, char_t* pResult, int nBase);
+char_t*       I64toa(int64_t nValue, char_t* pBuffer, int nBase);
+char_t*       U64toa(uint64_t nValue, char_t* pBuffer, int nBase);
+double        Strtod(const char_t* pString, char_t** ppStringEnd);
+double        StrtodEnglish(const char_t* pString, char_t** ppStringEnd);
+int64_t       StrtoI64(const char_t* pString, char_t** ppStringEnd, int nBase);
+uint64_t      StrtoU64(const char_t* pString, char_t** ppStringEnd, int nBase);
+int32_t       StrtoI32(const char_t* pString, char_t** ppStringEnd, int nBase);
+uint32_t      StrtoU32(const char_t* pString, char_t** ppStringEnd, int nBase);
+int32_t       AtoI32(const char_t* pString);
+uint32_t      AtoU32(const char_t* pString);
+int64_t       AtoI64(const char_t* pString);
+uint64_t      AtoU64(const char_t* pString);
+double        Atof(const char_t* pString);
+double        AtofEnglish(const char_t* pString);
+char_t*       Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+char_t*       FtoaEnglish(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+
+uint8_t*      Memset8 (void* pDestination, uint8_t  c, size_t count);
+uint16_t*     Memset16(void* pDestination, uint16_t c, size_t count);
+uint32_t*     Memset32(void* pDestination, uint32_t c, size_t count);
+uint64_t*     Memset64(void* pDestination, uint64_t c, size_t count);
+void*         MemsetN (void* pDestination, const void* pSource, size_t sourceBytes, size_t count);
+const char_t* Memchr(const char_t* pString, char_t c, size_t n);
+int           Memcmp(const char_t* pString1, const char_t* pString2, size_t n);
+char_t*       Memcpy(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Memmove(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Memset(char_t* pString, char_t c, size_t n);
+</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 353 - 0
doc/EADateTime.html

@@ -0,0 +1,353 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    
+<title>EADateTime</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EADateTime</h1>
+
+<h2>Introduction</h2>
+<p>EADateTime provides a unified implementation of date, time, and calendar functionality. 
+  The DateTime class represents date and time in a single class. Unlike other 
+  date/time systems, this class doesn't have a strictly limited resolution, due 
+  to its use of 64 bits (instead of 32 bits) of storage for the date/time value. 
+</p>
+<p> EADateTime supports the modern Gregorian calendar correctly, including such 
+  concepts as leap years. You can, for example, use EADateTime to implement proper 
+  calendar functionality for your application.</p>
+<p>The C standard library provides some time and date functionality, but it is 
+  slow, has some limitations, is not very easy to use, and isn't supported by 
+  all C/C++ compilers. Writing a calendar application with the C time and date 
+  functions would be possible, but would be tedious and error-prone.</p>
+<p>EADateTime does not support the formatting of date and time strings for display 
+  to a users. Such functionality is outside the scope of this class, primarily 
+  due to the issue of multiple string encodings (e.g. ASCII and Unicode) and due 
+  to the existence of multiple locales (e.g. English and French) which use different 
+  formats for time and date strings. A separate module -- EALocale -- exists which 
+  implements string formatting of dates and times. You can use that module and 
+  EADateTime together to implement locale-savvy times, dates, and calendars.</p>
+<h2>Example usage </h2>
+<p>All examples presume a <font face="Courier New, Courier, mono" size="-1">#include 
+  &quot;Foundation/EADateTime.h&quot;</font> and assume the using of <font face="Courier New, Courier, mono" size="-1">namespace 
+  EA</font>.</p>
+<p>Basic usage of class DateTime:</p>
+<pre class="code-example">DateTime dt;   // Sets dt to the current date and time.
+ 
+uint32_t year = dt.GetParameter(kParameterYear);      // Get the current year.
+ 
+dt.SetParameter(kParameterMonth, kMonthAugust);       // Set month to August.
+dt.AddTime(kParameterDayOfYear, 77);                  // Add 77 days.</pre> 
+<p>Comparison of DateTime instances:</p>
+<pre class="code-example">DateTime dtD(2008, kMonthDecember, 25);   // Sets dt to Christmas 2008.
+DateTime dtH(2008, kMonthOctober, 31);    // Sets dt to Halloween 2008.
+ 
+if(dtH &lt; dtD)
+    printf(&quot;Halloween occurs before Christmas.&quot;);
+else
+    printf(&quot;Christmas occurs before Halloween.&quot;);</pre>
+<blockquote></blockquote>
+<p>How to save and restore a DateTime to and from disk:</p>
+<pre><span class="code-example">DateTime dt;
+
+uint64_t n = dt.GetSeconds();         // Get the absolute time in seconds. This alone fully represents a DateTime.
+
+WriteToDisk64(ToBigEndian(n));        // Hypthetical disk writing function and endian conversion function.
+
+n = FromBigEndian(ReadFromDisk64());  // Hypothetical disk reading function and endian conversion function.
+
+dt = DateTime(n);                     // Restore the saved value to a DateTime instance.</span></pre>
+<p>Usage of standalone EADateTime utility functions, which are independent of 
+  class DateTime: </p>
+<pre class="code-example">bool b = IsLeapYear(2015);
+ 
+uint32_t n = GetDaysInYear(1974);
+ 
+uint32_t d = GetDaysInMonth(kMonthMarch, 2007);
+ 
+int64_t nSeconds = GetDaylightSavingsBias();</pre>
+<h2></h2>
+Conversion of C time to DateTime:
+<pre class="code-example">#include &lt;time.h&gt;
+
+tm time;
+asctime(&amp;ascTime);         // Get C time.
+
+DateTime dt;
+TmToDateTime(time, dt);    // Convert to DateTime.</pre>
+<h2>Interface</h2>
+<p>The following is defined in EADateTime.h. The latest versions of these definitions 
+  can be found in the current version of EADateTime.h</p>
+<p>Enumerations</p>
+<pre class="code-example">enum Month
+{
+    kMonthUnknown        =  0,
+    kMonthJanuary        =  1,
+    kMonthFebruary       =  2,
+    kMonthMarch          =  3,
+    kMonthApril          =  4,
+    kMonthMay            =  5,
+    kMonthJune           =  6,
+    kMonthJuly           =  7,
+    kMonthAugust         =  8,
+    kMonthSeptember      =  9,
+    kMonthOctober        = 10,
+    kMonthNovember       = 11,
+    kMonthDecember       = 12
+};
+ 
+enum DayOfMonth
+{
+    kDayOfMonthUnknown   =  0,
+    kDayOfMonthMin       =  1,
+    kDayOfMonthMax       = 31   <font color="#999999">/// The actual max month is dependent on which month is being referred to.</font>
+};
+
+enum DayOfWeek
+{
+    kDayOfWeekUnknown    =  0,
+    kDayOfWeekSunday     =  1,  
+    kDayOfWeekMonday     =  2,
+    kDayOfWeekTuesday    =  3,
+    kDayOfWeekWednesday  =  4,
+    kDayOfWeekThursday   =  5,
+    kDayOfWeekFriday     =  6,
+    kDayOfWeekSaturday   =  7
+};
+
+<span class="code-example-comment">/// TimeFrame
+</span>enum TimeFrame
+{
+    kTimeFrameUnknown = 0,  <font color="#999999">/// Unspecified time frame.</font>
+    kTimeFrameUTC     = 1,  <font color="#999999">/// Universal Coordinated Time. This is the time based on the time zone at Greenwich, England.</font>
+    kTimeFrameLocal   = 2   <font color="#999999">/// Same time as current machine.</font>
+};
+
+<span class="code-example-comment">/// TimeZone
+/// Standard time zone definitions, with their values corresponding to the nmuber of hours they are
+/// off relative to UTC (Universal Coordinated Time, which is the time at Greenwich England). 
+/// Note, for example, that kTimeZonePacific is -8 hours relative to Greenwich, England.
+</span>enum TimeZone
+{
+    kTimeZoneEniwetok    = -12,
+    kTimeZoneSamoa       = -11,
+    kTimeZoneHawaii      = -10,
+    kTimeZoneAlaska      =  -9,
+    kTimeZonePacific     =  -8,
+    kTimeZoneMountain    =  -7,
+    kTimeZoneCentral     =  -6,
+    kTimeZoneEastern     =  -5,
+    kTimeZoneAltantic    =  -4,
+    kTimeZoneBrazilia    =  -3,
+    kTimeZoneMidAtlantic =  -2,
+    kTimeZoneAzores      =  -1,
+    kTimeZoneGreenwich   =   0,
+    kTimeZoneRome        =  +1,
+    kTimeZoneIsrael      =  +2,
+    kTimeZoneMoscow      =  +3,
+    kTimeZoneBaku        =  +4,
+    kTimeZoneNewDelhi    =  +5,
+    kTimeZoneDhakar      =  +6,
+    kTimeZoneBangkok     =  +7,
+    kTimeZoneHongKong    =  +8,
+    kTimeZoneTokyo       =  +9,
+    kTimeZoneSydney      = +10,
+    kTimeZoneMagadan     = +11,
+    kTimeZoneWellington  = +12
+};
+
+<span class="code-example-comment">/// Epoch
+/// The use of an epoch is to provide a timeframe with which to work.
+/// Most of the time you don't need to know about this.
+</span>enum Epoch
+{
+    kEpochUnknown         =  0,
+    kEpochJulian          =  1, <font color="#999999">/// Began at noon, January 1, 4712 BC.</font>
+    kEpochModifiedJulian  =  2, <font color="#999999">/// Began at midnight, November 17, 1858. Exactly 2,400,000.5 days after Julian epoch began.</font>
+    kEpochGregorian       =  3, <font color="#999999">/// Began at midnight, September 14, 1752.</font>
+    kEpoch1900            =  4, <font color="#999999">/// Began at midnight, January 1, 1900.</font>
+    kEpoch1950            =  5, <font color="#999999">/// Began at midnight, January 1, 1950.</font>
+    kEpoch1970            =  6, <font color="#999999">/// Began at midnight, January 1, 1970. Same epoch used by C runtime library and Unix OS.</font>
+    kEpoch2000            =  7  <font color="#999999">/// Began at midnight, January 1, 2000.</font>
+};
+
+enum Era
+{
+    kEraUnknown = 0,
+    kEraBC      = 1,
+    kEraAD      = 2
+};
+
+<span class="code-example-comment">/// Parameter
+/// Defines a date or time parameter.
+</span>enum Parameter
+{
+    kParameterUnknown     =  0,
+    kParameterYear        =  1, <font color="#999999">/// Refers to full year value, such as 1994, 2006, etc. but not a two digit value such as 94 or 04. The valid range is 0-INT_MAX.</font>
+    kParameterMonth       =  2, <font color="#999999">/// Refers to month of year, starting with 1 for January. The valid range is 1-12.</font>
+    kParameterWeekOfYear  =  3, <font color="#999999">/// Refers to the week of the year, starting with 1 for the week of January 1. The valid range is 1-52.</font>
+    kParameterWeekOfMonth =  4, <font color="#999999">/// Refers to the week of the month, starting with 1 for the first week. The valid range is 1-5.</font>
+    kParameterDayOfYear   =  5, <font color="#999999">/// Refers to a day of the year, starting with 1 for January 1st. The valid range is 1-366.</font>
+    kParameterDayOfMonth  =  6, <font color="#999999">/// Refers to the day of the month, starting with 1 for the first of the month. The valid range is 1-31.</font>
+    kParameterDayOfWeek   =  7, <font color="#999999">/// Refers to the day of the week, starting with 1 for Sunday. The valid range is 1-7.</font>
+    kParameterHour        =  8, <font color="#999999">/// Refers to the hour of a day in 24 hour format, starting with 0 for midnight. The valid range is 0-23.</font>
+    kParameterMinute      =  9, <font color="#999999">/// Refers to the minute of the hour, starting with 0 for the first minute. The valid range is 0-59.</font>
+    kParameterSecond      = 10, <font color="#999999">/// Refers to the second of the minute, starting with 0 for the first second. The valid range is 0-59.</font>
+};
+
+<span class="code-example-comment">/// Conversions
+/// Defines useful conversion multipliers between seconds, minutes, hours, and days.
+/// Conversions are not defined for some entities (e.g. days per year) because the 
+/// value changes based on the particular year.
+</span>enum Conversions
+{
+    kSecondsPerMinute =    60,
+    kSecondsPerHour   =  3600,
+    kSecondsPerDay    = 86400,
+    kMinutesPerHour   =    60,
+    kMinutesPerDay    =  1440,
+    kHoursPerDay      =    24,
+    kDaysPerWeek      =     7,
+    kWeeksPerYear     =    52,
+    kMonthsPerYear    =    12
+};
+  </pre>
+<p>DateTime</p>
+<pre class="code-example">class DateTime
+{
+public:
+    static const uint32_t kValueDefault = 0xffffffff;
+    static const uint32_t kValueIgnored = 0xffffffff;
+
+public:
+    DateTime(TimeFrame timeFrame = kTimeFrameLocal);
+
+    DateTime(int64_t nSeconds) : mnSeconds(nSeconds);
+
+    DateTime(const DateTime& dateTime) : mnSeconds(dateTime.mnSeconds);
+
+<span class="code-example-comment">    /// DateTime
+    /// Constructs a DateTime class object from some standard parameters.
+    /// To construct a DateTime class with a different set of parameter types,
+    /// you'll need to use the Set function or in odd cases do manual calculations. 
+</span>    DateTime(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, 
+             uint32_t nHour = 0, uint32_t nMinute = 0, uint32_t nSecond = 0);
+
+<span class="code-example-comment">    /// operator=
+</span>    DateTime& operator=(const DateTime& dateTime);
+
+<span class="code-example-comment">    /// Compare
+    /// This function compares this object with another DateTime object and 
+    /// returns an integer result.The return value is the same as with the 
+    /// strcmp string comparison function. If this object is less than the 
+    /// argument dateTime, the return value is < 0. Comparison operators are 
+    /// defined outside this class, though they use the Compare function to do their work.
+</span>    int Compare(const DateTime& dateTime, bool bCompareDate = true, bool bCompareTime = true) const;
+
+<span class="code-example-comment">    /// GetParameter
+    /// Gets the given parameter. If you want to get the year, you would call Get(kParameterYear).
+</span>    uint32_t GetParameter(Parameter parameter) const;
+
+<span class="code-example-comment">    /// SetParameter
+    /// Sets the given parameter. If you want to set the year to 1999, you would 
+    /// call Set(kParameterYear, 1999).
+</span>    void SetParameter(Parameter parameter, uint32_t nValue);
+
+<span class="code-example-comment">    /// Set
+    /// Sets the time based on the current time. If the timeFrame is kTimeFrameUTC, 
+    /// the time is set to what the current UTC time is, which will be a fixed number 
+    /// of hours off of what the current local time is.
+</span>    void Set(TimeFrame timeFrame = kTimeFrameLocal);
+
+<span class="code-example-comment">    /// Set
+    /// Sets the time based on various inputs. If any input is kValueIgnored (uint32_t)-1, 
+    /// then the input is ignored and the current value is used. If any of the cyclic 
+    /// inputs is beyond its valid range, the modulo of the value is used and the division 
+    /// of the value is added to the next higher bracket. For example, if the input nMinute 
+    /// is 65, then the minute used is 5 and 1 is added to the current hour value.
+</span>    void Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour, 
+             uint32_t nMinute, uint32_t nSecond);
+
+<span class="code-example-comment">    /// AddTime
+    /// Allows you to increment (or decrement) the given parameter by the given amount.
+</span>    void AddTime(Parameter parameter, int32_t nValue);
+
+<span class="code-example-comment">    /// IsDST
+    /// Returns true if the time is daylight savings time. This function assumes that DST is valid
+    /// for the given current locale; some locales within the United States don't observe DST.
+</span>    bool IsDST() const;
+
+<span class="code-example-comment">    /// GetSeconds
+    /// Returns the DateTime internal representation.
+</span>    int64_t GetSeconds() const;
+};</pre>
+<blockquote></blockquote>
+<p>Utility</p>
+<pre class="code-example"><span class="code-example-comment">/// IsLeapYear
+/// Returns true if the given year is a leap year.
+</span>bool IsLeapYear(uint32_t nYear);
+
+<span class="code-example-comment">/// GetDaysInYear
+/// Returns the number of days in the given year. The value will vary based on whether 
+/// the year is a leap year or not.
+</span>uint16_t GetDaysInYear(uint32_t nYear);
+
+<span class="code-example-comment">/// GetDaysInMonth
+/// Returns the number of days in the given month. The value will vary based on the 
+/// month and based on whether the year is a leap year. The input nMonth takes one
+/// of enum Month or an integer equivalent.
+</span>uint8_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear);
+
+<span class="code-example-comment">/// GetDayOfYear
+/// The input nMonth takes one of enum Month or an integer equivalent.
+/// The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
+</span>uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear);
+
+<span class="code-example-comment">/// Convert4DigitTo2DigitYear
+/// Note that two-digit years bring a number of problems; they are best used for text
+/// display only and not for any internal processing.
+</span>inline int Convert4DigitTo2DigitYear(int nYear4);
+
+<span class="code-example-comment">/// Convert2DigitTo4DigitYear
+/// This code returns a year in the 1900s if the input year is > 30 but returns
+/// a year in the 2000s if the year is &lt;= 30. This is merely a guess and in fact there
+/// really is no good way to reliably convert a two digit year to a four digit year.
+</span>inline int Convert2DigitTo4DigitYear(int nYear2); 
+
+<span class="code-example-comment">/// GetCurrent
+/// Returns the current year, month, hour, etc.
+</span>uint32_t GetCurrent(Parameter parameter);
+
+<span class="code-example-comment">/// GetDaylightSavingsBias
+/// Returns the number of seconds that the current time is daylight savings adjusted from 
+/// the conventional time. Adding this value to the conventional time yields the time when
+/// adjusted for daylight savings.
+</span>int64_t GetDaylightSavingsBias();
+
+<span class="code-example-comment">/// GetTimeZoneBias
+/// Returns the number of seconds that the local time zone is off of UTC.
+/// Adding this value to the current UTC yields the current local time.
+</span>int64_t GetTimeZoneBias();
+
+<span class="code-example-comment">/// DateTimeToTm
+/// Converts a DateTime to a C tm struct.
+</span>void DateTimeToTm(const DateTime& dateTime, tm& time);
+
+<span class="code-example-comment">/// TmToDateTime
+/// Converts a C tm struct to a DateTime.
+</span>void TmToDateTime(const tm& time, DateTime& dateTime);</pre>
+<blockquote></blockquote>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 514 - 0
doc/EAFixedPoint.html

@@ -0,0 +1,514 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<title>EAFixedPoint</title>
+<meta name="author" content="Paul Pedriana">
+<link type="text/css" rel="stylesheet" href="UTFDoc.css">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAFixedPoint</h1>
+<h2> <span style="font-weight: bold;">Introduction</span> </h2>
+<p>The EAFixedPoint module implements fixed point math via classic C macros and 
+  functions and via a more advanced implementation using C++ classes. The C++ 
+  classes constitute a fairly complete implementation of a fixed point C++ numerical 
+  data type that acts much like the built-in float and double data types. </p>
+<p>The following code freely mixes the SFixed16 (signed 16:16 fixed point) data 
+  type with other numerical data types:</p>
+<pre><span style="font-family: monospace;"><span class="code-example">SFixed16_16 a(1), b(2), c(3);<br>float       f(4.5f);<br>double      d(3.2);<br>int         i(6);
+<br>a = b * f;<br>a = (c / d) + b + f;<br>a = c / d * (b % i) + f / c;<br>a = i * -c / (b++);<br>a = sin(a) + pow(b, d) * sqrt(a);<br>a = log(a) / log(f);</span></span></pre>
+<p></p>
+<p>Fixed point math has a number of uses:</p>
+<ul>
+  <li>Improved precision over floating point math.</li>
+  <li>Improved speed over floating point math, particularly with respect to division.</li>
+  <li>Consistent behaviour across CPUs, especially during network play when two 
+    different machines must behave identically but FPU behaviour may differ.</li>
+</ul>
+<p>Information about fixed point can be found on the Internet by simply searching 
+  for &quot;fixed point&quot; with your favorite search site.</p>
+<h2>Fixed point vs. floating point </h2>
+<p>Fixed point</p>
+<blockquote> 
+  <p>+ Can be very fast. Fixed point 
+    math executes at the same speed as integer math. Fixed to int conversions 
+    are much faster float to int.<br>
+    + Executes concurrently with floating point math, due its use of integer math.<br>
+    &#150; Limited range. Fixed point16:16 numbers are between -32767 and +32767. 
+    The fractional part is accurate to 1 / 65536. <br>
+    &#150; Harder to code in high-level languages.</p>
+</blockquote>
+<p>Floating point</p>
+<blockquote> 
+  <p>+ Large range. The range for floating point numbers is typically in excess 
+    of 1e-100 to 1e+100 and have an accuracy of about 13 decimal places. <br>
+    + Executes concurrently with integer math.<br>
+    &#150; Can be slower. Generally 
+    fast for addition and multiplication but may be slower for division and float 
+    to int conversions. </p>
+</blockquote>
+<h2>Fixed point precision </h2>
+<p>The C++ fixed point classes provide varying precision via the use of template 
+  parameter constants. EAFixedPoint provides the following predefined C++ fixed 
+  point types</p>
+<table width="100%" border="1">
+  <tr> 
+    <td><b>Type</b></td>
+    <td><b>Signed</b></td>
+    <td><b>Integral type</b></td>
+    <td><b>Precision</b></td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed24_8</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>24 bits of integer, 8 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed24_8</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>24 bits of integer, 8 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed22_10</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>22 bits of integer, 10 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed22_10</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>22 bits of integer, 10 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed20_12</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>20 bits of integer, 12 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed20_12</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>20 bits of integer, 12 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed18_14</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>18 bits of integer, 14 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed18_14</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>18 bits of integer, 14 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed16_16</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>16 bits of integer, 16 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed16_16</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>16 bits of integer, 16 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed14_18</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>14 bits of integer, 18 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed14_18</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>14 bits of integer, 18 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed12_20</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>12 bits of integer, 20 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed12_20</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>12 bits of integer, 20 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed10_22</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>10 bits of integer, 22 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed10_22</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>10 bits of integer, 22 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed8_24</font></td>
+    <td>signed</td>
+    <td>32</td>
+    <td>8 bits of integer, 24 bits of fraction</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed8_24</font></td>
+    <td>unsigned</td>
+    <td>32</td>
+    <td>8 bits of integer, 24 bits of fraction</td>
+  </tr>
+  <tr>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed48_16</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>48 bits of integer, 16 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed48_16</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>48 bits of integer, 16 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed44_20</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>44 bits of integer, 20 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed44_20</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>44 bits of integer, 20 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed40_24</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>40 bits of integer, 24 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed40_24</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>40 bits of integer, 24 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed36_28</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>36 bits of integer, 28 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed36_28</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>36 bits of integer, 28 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed32_32</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>32 bits of integer, 32 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed32_32</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>32 bits of integer, 32 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed28_36</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>28 bits of integer, 36 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed28_36</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>28 bits of integer, 36 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed24_40</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>24 bits of integer, 40 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed24_40</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>24 bits of integer, 40 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed20_44</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>20 bits of integer, 44 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed20_44</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>20 bits of integer, 44 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">SFixed16_48</font></td>
+    <td>signed</td>
+    <td>64</td>
+    <td>16 bits of integer, 48 bits of fraction.</td>
+  </tr>
+  <tr> 
+    <td><font face="Courier New, Courier, mono" size="-1">UFixed16_48</font></td>
+    <td>unsigned</td>
+    <td>64</td>
+    <td>16 bits of integer, 48 bits of fraction.</td>
+  </tr>
+</table>
+<h2>Example usage </h2>
+<p>To a large degree, you can use fixed point types the same way you would use 
+  floating point types. </p>
+<p>Mixed integer math expressions (same as shown earlier above):</p>
+<pre class="code-example">SFixed16_16 a(1), b(2), c(3);<br>float       f(4.5f);<br>double      d(3.2);<br>int         i(6);
+<br>a = b * f;<br>a = (c / d) + b + f;<br>a = c / d * (b % i) + f / c;<br>a = i * -c / (b++);<br>a = sin(a) + pow(b, d) * sqrt(a);<br>a = log(a) / log(f);</pre>
+<p>printf:</p>
+<pre class="code-example">SFixed24_8 f = 23.5f;<br><br>printf(&quot;%f&quot;, f.AsFloat());</pre>
+<p>Logical expresions:</p>
+<pre class="code-example">SFixed16_16 a = 20.4;
+SFixed16_16 b = 130.6;
+SFixed16_16 c = 223.3;
+
+if((a &lt; b) || (b &gt;= c) || (a &lt; 23.5))
+    a *= 25;</pre>
+<h2>Limitations </h2>
+<p>The primary differences between our fixed point type and a hypothetical built-in 
+  version are:</p>
+<ul>
+  <li>EAFixedPoint doesn't implement cast operators. Instead, provides explicit 
+    converters such as AsInt, AsFloat, etc. This is by design, as such casting 
+    operators result in ambiguous conversions and would need built-in compiler 
+    knowledge to resolve the situation.</li>
+  <li>Standard library functions such as sprintf have no support for such fixed 
+    point types. However, most of the time it is sufficient to convert to floating 
+    point and use the built-in floating point formatting functions.</li>
+  <li>There is no explicit support for mixing expressions between two different 
+    fixed point types, such as SFixed16_16 with SFixed24_8. You can use these 
+    together via conversion between built-in types. </li>
+</ul>
+<h2>Interface </h2>
+<p>C interface:</p>
+<pre class="code-example">typedef int32_t EAFixed16;
+ 
+#define    EAMAX_FIXED16        0x7fffffff
+#define    EAMIN_FIXED16        0x80000000
+
+#define    EAFixed16ToInt(a)    ((int32_t)(a) >> 16)
+#define    EAIntToFixed16(a)    ((EAFixed16)((a) << 16))
+#define    EAFixed16ToDouble(a) (((double)a) / 65536.0)
+#define    EADoubleToFixed16(a) ((EAFixed16)((a) * 65536.0))
+#define    EAFixed16ToFloat(a)  (((float)a) / 65536.f)
+#define    EAFloatToFixed16(a)  ((EAFixed16)((a) * 65536.f))
+#define    EAFixed16Negate(a)   (-a)
+
+EAFixed16  EAFixed16Mul         (EAFixed16 a, EAFixed16 b);
+EAFixed16  EAFixed16Div         (EAFixed16 a, EAFixed16 b);
+EAFixed16  EAFixed16DivSafe     (EAFixed16 a, EAFixed16 b);
+EAFixed16  EAFixed16MulDiv      (EAFixed16 a, EAFixed16 b, EAFixed16 c);
+EAFixed16  EAFixed16MulDivSafe  (EAFixed16 a, EAFixed16 b, EAFixed16 c);
+EAFixed16  EAFixed16Mod         (EAFixed16 a, EAFixed16 b);
+EAFixed16  EAFixed16ModSafe     (EAFixed16 a, EAFixed16 b);
+EAFixed16  EAFixed16Abs         (EAFixed16 a);
+</pre>
+<p>C++ interface, by example of SFixed16_16. Note that nearly all the functions 
+  below are implemented as simple inlines:</p>
+<pre class="code-example">struct SFixed16_16
+{
+    SFixed16_16();
+    SFixed16_16(const SFixed16_16&amp; value);
+    SFixed16_16(const int&amp; value);
+    SFixed16_16(const unsigned int&amp; value);
+    SFixed16_16(const long&amp; value);
+    SFixed16_16(const unsigned long&amp; value);
+    SFixed16_16(const float&amp; value);
+    SFixed16_16(const double&amp; value);
+
+    void    FromFixed(const int&amp; value);
+    int32_t AsFixed();
+
+    int           AsInt() const;
+    unsigned int  AsUnsignedInt() const;
+    long          AsLong() const;
+    unsigned long AsUnsignedLong()const;
+    float         AsFloat() const;
+    double        AsDouble() const;
+
+    SFixed16_16&amp; operator=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator=(const int&amp; value);
+    SFixed16_16&amp; operator=(const unsigned int&amp; value);
+    SFixed16_16&amp; operator=(const long&amp; value);
+    SFixed16_16&amp; operator=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator=(const float&amp; value);
+    SFixed16_16&amp; operator=(const double&amp; value);
+
+    bool operator&lt; (const SFixed16_16&amp; value) const;
+    bool operator&gt; (const SFixed16_16&amp; value) const;
+    bool operator&gt;=(const SFixed16_16&amp; value) const;
+    bool operator&lt;=(const SFixed16_16&amp; value) const;
+    bool operator==(const SFixed16_16&amp; value) const;
+    bool operator!=(const SFixed16_16&amp; value) const;
+     
+    bool operator&lt; (const int&amp; value) const;
+    bool operator&gt; (const int&amp; value) const;
+    bool operator&gt;=(const int&amp; value) const;
+    bool operator&lt;=(const int&amp; value) const;
+    bool operator==(const int&amp; value) const;
+    bool operator!=(const int&amp; value) const;
+     
+    bool operator&lt; (const unsigned int&amp; value) const;
+    bool operator&gt; (const unsigned int&amp; value) const; 
+    bool operator&gt;=(const unsigned int&amp; value) const;
+    bool operator&lt;=(const unsigned int&amp; value) const;
+    bool operator==(const unsigned int&amp; value) const;
+    bool operator!=(const unsigned int&amp; value) const;
+     
+    bool operator&lt; (const long&amp; value) const;
+    bool operator&gt; (const long&amp; value) const;
+    bool operator&gt;=(const long&amp; value) const;
+    bool operator&lt;=(const long&amp; value) const;
+    bool operator==(const long&amp; value) const;
+    bool operator!=(const long&amp; value) const;
+     
+    bool operator&lt; (const unsigned long&amp; value) const;
+    bool operator&gt; (const unsigned long&amp; value) const;
+    bool operator&gt;=(const unsigned long&amp; value) const;
+    bool operator&lt;=(const unsigned long&amp; value) const;
+    bool operator==(const unsigned long&amp; value) const;
+    bool operator!=(const unsigned long&amp; value) const;
+
+    bool operator&lt; (const float&amp; value) const;
+    bool operator&gt; (const float&amp; value) const;
+    bool operator&gt;=(const float&amp; value) const;
+    bool operator&lt;=(const float&amp; value) const;
+    bool operator==(const float&amp; value) const;
+    bool operator!=(const float&amp; value) const;
+     
+    bool operator&lt; (const double&amp; value) const;
+    bool operator&gt; (const double&amp; value) const;
+    bool operator&gt;=(const double&amp; value) const;
+    bool operator&lt;=(const double&amp; value) const;
+    bool operator==(const double&amp; value) const;
+    bool operator!=(const double&amp; value) const;
+    bool operator! () const;
+     
+    SFixed16_16 operator~() const;
+    SFixed16_16 operator-() const;
+    SFixed16_16 operator+() const;
+
+    SFixed16_16&amp; operator+=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator+=(const int&amp; value);
+    SFixed16_16&amp; operator+=(const unsigned int&amp; value);
+    SFixed16_16&amp; operator+=(const long &amp; value);
+    SFixed16_16&amp; operator+=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator+=(const float&amp; value);
+    SFixed16_16&amp; operator+=(const double&amp; value);
+
+    SFixed16_16&amp; operator-=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator-=(const int&amp; value);
+    SFixed16_16&amp; operator-=(const unsigned int&amp; value);
+    SFixed16_16&amp; operator-=(const long&amp; value);
+    SFixed16_16&amp; operator-=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator-=(const float&amp; value);
+    SFixed16_16&amp; operator-=(const double&amp; value);
+
+    SFixed16_16&amp; operator*=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator*=(const int&amp; value)
+    SFixed16_16&amp; operator*=(const unsigned int&amp; value)
+    SFixed16_16&amp; operator*=(const long&amp; value)
+    SFixed16_16&amp; operator*=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator*=(const float&amp; value);
+    SFixed16_16&amp; operator*=(const double&amp; value);
+
+    SFixed16_16&amp; operator/=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator/=(const int&amp; value);
+    SFixed16_16&amp; operator/=(const unsigned int&amp; value);
+    SFixed16_16&amp; operator/=(const long&amp; value);
+    SFixed16_16&amp; operator/=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator/=(const float&amp; value);
+    SFixed16_16&amp; operator/=(const double&amp; value);
+
+    SFixed16_16&amp; operator%=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator%=(const int&amp; value);
+    SFixed16_16&amp; operator%=(const unsigned int&amp; value);
+    SFixed16_16&amp; operator%=(const long&amp; value);
+    SFixed16_16&amp; operator%=(const unsigned long&amp; value);
+    SFixed16_16&amp; operator%=(const float&amp; value);
+    SFixed16_16&amp; operator%=(const double&amp; value);
+
+    SFixed16_16&amp; operator|=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator|=(const int&amp; value);
+
+    SFixed16_16&amp; operator&amp;=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator&amp;=(const int&amp; value);
+
+    SFixed16_16&amp; operator^=(const SFixed16_16&amp; value);
+    SFixed16_16&amp; operator^=(const int&amp; value);
+
+    SFixed16_16 operator&lt;&lt;(int numBits) const;
+    SFixed16_16 operator&gt;&gt;(int numBits) const;
+
+    SFixed16_16&amp; operator&lt;&lt;=(int numBits);
+    SFixed16_16&amp; operator&gt;&gt;=(int numBits);
+
+    SFixed16_16&amp; operator++();
+    SFixed16_16&amp; operator--();
+    SFixed16_16  operator++(int);
+    SFixed16_16  operator--(int);
+
+    SFixed16_16  Abs();
+    SFixed16_16  DivSafe(const SFixed16_16&amp; denominator);
+    SFixed16_16&amp; DivSafeAssign(const SFixed16_16&amp; denominator);
+}<font color="#000099">;</font>
+  </pre>
+<hr>
+<p><br>
+  <br>
+  <br>
+  <span style="font-family: monospace;">&nbsp;&nbsp; </span><br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+</p>
+</body></html>

+ 157 - 0
doc/EAGlobal.html

@@ -0,0 +1,157 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">  
+    <title>EAGlobal</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAGlobal</h1>
+
+<h2>Introduction</h2>
+<p>EAGlobal provides the GlobalPtr class. GlobalPtr acts as a reference to a pointer which is global throughout the process (includes the application and any loaded DLLs). The object pointed to must define a unique 32-bit kGlobalID if one is not given. The GlobalPtr class works in a way similar to a smart pointer, but note that it is not the same as your typical auto_ptr or anything else provided by C++ library vendors. The pointer is set to NULL on creation.<br>
+<br>
+ Global pointers may be used from multiple threads once initialized to point to an object, but are _not_ thread-safe when being set. If you have a situation where two threads may attempt to set a global pointer at the same time, you should use OS globals (See EAOSGlobal.h) instead to serialize the creators on the OS global lock and prevent race conditions.<br>
+<br>
+ A GlobalPtr is not the same thing as simply declaring a pointer at 
+ a globally accessible scope, especially on platforms with dynamic libraries such as Windows with its DLLs. A GlobalPtr allows multiple pieces of code to declare independent pointers to an object, even if the pieced of code are in independent DLLs.<br>
+<br>
+ The pointer assigned to a GlobalPointer need not be a pointer allocated
+dynamically on the heap. It can just as well be the address of some static or local variable.</p>
+<h2>Example usage </h2>
+<p>Here we provide some basic example usage.</p>
+<pre class="code-example">GlobalPtr&lt;int, 0x11111111&gt; pInteger;
+GlobalPtr&lt;int, 0x11111111&gt; pInteger2;
+
+assert(pInteger == NULL);
+
+pInteger = new int[2];
+pInteger[0] = 10;
+pInteger[1] = 20;
+assert(pInteger2 == pInteger);
+assert(pInteger2[0] == pInteger[0]);
+
+delete[] pInteger;
+pInteger = NULL;
+assert(pInteger2 == NULL);
+</pre>
+<h2>Interface</h2>
+<p>The GlobalPtr class works like a smart pointer, but note that it is not the same as your typical auto_ptr or anything else provided by C++ library vendors. </p>
+<pre class="code-example">template&lt;class t, uint32_t kGlobalId = T::kGlobalId&gt;
+class GlobalPtr
+{
+public:
+<span class="code-example-comment">    /// this_type
+    /// This is an alias for this class.
+</span>    typedef GlobalPtr&lt;T, kGlobalId&gt; this_type;
+
+<span class="code-example-comment">    /// GlobalPtr
+    ///
+    /// Default constructor. Sets member pointer to whatever the 
+    /// shared version is. If this is the first usage of the shared
+    /// version, the pointer will be set to NULL.
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass;
+    ///
+</span>    GlobalPtr();
+
+<span class="code-example-comment">    /// GlobalPtr (copy constructor)
+    ///
+    /// Default constructor. Sets member pointer to whatever the 
+    /// shared version is. If this is the first usage of the shared
+    /// version, the pointer will be set to NULL.
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass1;
+    ///    pSomeClass1 = new pSomeClass;
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass2(pSomeClass1);
+    ///    pSomeClass2-&gt;DoSomething();
+    ///
+</span>    explicit GlobalPtr(const this_type& globalPtr);
+
+<span class="code-example-comment">    /// operator =
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass1;
+    ///    pSomeClass1 = new pSomeClass;
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass2(pSomeClass1);
+    ///    pSomeClass2->DoSomething();
+    ///
+</span>    this_type& operator=(const this_type& /*globalPtr*/);
+
+<span class="code-example-comment">    /// operator =
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass1;
+    ///    pSomeClass1 = new pSomeClass;
+    ///    delete pSomeClass1;
+    ///    pSomeClass1 = new pSomeClass;
+    ///
+</span>    this_type& operator=(T* p);
+
+<span class="code-example-comment">    /// operator T*
+    ///
+    /// Example usage:
+    ///    GlobalPtrlt;SomeClass, 0x12345678&gt; pSomeClass;
+    ///    FunctionWhichUsesSomeClassPtr(pSomeClass);
+    ///
+</span>    operator T*() const;
+
+<span class="code-example-comment">    /// operator T*
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass;
+    ///    CallFunctionWhichUsesSomeClassPtr(pSomeClass);
+    ///
+</span>    T& operator*() const;
+
+<span class="code-example-comment">    /// operator -&gt;
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass;
+    ///    pSomeClass-&gt;DoSomething();
+    ///
+</span>    T* operator-&gt;() const;
+
+<span class="code-example-comment">    /// operator !
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass;
+    ///    if(!pSomeClass)
+    ///        pSomeClass = new SomeClass;
+    ///
+</span>    bool operator!() const;
+
+<span class="code-example-comment">    /// get
+    ///
+    /// Returns the owned pointer.
+    ///
+    /// Example usage:
+    ///    GlobalPtr&lt;SomeClass, 0x12345678&gt; pSomeClass = new SomeClass;
+    ///    SomeClass* pSC = pSomeClass.get();
+    ///    pSC->DoSomething();
+    ///
+</span>    T* get() const;
+};</pre>
+<p></p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 65 - 0
doc/EAHash.html

@@ -0,0 +1,65 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    <title>EAHash</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAHash</h1>
+
+<h2>Introduction</h2>
+<p>EAHash is a family of hashing functions. There are string hashes, binary hashes, cryptographic hashes, checksums, and CRCs; each has its applications. If you are looking for hash tables, they are present in the EASTL library, which provides a suite of hash table containers.</p>
+<p>Hash functions are functions which take input data and produce a hash value, which is usually an integer or some short fixed-size data. This is useful for implementing hash-tables, cryptographic security, and error checking. A good hash function produces a unique hash value for the large majority of unique input. A cryptographic hash goes further and has a level of mathematical security built into it so that it is difficult or impossible to guess the original input from a given hash value output. You can read more about hash functions at Wikipedia at <a href="http://en.wikipedia.org/wiki/Hash_function">http://en.wikipedia.org/wiki/Hash_function</a>.</p>
+<p>EAStdC  provides the following hash submodules:</p>
+<ul><li>EAHashString (string hashing) </li>
+  <li>EAHashCRC (cyclical redundancy check)</li>
+</ul>
+<p>EAHash provides these hashes in forms that read from blocks in memory. </p>
+<h2>EAHashString</h2>
+<p>String hashes are probably the most common hashes used in game development. There is a particular interest in having string hashes that are fast but reasonably collision-free in practice. Cryptographical security is not of interest to string hashes. However, being able to do a case-insensitive hash often is. </p>
+<p>String hashes are different from binary hashes in that they hash characters and not bytes. Thus, the hash for the 8 bit string &quot;hello&quot; will be the same as the hash for a 16 bit char string L&quot;hello&quot; even though the latter uses twice as many bytes and would hash to a different value via a binary hash function. String hashes are also different in that they sometimes have the option to act with case insensitivity. In such cases the hash for &quot;Hello&quot; is the same as for &quot;hello&quot;. </p>
+<p>The primary string hash provided by EAHashString is the FNV1 hash, though the DJB2 hash is also provided. The FNV1 hash appears to be the best string hash for most users' needs, as it is very fast and yet generates surprisingly few collisions with typical text input. </p>
+<p>The FNV1 hash functions provided by EAHashString are:</p>
+<pre class="code-example">uint32_t FNV1         (const void*     pData, size_t nLength, uint32_t nInitialValue = kFNV1InitialValue);<br>uint32_t FNV1_String8 (const char8_t*  pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);<br>uint32_t FNV1_String16(const char16_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);</pre>
+<p>Example usage: </p>
+<pre class="code-example">const char16_t fileName[IO::kMaxFileNameLength];
+ 
+const uint32_t fileNameHash = FNV1_string16(filePath, kFNV1InitialValue, kCharCaseLower);
+ 
+printf(&quot;Case-insensitive hash for file %ls is 0x%08x.\n&quot;, filePath, fileNameHash);</pre>
+<h2>EAHashCRC</h2>
+<p>A CRCs (cyclical redundancy check) is a form of hash which is good at detecting uniqueness of input yet is fairly simple to implement. CRCs are most often used to detect errors during data transmission in a non-security-sensitive environment. If a recieved file has a different checksum than the original file, there was a transmission error. A cryptographic hash could also be used to do what a CRC hash does, but it would have more overhead, particularly when implemented in hardware. </p>
+<p>There are many standards and variations of CRC algorithms, and trying to understand them can be a tedious frustrating experience. EAHashCRC provides a single CRC implementation for each of various bit sizes and doesn't try to cater to the myriad of variations that exist. Unless you need a CRC function which interacts with some specific third party CRC convention, the version in EAHashCRC is probably fine. You can read more about CRCs on Wikipedia at <a href="http://en.wikipedia.org/wiki/Cyclic_redundancy_check">http://en.wikipedia.org/wiki/Cyclic_redundancy_check</a>.</p>
+<p>The CRC functions have a 'bFinalize' parameter, which enables the inversion of the bits upon completion. This is a common practice in standardized CRC calculations. When doing iterative CRC calculations, you need to make sure you set bFinalize to false for all but the last call. See the example code below.</p>
+<p>EAHashCRC provides the following block functions: </p>
+<pre class="code-example">uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue = kCRC16InitialValue, bool bFinalize = true);
+uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC24InitialValue, bool bFinalize = true);
+uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
+uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue = kCRC64InitialValue, bool bFinalize = true);
+
+uint32_t CRC32Reverse(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);</pre>
+<p>Example basic usage:</p>
+<pre class="code-example">char*  fileData;
+size_t fileLength;
+ 
+ReadFile(pData, fileLength);
+ 
+uint16_t crc = CRC16(fileData, fileLength);</pre>
+<p>Example usage of incremental CRC calculation, which is useful when you don't have the data in a single block:</p>
+<pre class="code-example">char     pDataArray[1000];<br>uint64_t crc = kCRC64InitialValue;<br><br>for(int i = 0; i &lt; 10; i++)<span class="code-example-comment"> // Calculate the CRC for the data array incrementally.</span><br>    crc = EA::Hash::CRC64(pDataArray[i * 100], 100, crc, i == 9);<br>
+</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+</body></html>
+
+
+

+ 232 - 0
doc/EAMathHelp.html

@@ -0,0 +1,232 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAMathHelp</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAMathHelp</h1>
+
+<h2>Introduction</h2>
+<p>EAMathHelp provides fast floating point math primitives. It is not a vector/matrix math library such as those in use around Electronic Arts, but rather is a base 
+    for doing floating point characterizations and for doing fast floating point conversions. The former often serve to help implement the latter.</p>
+<p>The constants listed below may look odd if you aren't familiar with the standardized IEEE floating point format. A description of this format is outside the scope 
+    of this document, but you can find plenty of documentation about it by looking it up on the Internet. Suffice it to say the floating point numbers are essentially 
+    bitfields that specify a sign, an integer value (mantissa), and an exponent to raise the integer value by.</p>
+<h2>Constants</h2>
+<pre class="code-example"><span class="code-example-comment">// 32 bit float bits
+</span>const uint32_t  kFloat32SignMask                = UINT32_C(0x80000000);
+const uint32_t  kFloat32ExponentMask            = UINT32_C(0x7F800000);
+const uint32_t  kFloat32MantissaMask            = UINT32_C(0x007FFFFF);
+const uint32_t  kFloat32SignAndExponentMask     = UINT32_C(0xFF800000);
+const uint32_t  kFloat32SignAndMantissaMask     = UINT32_C(0x807FFFFF);
+const uint32_t  kFloat32ExponentAndMantissaMask = UINT32_C(0x7FFFFFFF);
+const uint32_t  kFloat32PositiveInfinityBits    = UINT32_C(0x7F800000);
+const unsigned  kFloat32SignBits                = 1;
+const unsigned  kFloat32ExponentBits            = 8;
+const unsigned  kFloat32MantissaBits            = 23;
+const unsigned  kFloat32BiasValue               = 127;
+ 
+<span class="code-example-comment">// 64 bit float bits
+</span>const uint64_t  kFloat64SignMask                = UINT64_C(0x8000000000000000);
+const uint64_t  kFloat64ExponentMask            = UINT64_C(0x7FF0000000000000);
+const uint64_t  kFloat64MantissaMask            = UINT64_C(0x000FFFFFFFFFFFFF);
+const uint64_t  kFloat64SignAndExponentMask     = UINT64_C(0xFFF0000000000000);
+const uint64_t  kFloat64SignAndMantissaMask     = UINT64_C(0x800FFFFFFFFFFFFF);
+const uint64_t  kFloat64ExponentAndMantissaMask = UINT64_C(0x7FFFFFFFFFFFFFFF);
+const uint64_t  kFloat64PositiveInfinityBits    = UINT64_C(0x7FF0000000000000);
+const unsigned  kFloat64SignBits                = 1;
+const unsigned  kFloat64ExponentBits            = 11;
+const unsigned  kFloat64MantissaBits            = 52;
+const unsigned  kFloat64BiasValue               = 1023;
+
+const float32_t kFloat32Infinity                = kFloat32PositiveInfinityBits;
+const float64_t kFloat64Infinity                = kFloat64PositiveInfinityBits;
+ 
+<span class="code-example-comment">// bias to integer
+</span>const float32_t kFToIBiasF32 = uint32_t(3) << 22;
+const int32_t   kFToIBiasS32 = 0x4B400000;           <span class="code-example-comment">// Same as ((int32_t&) kFToIBiasF32), but known to optimizer at compile time.</span>
+const float64_t kFToIBiasF64 = uint64_t(3) << 52;
+ 
+<span class="code-example-comment">// bias to 8-bit fraction
+</span>const float32_t kFToI8BiasF32 = uint32_t(3) << 14;
+const int32_t   kFToI8BiasS32 = 0x47400000;          <span class="code-example-comment">// Same as ((int32_t&) kFToI8BiasF32), but known to optimizer at compile time.</span>
+ 
+<span class="code-example-comment">// bias to 16-bit fraction
+</span>const float32_t kFToI16BiasF32 = uint32_t(3) << 6;
+const int32_t   kFToI16BiasS32 = 0x43400000;         <span class="code-example-comment">// Same as ((int32_t&) kFToI16BiasF32), but known to optimizer at compile time.</span>
+</pre>
+<h2>Functions</h2>
+<p>Float conversion functions</p>
+<pre class="code-example"><span class="code-example-comment">///////////////////////////////////////////////////////////////////////
+// Full range conversion functions
+//
+// These are good for floats within the full range of a float. Remember
+// that a single-precision float only has a 24-bit significand so
+// most integers |x| > 2^24 cannot be represented exactly.
+//
+// The result of converting an out-of-range number, infinity, or NaN
+// is undefined.
+//
+</span>inline uint32_t RoundToUint32(float fValue);
+inline int32_t  RoundToInt32(float fValue);
+inline int32_t  FloorToInt32(float fValue);
+inline int32_t  CeilToInt32(float fValue);
+inline int32_t  TruncateToInt32(float fValue);
+
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////
+// Partial range conversion functions.
+//
+// These are only good for |x| &lt;= 2^23. The result of converting an
+// out-of-range number, infinity, or NaN is undefined.
+//
+</span>inline int32_t FastRoundToInt23(float fValue);
+
+
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////
+// Unit-to-byte functions.
+//
+// Converts real values in the range |x| &lt;= 1 to unsigned 8-bit values
+// [0, 255]. The result of calling UnitFloatToUint8() with |x|>1 is
+// undefined.
+//
+</span>inline uint8_t UnitFloatToUint8(float fValue);
+inline uint8_t ClampUnitFloatToUint8(float fValue);
+
+</pre>
+<p>Float characterization functions</p>
+<pre class="code-example"><span class="code-example-comment">///////////////////////////////////////////////////////////////////////
+// IsInvalid
+//
+// Returns true if a value does not obey normal arithmetic rules;
+// specifically, x != x. In the case of Visual C++ 2003, this is true
+// for NaNs and indefinites, and not for normalized finite values,
+// denormals, and infinities. Other compilers may return different
+// results or even false for all values.
+//
+// IsInvalid() is useful as a fast assert check that floats are
+// sane and won't poison computations as NaNs can with masked
+// exceptions.
+//
+</span>inline bool IsInvalid(float32_t fValue);
+inline bool IsInvalid(float64_t fValue);
+ 
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////////////
+// IsNormal
+//
+// Returns true if the value is a normalized finite number. That is, it is neither
+// an infinite, nor a NaN (including indefinite NaN), nor a denormalized number.
+// You generally want to write math operation checking code that asserts for 
+// IsNormal() as opposed to checking specifically for IsNaN, etc.
+//
+// Normal values are defined as any floating point value with an exponent in 
+// the range of [1, 254], as 0 is reserved for denormalized (underflow) values 
+// and 255 is reserved for infinite (overflow) and NaN values. 
+//
+</span>inline bool IsNormal(float32_t fValue);
+inline bool IsNormal(float64_t fValue);
+ 
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////////////
+// IsNAN
+//
+// A NaN is a special kind of number that is neither finite nor infinite. 
+// It is the result of doing things like the following:
+//    float x = 1 * NaN;
+//    float x = NaN + NaN;
+//    float x = 0 / 0;
+//    float x = 0 / infinite;
+//    float x = infinite - infinite
+//    float x = sqrt(-1);
+//    float x = cos(infinite);
+// Under the VC++ debugger, x will be displayed as 1.#QNAN00 or 1.#IND00 and 
+// the bit representation of x will be 0x7fc00001 (in the case of 1 * NaN). 
+// The 'Q' in front of NAN stands for "quiet" and means that use of that value 
+// in expressions won't generate exceptions. A signaling NaN (SNAN) means that 
+// use of the value would generate exceptions.
+// 
+// NaNs are frequently generated in physics simulations and similar mathematical 
+// situations when you are simulating an object moving or turning over time but 
+// the time or distance differential in the calculation is very small. 
+// Also, floating point roundoff error can generate NaNs if you do things 
+// like call acos(x) where you didn't take care to clamp x to &lt;= 1. You can 
+// also get a NaN when memory used to store a floating point value is written 
+// with random data.
+//
+// A curious property of NaNs is that all comparisons between NaNs return 
+// false except the expression: NaN != NaN. This is so even if the bit 
+// representation of the two compared NaNs are identical. Thus, with NaNs, 
+// the following holds:
+//    x == x is always false
+//    x < y is always false
+//    x > y is always false
+//
+// As a result, one simple way to test for a NaN without fiddling with bits is 
+// to simply test for x == x. If this returns false, then you have a NaN. 
+// Unfortunately, many C and C++ compilers don't obey this, so you are usually 
+// stuck fiddling with bits.
+//
+// With a NaN, all exponent bits are 1 and the mantissa is not zero.
+// If the highest fraction bit is 1, the NAN is "quiet" -- it represents
+// and indeterminant operation rather than an invalid one.
+//</span>
+inline bool IsNAN(float32_t fValue);
+inline bool IsNAN(float64_t fValue);
+
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////////////
+// IsInfinite
+//
+// A value is infinity if the exponent bits are all 1 and all the bits of the 
+// mantissa (significand) are 0. The sign bit indicates positive or negative
+// infinity. Thus, for Float32, 0x7f800000 is positive infinity and 0xff800000
+// is negative infinity.
+//
+</span>inline bool IsInfinite(float32_t fValue);
+inline bool IsInfinite(float64_t fValue);
+
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////////////
+// IsIndefinite
+//
+// An indefinite is a special kind of NaN that is used to signify that an 
+// operation between non-NaNs generated a NaN. Other than that, it really is 
+// simply another NaN.
+//
+</span>inline bool IsIndefinite(float32_t fValue);
+inline bool IsIndefinite(float64_t fValue);
+
+<span class="code-example-comment">///////////////////////////////////////////////////////////////////////////////
+// IsDenormalized
+//
+// Much in the same way that infinite numbers represent an overflow, 
+// denormalized numbers represent an underflow. A denormalized number is 
+// indicated by an exponent with a value of zero. You get a denormalized 
+// number when you do operations such as this:
+//    float x = 1e-10 / 1e35;
+// Under the VC++ debugger, x will be displayed as 1.4e-045#DEN and the 
+// bit representation of x will be 0x00000001. Unlike infinites and NaNs, 
+// you can still do math with denormalized numbers. However, the results 
+// of your math will likely have a lot of imprecision. You can also get a 
+// denormalized value when memory used to store a floating point value is 
+// written with random data.
+//
+</span>inline bool IsDenormalized(float32_t fValue);
+inline bool IsDenormalized(float64_t fValue);</pre>
+
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+</body></html>
+
+
+

+ 144 - 0
doc/EAMemory.html

@@ -0,0 +1,144 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    
+<title>EAMemory</title>
+<link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+    <style type="text/css">
+<!--
+.style1 {font-size: smaller}
+-->
+    </style>
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAMemory</h1>
+
+<p>EAMemory provides functions related to copying and filling memory. </p>
+<p>Note the presence of &quot;C&quot; functions below such as MemcpyC. These refer to memory functions that operate only on cacheable memory, but are faster than otherwise. Cacheable memory is standard system RAM used by applications and is the memory you work with 98% of the time. However, on some hardware systems (e.g. gaming console machines) there is uncacheable memory, such as memory that is mapped to video addresses. This memory is typically called write-through or write-combined memory and is useful for writing data in one direction from system RAM to video memory. The &quot;C&quot; functions do not work on this kind of memory and you can instead use the regular functions such as Memcpy for uncacheable memory. The C here has nothing to do with the C vs. C++ language.</p>
+<table border="1">
+  <tr>
+    <td colspan="2" valign="top" scope="col"><h3>Memcpy</h3></td>
+  </tr>
+  <tr>
+    <td width="527" valign="top"><span class="style1">char8_t* Memcpy (void* pDest, const void* pSource, size_t n)</span></td>
+    <td rowspan="5" valign="top"><p>Copies nByteCount bytes from pSource to pDestination. The source and destination memory ranges must not overlap. Returns pDestination. There are no restrictions in the way of size, alignment, or memory type, though the source memory must be readable and the destination memory must be writable. </p>
+    <p>The MemcpyS  function copies memory from source to destination without filling the cache with the memory. This is useful for when you want to write memory that will not be read by the processor used to write it, such as when the CPU writes to memory used by the GPU. </p>
+    <p>The Memcpy128 function is useful for higher performance memory copies when the requirements can be met. The address pointed to by pDestination must be aligned on a 128-byte boundary, and uint8Count must be a multiple of 128.</p></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* MemcpyC (void* pDest, const void* pSource, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* MemcpyS (void* pDest, const void* pSource, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* Memcpy128 (void* pDest, const void* pSource, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* Memcpy128C (void* pDest, const void* pSource, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td colspan="2" valign="top"><h3>Memmove</h3></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* Memmove (void* pDest, const void* pSource, size_t n)</span></td>
+    <td rowspan="2" valign="top">Copies nByteCount bytes from pSource to pDestination. The source and destination memory ranges may overlap. Returns pDestination. There are no restrictions in the way of size, alignment, or memory type, though the source memory must be readable and the destination memory must be writable. </td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">char8_t* MemmoveC (void* pDest, const void* pSource, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td colspan="2" valign="top"><h3>Memset</h3></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint8_t* Memset8 (void* pDest, uint8_t c, size_t uint8Count)</span></td>
+    <td rowspan="10" valign="top"><p> The standard memset function replicates a given 8 bit value into a memory block. However, we might want to replicate a 16 bit, 32 bit, or 64 bit value into a block. That's what these functions do. The MemsetN function is a  generic version which can copy unusual sizes such as 24 bits (e.g. RGB fills). The count values for each of these is the count of uint16_t, count of uint32_t, count of pointers, etc.<br>
+  <br>
+  Memset8 is the same as the memset function. We provide additional variations of memset which set uint16_t values, uint32_t value, etc. instead of uint8_t values like Memset8. MemsetN writes a generic type of any size. In each case pDestination must point to enough memory to hold full values. Thus pDestination for Memset32 must have a capacity for at least (uint32Count * sizeof(uint32_t)) bytes. The destination is required to be aligned to its type. Thus the destination of Memset32 must be 32 bit aligned. There are no restrictions about the type of memory pDestination refers to except that it be writable.</p>
+    </td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint8_t* Memset8C (void* pDest, uint8_t c, size_t uint8Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint8_t* Memset8_128 (void* pDest, uint8_t c, size_t uint8Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint8_t* Memset8_128C (void* pDest, uint8_t c, size_t uint8Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint16_t* Memset16 (void* pDest, uint16_t c, size_t uint16Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint32_t* Memset32 (void* pDest, uint32_t c, size_t uint32Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint64_t* Memset64 (void* pDest, uint64_t c, size_t uint64Count)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void* MemsetN (void* pDest, const void* pSource, size_t sourceBytes, size_t nCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void Memclear (void* pDest, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void MemclearC (void* pDest, size_t n)</span></td>
+  </tr>
+  <tr>
+    <td colspan="2" valign="top"><h3>Memfill</h3></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void Memfill16 (void* pDest, uint16_t c, size_t byteCount)</span></td>
+    <td rowspan="5" valign="top">Memfill is the same as memset except that the count parameter is a count of bytes and not (for example) a count of uint32_t values. Memfill supports byte counts that aren't an even multiple the value size. Thus a call to Memfill32(p, 0x00112233, 3) is valid and does what you would expect. MemFill8 is not defined because it is the same thing as Memset8. MemfillSpecific fills (and potentially repeats) any source pattern into any destination space. There are no restrictions about the type of memory pDest refers to except that it be writable.</td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void Memfill24 (void* pDest, uint32_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void Memfill32 (void* pDest, uint32_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void Memfill64 (void* pDest, uint64_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void MemfillSpecific(void* pDest, const void* pSource, size_t destByteCount, size_t sourceByteCount)</span></td>
+  </tr>
+  <tr>
+    <td colspan="2" valign="top"><h3>Memcheck</h3></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint8_t* Memcheck8 (void* pDest, uint8_t c, size_t byteCount)</span></td>
+    <td rowspan="4" valign="top">This family of functions is like Memfill except it verifies that the memory is filled as per the value and byte count. Returns a pointer to the first mis-matching byte if there's a mismatch. Returns NULL if  there are no mismatches. There are no restrictions about the type of memory pDest refers to except that it be readable.</td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint16_t* Memcheck16 (void* pDest, uint16_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint32_t* Memcheck32 (void* pDest, uint32_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">uint64_t* Memcheck64 (void* pDest, uint64_t c, size_t byteCount)</span></td>
+  </tr>
+  <tr>
+    <td colspan="2" valign="top"><h3>Misc</h3></td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">const void* Memchr(const void* p, char8_t c, size_t n)</span></td>
+    <td valign="top">This is the same as memchr and wmemchr. Searches the first n characters (not necessarily bytes) of the memory block pointed to by pString for character c. Returns a pointer to the character or NULL if not found. There are no restrictions about the type of memory p refers to except that it be readable.</td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">int Memcmp(const void* p1, const void* p2, size_t n)</span></td>
+    <td valign="top">Same as memcmp and wmemcmp. Compares the first n bytes of two memory blocks pointed by p1 and p2.  The comparison is a bytewise compare and thus for strings it is case-sensitive.  For a case-insensitive string comparison, use the Stricmp function. Bytes are treated as uint8_t for comparison purposes. Returns 0 if the memory is equal, &lt; 0 if p1 &lt; p2, and &gt; 0 if p1 &gt; p2. There are no restrictions about the type of memory p1 and p2 refer to except that they be readable.</td>
+  </tr>
+  <tr>
+    <td valign="top"><span class="style1">void* EAAlloca(size_t n)</span></td>
+    <td valign="top"><p>EAAlloca is a portable declaration for the alloca function. </p>
+    <p>The alloca function allocates space in the stack frame of the caller, and returns a pointer to the allocated block. This temporary space is automatically freed when the function from which alloca is called returns.</p></td>
+  </tr>
+</table>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+</body></html>
+
+
+

+ 85 - 0
doc/EAOSGlobal.html

@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    <title>EAOSGlobal</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAOSGlobal</h1>
+
+<h2>Introduction</h2>
+<p>OS globals are process-wide globals (singletons, actually) and are shared between an EXE and DLLs, though you can use them on platforms that don't support DLLs. The Windows OS does not directly support this concept of global object sharing, as the only way to accomplish this under Windows is to sequester the global object in its own DLL of which the EXE and all DLLs explicitly reference. OS Globals allow you to have the global object anywhere (including the EXE) and does so without requiring explicit linking. The OS global system works at the operating system level and has auto-discovery logic so that no pointers or init calls need to be made between modules for them to link their OS global systems together. The OS global system consists of EAOSGlobal and <a href="EAGlobal.html">EAGlobal</a>. The latter provides simpler functionality but has less overhead.</p>
+<p>A primary use of OS globals is in the creation of application singletons such as the main heap, messaging servers, asset managers, etc. <br>
+  <br>
+  Note that the implementation behind OS globals (EAOSGlobal.cpp) may seem a bit convoluted; this is because it needs to be thread-safe, cross-module, and independent of application-level memory allocators. For objects for which order of initialization is clearer and you don't need to reference singletons across DLLs, EASingleton is probably a better choice, as it is simpler and has lower overhead. Indeed, another way of looking at OS globals is to view them as process-wide singletons. </p>
+<p>The easiest way to use OS globals is via their auto pointers, which make OS globals just about trivial to use. We will cover the major components of EAOSGlobal below, starting with the auto pointers. </p>
+<p><strong>Caveats</strong></p>
+<p>OS globals have the potential for resulting in code that's duplicated within each DLL and thus increasing code memory usage. It might be useful to use an OS global to store a pointer to an interface rather  than an instance of an interface, and have just a single entity within the application provide the implementation. With this approach you still get the benefit of users of the global not having to know ahead of time where the implementation is as you otherwise need to do with standard DLL linking via &quot;dllexport.&quot;</p>
+<p>OS globals probably aren't needed in cases where you can simply use dllexport (and other platforms' equivalents).</p>
+<h2>AutoOSGlobalPtr</h2>
+<p> Holds a reference to an OS global of the specified type and Id. If the OS global does not exist, a new one is created in the shared heap. The Id parameter is an arbitrary guid and allows the user to have multiple OSGlobalPtrs of the same stored type T.<br>
+<br>
+OS global lookup is not very fast so the preferred usage of this class is to wrap it in an accessor. This also ensures that the OS global stays around while created.</p>
+<p>Example usage: </p>
+<pre class="code-example">AutoOSGlobalPtr&lt;Widget, 0x11111111&gt; gpWidget1A;  <span class="code-example-comment">// Declare a global pointer.</span><br>AutoOSGlobalPtr&lt;Widget, 0x11111111&gt; gpWidget1B;  <span class="code-example-comment">// Points to the same global Widget as gpWidget1A.</span><br>AutoOSGlobalPtr&lt;Widget, 0x22222222&gt; gpWidget2;   <span class="code-example-comment">// Points to a different Widget, as it has the 0x22222222 id.</span><br><br>void Foo()
+{<br>    assert(gpWidget1A == gpWidget1B);<br>    assert(gpWidget1A != gpWidget2);<br><br>    gpWidget1A-&gt;DoSomething();<br>    gpWidget2-&gt;DoSomething();<br>}</pre>
+<h2>AutoStaticOSGlobalPtr</h2>
+<p> Holds a reference to a OS global of the specified type and ID. If the OS global does not exist, a new one is created using static memory.<br>
+ The Id parameter is an arbitrary guid and allows the user to have  multiple OSGlobalPtrs of the same stored type T.<br>
+<br>
+The advantage of this class is that it uses static memory, so it does not contribute to heap usage and it always succeeds in allocating the object. The disadvantage is that if multiple DLLs are involved each will have its own static space reserved for the OS global.</p>
+<p>AutoStaticOSGlobalPtrs and AutoOSGlobalPtrs should not be mixed when referring to a OS global. You should reference an OSGlobal via either one or more AutoOSGlobalPtrs, one or more AutoStaticOSGlobalPtrs, but not both at the same time.</p>
+<p>Example usage (note that it is virtually identical to the AutoOSGlobalPtr example): </p>
+<pre class="code-example">AutoStaticOSGlobalPtr&lt;Widget, 0x11111111&gt; gpWidget1A;  <span class="code-example-comment">// Declare a global pointer.</span><br>AutoStaticOSGlobalPtr&lt;Widget, 0x11111111&gt; gpWidget1B;  <span class="code-example-comment">// Points to the same global Widget as gpWidget1A.</span><br>AutoStaticOSGlobalPtr&lt;Widget, 0x22222222&gt; gpWidget2;   <span class="code-example-comment">// Points to a different Widget, as it has the 0x22222222 id.</span><br><br>void Foo()
+{<br>    assert(gpWidget1A == gpWidget1B);<br>    assert(gpWidget1A != gpWidget2);<br><br>    gpWidget1A-&gt;DoSomething();<br>    gpWidget2-&gt;DoSomething();<br>}</pre>
+<h2>Low level functionality </h2>
+<p>The above auto pointer classes are really just wrappers around the low level OS global API. Most of the time you want to use the auto pointers. We provide a description of the lowel level API here for those who need to access OS globals in a custom way.</p>
+<p>The low level system is comprised of the following four entities: </p>
+<pre class="code-example">struct OSGlobalNode : public eastl::intrusive_list_node { };
+ 
+typedef OSGlobalNode* (*OSGlobalFactoryPtr)();
+ 
+OSGlobalNode* GetOSGlobal(uint32_t id, OSGlobalFactoryPtr pFactory);
+ 
+bool ReleaseOSGlobal(OSGlobalNode* pOSGlobalNode);
+</pre>
+<p>Example usage:</p>
+<pre class="code-example">struct Widget : public OSGlobalNode
+{
+    <span class="code-example-comment">// User data here.</span>
+};
+    
+Widget* WidgetFactory()
+{
+    return new Widget;
+}
+
+ 
+int main(int, char**)
+{
+    Widget* pWidget = GetOSGlobal(0x12345678, WidgetFactory);
+    
+    pWidget-&gt;DoSomething();
+    
+    if(ReleaseOSGlobal(pWidget))
+        delete pWidget;
+    
+    return 0;
+}</pre>
+<p>The above example looks like a basic  singleton pattern, and it is such a thing. The difference between this and your typical singleton system is that the OS global system solves the potentially tricky problems of cross-DLL access, thread safety, and memory allocation. </p>
+<p>For additional example usage,  see the source code to AutoOSGlobalPtr in EAOSGlobal.h.</p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p></p>
+</body></html>
+
+
+

+ 374 - 0
doc/EARandom.html

@@ -0,0 +1,374 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    
+<title>EARandom</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EARandom</h1>
+
+<h2>Introduction</h2>
+<p>EARandom provides pseudo-random number generation suitable for most kinds of 
+  professional game development. Functionality is divided into two modules:</p>
+<ul>
+  <li>Core random number generation (e.g. random integer, random float)</li>
+  <li>Variable distribution generation (e.g. Gaussian distribution, triangle distribution)</li>
+</ul>
+<p>EARandom provides two core generators which address a speed/randomness tradeoff:</p>
+<ul>
+  <li> RandomLinearCongruential (very fast, also known as RandomFast, but still good randomness)</li>
+  <li>RandomMersenneTwister (very random, also known as RandomQuality)</li>
+</ul>
+<p>EARandom provides a number of distribution generators, each of which uses the 
+  above core generators underneath:</p>
+<ul>
+  <li>RandomBool</li>
+  <li>Random2, Random4, Random8, ... Random256</li>
+  <li>RandomInt32UniformRange</li>
+  <li>RandomDoubleUniformRange</li>
+  <li>RandomUint32WeightedChoice</li>
+  <li>RandomUint32Gaussian</li>
+  <li>RandomUint32Triangle</li>
+</ul>
+<p><i>Note: EARandom is not certified nor suitable for use in certified gambling 
+  software. There are strict standards regarding such software which EARandom 
+  does not try to comply with. Similarly, EARandom is not suitable for use in security-related entropy collection (at least not by itself). EARandom's goal is to provide good random number 
+  generation with high efficiency.</i></p>
+<h2>Don't use the C rand() function!</h2>
+<p> The C library rand function does an OK job as a basic random number generator 
+  for testing and hobby purposes. However, the rand function is generally not 
+  suitable for professional game software. This is because the rand function:</p>
+<ul>
+  <li>Generates only numbers between 0 and RAND_MAX, which is usually 32767.</li>
+  <li>Doesn't generate random numbers within a prescribed range. Using the % operator 
+    to rectify this is slow (requires integer division) and produces a lopsided 
+    distribution unless the divisor is evenly divisible into RAND_MAX.</li>
+  <li>Generates rather poor random numbers. In particular, they tend to have obvious 
+    patterns in the low bits. In some cases this can result in users taking advantage 
+    of the number generator at the expense of other players or your game franchise 
+    itself.</li>
+  <li>Exists only as a single instance of a generator which is shared with the 
+    entire application. There is no way to make another instance.</li>
+  <li>Is slow. Generating a random number via rand() was measured as 70% slower 
+    than the EARandom generator on modern hardware.</li>
+  <li>Doesn't generate floating point numbers.</li>
+</ul>
+<h2>How random is EARandom?</h2>
+<p>Both RandomLinearCongruential (i.e. RandomFast) and RandomMersenneTwister (RandomQuality) provide randomness that is likely good enough for most convential game uses. Don't be fooled by the &quot;linear congruential&quot; name; it's not the bad generator that you might think based on what you've read about old C rand implementations. </p>
+<p>A good way to test randomness is with the DieHard randomness tests. See http://en.wikipedia.org/wiki/Diehard_tests for information about DieHard. We have a copy of the <a href="http://eaos.rws.ad.ea.com:8080/@md=d&cd=//EAOS/UTF/DL/UTFResearch/DieHard%20Random%20Number%20Tester/&c=2lr@//EAOS/UTF/DL/UTFResearch/DieHard%20Random%20Number%20Tester/bin/?ac=83">DieHard tester in EAOS</a> which is used to test EARandom and could test any other random number generator. EARandom produces DieHard scores which are pretty good. A number of home-grown random number generators have shown much worse results. </p>
+<h2>Common misuses</h2>
+<p>It is worth mentioning that it is surprisingly common for users of random number 
+  generators (including EARandom) to come to the belief that the generator is 
+  broken when in fact they are misusing the generator. Common misuses of generators 
+  include:</p>
+<ul>
+  <li>Seeding a generator with the same seed every time it's used.</li>
+  <li>Seeding two generators at the same time via the system clock and finding 
+    that they produce idential values.</li>
+  <li>Using 'RandomUint32Uniform() % 5000' instead of 'RandomUint32Uniform(5000)'.</li>
+  <li>Inventing flawed distribution generators.</li>
+  <li>Misusing the results of a generator but assuming the generator is yielding 
+    bad values.</li>
+  <li>Creating a random number generator for a single use right when it is needed. 
+    This is usually bad because the first generated value is no more random than 
+  the seed used to generate the number.</li>
+</ul>
+<h2>Example usage </h2>
+<p>EARandom is fairly straightforward to use. Just avoid the above common mistakes 
+  and things should work well.</p>
+<p>Mixed integer math expressions.</p>
+<pre class="code-example">EA::RandomFast rng(GetCPUTick());           <span class="code-example-comment">// Seed with a hypthetical CPU tick function.</span>
+
+uint32_t n = rng.RandomUint32Uniform();     <span class="code-example-comment">// Generate value in the range of [0, 0xffffffff] (all bit patterns).</span>
+uint32_t i = rng.RandomUint32Uniform(200);  <span class="code-example-comment">// Generate value in the range of [0, 200).</span>
+double   d = rng.RandomDoubleUniform();     <span class="code-example-comment">// Generate value in the range of [0, 1).</span>
+double   f = rng.RandomDoubleUniform(37);   <span class="code-example-comment">// Generate value in the range of [0, 37).</span></pre>
+How to use EARandom with STL (including EASTL).
+<pre class="code-example">#include &lt;algorithm&gt;
+#include &lt;vector&gt;
+
+std::vector    v;
+EA::RandomFast rng;
+
+std::random_shuffle(v.begin(), v.end(), rng);</pre>
+How to use basic random distributions. Note that these functions take a random number generator as an argument.
+<pre class="code-example">EA::RandomQuality rng;
+
+bool    b = EA::RandomBool(rng);
+int32_t n = EA::Random256(rng);
+double  d = EA::RandomDoubleGaussian(rng, 15.0, 30.0);</pre>
+<p>How to use RandomUint32WeightedChoice. This function is useful for generating 
+  a custom distribution.</p>
+<pre class="code-example">EA::RandomQuality rng;
+const float       weights[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 }; // Create a triangle distribution
+
+uint32_t i = EA::RandomUint32WeightedChoice(rng, 10, weights); </pre>
+<h2>Interface</h2>
+<h3>RandomLinearCongruential</h3>
+<p>This algorithm generates good enough pseudorandom numbers for most simulationuses. 
+  Its biggest weakness is that there are some patterns that occur in the lower 
+  bits. However, it is still far better than the C rand function. </p>
+<pre class="code-example">class RandomLinearCongruential
+{
+public:
+	typedef uint32_t result_type;
+ 
+
+<span class="code-example-comment">    /// RandomLinearCongruential
+    /// Constructs the random number generator with a given initial state (seed).
+    /// If the seed is 0xffffffff (MAX_UINT32), then the seed is generated automatically
+    /// by a semi-random internal mechanism such as reading the system clock. Note that 
+    /// seeding by this mechanism can yield unexpected poor results if you create multiple
+    /// instances of this class within a short period of time, as they will all get the 
+    /// same seed due to the system clock having not advanced.
+</span>    RandomLinearCongruential(uint32_t nSeed = 0xffffffff);
+
+<span class="code-example-comment">    /// RandomLinearCongruential
+    /// Copy constructor
+</span>    RandomLinearCongruential(const RandomLinearCongruential& randomLC);
+
+<span class="code-example-comment">    /// operator =
+</span>    RandomLinearCongruential& operator=(const RandomLinearCongruential& randomLC);
+
+<span class="code-example-comment">    /// GetSeed
+    /// Gets the state of the random number generator, which can be entirely 
+    /// defined by a single uint32_t. 
+</span>    uint32_t GetSeed() const;
+
+<span class="code-example-comment">    /// SetSeed
+    /// Sets the current state of the random number generator, which can be 
+    /// entirely defined by a single uint32_t.
+</span>    void SetSeed(uint32_t nSeed = 0xffffffff);
+
+<span class="code-example-comment">    /// operator ()
+    /// Generates a random uint32 with an optional limit. Acts the same as the 
+    /// RandomUint32Uniform(uint32_t nLimit) function. This function is useful for
+    /// some templated uses whereby you want the class to act as a function object.
+    /// If the input nLimit is 0, then the return value is from 0 to MAX_UINT32 inclusively.
+</span>    uint32_t operator()(uint32_t nLimit = 0);
+
+<span class="code-example-comment">    /// RandomUint32Uniform
+    /// Return value is from 0 to MAX_UINT32 inclusively, with uniform probability.
+    /// This is the most basic random integer generator for this class; it has no 
+    /// extra options but is also the fastest. Note that if you want a random
+    /// integer between 0 and some value, you should use RandomUint32Uniform(nLimit)
+    /// and not use RandomUint32Uniform() % nLimit. The former is both faster and 
+    /// more random; using % to achieve a truly random distribution fails unless
+    /// nLimit is evenly divisible into MAX_UINT32.
+</span>    uint32_t RandomUint32Uniform();
+
+<span class="code-example-comment">    /// RandomUint32Uniform (with limit)
+    /// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
+</span>    uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+<span class="code-example-comment">    /// RandomDoubleUniform
+    /// Output is in range of [0, 1) with uniform distribution.
+</span>    double RandomDoubleUniform();
+
+<span class="code-example-comment">    /// RandomDoubleUniform (with limit)
+    /// Output is in range of [0, limit) with uniform distribution.
+</span>    double RandomDoubleUniform(double limit);
+};
+</pre>
+<h3>RandomTaus</h3>
+<p> RandomTaus is slower than the other EARandom generators but has only 12 bytes of state data. RandomLinearCongruental has only 4 bytes of data but is not as random as RandomTaus. RandomMersenneTwister is more random than RandomTaus but has about 2500 bytes of state data. Thus RandomTaus is a tradeoff. This generator optimizes randomness and and to some degree size at the cost of speed.</p>
+<pre class="code-example">class RandomTaus
+{
+public:
+    typedef uint32_t result_type;
+
+
+    RandomTaus(uint32_t nSeed = 0xffffffff);
+    RandomTaus(const uint32_t* pSeedArray); <span class="code-example-comment">// Array of 3 uint32_t values.</span>
+ 
+    RandomTaus(const RandomTaus&amp; randomT);
+    RandomTaus&amp; operator=(const RandomTaus&amp; randomT);
+
+<span class="code-example-comment">    // Single uint32_t version, for compatibility.
+    // Use the seed array version for best behavior.
+    // Not guaranteed to return the uint32_t set by SetSeed(uint32_t).
+</span>    uint32_t GetSeed() const;
+    void SetSeed(uint32_t nSeed = 0xffffffff);
+
+    void GetSeed(uint32_t* pSeedArray) const; <span class="code-example-comment">// Array of 3 uint32_t values.</span>
+    void SetSeed(const uint32_t* pSeedArray); <span class="code-example-comment">// Array of 3 uint32_t values.</span>
+
+<span class="code-example-comment">    /// Output is in range of [0, nLimit) with uniform distribution.
+</span>    uint32_t operator()(uint32_t nLimit = 0);
+
+<span class="code-example-comment">    /// Output is in range of [0, UINT32_MAX] with uniform distribution.
+</span>    uint32_t RandomUint32Uniform();
+
+<span class="code-example-comment">    /// Output is in range of [0, nLimit) with uniform distribution.
+</span>    uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+<span class="code-example-comment">    /// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
+</span>    double RandomDoubleUniform();
+
+<span class="code-example-comment">    /// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
+    /// limit is a value &gt; 0.
+</span>    double RandomDoubleUniform(double limit);<br><span class="code-example-comment"></span>};</pre>
+<h3>RandomMersenneTwister</h3>
+<p>This algorithm implements a random number generator via the Mersenne Twister 
+  algorithm. This algorithm is popular for its very high degree of randomness 
+  (period of 2^19937-1 with 623-dimensional equidistribution) while achieving 
+  good speed.</p>
+<pre class="code-example">class RandomMersenneTwister
+{
+public:
+    typedef uint32_t result_type;
+ 
+<span class="code-example-comment">    /// enum SeedArray
+    /// This enum is public because it allows the user to know how much 
+    /// data or space to provide for the GetSeed and SetSeed functions.
+</span>    enum SeedArray { kSeedArrayCount = 624 };
+
+    RandomMersenneTwister(uint32_t nSeed = 0xffffffff);
+    RandomMersenneTwister(const uint32_t seedArray[], unsigned nSeedArraySize);
+    RandomMersenneTwister(const RandomMersenneTwister& randomMT);
+
+    RandomMersenneTwister& operator=(const RandomMersenneTwister& randomMT);
+
+    void     GetSeed(uint32_t seedArray[], unsigned nSeedArraySize) const;
+    void     SetSeed(const uint32_t seedArray[], unsigned nSeedArraySize);
+    void     SetSeed(uint32_t nSeed = 0xffffffff);
+
+    uint32_t operator()(uint32_t nLimit = 0);
+
+    uint32_t RandomUint32Uniform();                                          
+    uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+    double   RandomDoubleUniform();
+    double   RandomDoubleUniform(double limit);
+};</pre>
+<h3>Random distributions</h3>
+<p>Here is a list of the currently provided distribution functions.</p>
+<pre class="code-example"><span class="code-example-comment">/// RandomBool
+/// Returns true or false.
+</span>template &lt;typename Random&gt;
+bool RandomBool(Random& r);
+
+<span class="code-example-comment">/// Random2
+/// Returns a value between 0 and 1, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random2(Random& r);
+
+<span class="code-example-comment">/// Random4
+/// Returns a value between 0 and 3, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random4(Random& r);
+
+<span class="code-example-comment">/// Random8
+/// Returns a value between 0 and 7, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random8(Random& r);
+
+<span class="code-example-comment">/// Random16
+/// Returns a value between 0 and 15, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random16(Random& r);
+
+<span class="code-example-comment">/// Random32
+/// Returns a value between 0 and 31, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random32(Random& r);
+
+<span class="code-example-comment">/// Random64
+/// Returns a value between 0 and 63, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random64(Random& r);
+
+<span class="code-example-comment">/// Random128
+/// Returns a value between 0 and 127, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random128(Random& r);
+
+<span class="code-example-comment">/// Random256
+/// Returns a value between 0 and 255, inclusively.
+</span>template &lt;typename Random&gt;
+int32_t Random256(Random& r);
+
+<span class="code-example-comment">/// RandomPowerOfTwo
+/// Returns a value between 0 and 2 ^ nPowerOfTwo - 1, inclusively. 
+/// This is a generalized form of the RandomN set of functions.
+</span>template &lt;typename Random&gt;
+int32_t RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo);
+
+<span class="code-example-comment">/// RandomInt32UniformRange
+/// Return value is from nBegin to nEnd-1 inclusively, with uniform probability.
+</span>template &lt;typename Random&gt;
+int32_t RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd);
+
+<span class="code-example-comment">/// RandomDoubleUniformRange
+/// Return value is in range of [nBegin, nEnd) with uniform probability.
+</span>template <class Random><class Random>&lt;typename Random&gt;
+double RandomDoubleUniformRange(Random& r, double begin, double end);
+
+<span class="code-example-comment">/// RandomUint32WeightedChoice
+/// Return value is from 0 to nLimit-1 inclusively, with probabilities proportional to weights.
+/// The input array weights must be of length <nlimit>. These values are used to 
+/// determine the probability of each choice. That is, weight[i] is proportional 
+/// to the probability that this function will return i. Negative values are ignored. 
+/// This function is useful in generating a custom distribution.
+</span>template &lt;typename Random&gt;
+uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, float weights[]);
+
+<span class="code-example-comment">/// RandomUint32Gaussian
+/// Return value is in the range [0, nLimit) with Gaussian (a.k.a 'normal') distribution.
+</span>template &lt;typename Random&gt;
+uint32_t RandomUint32Gaussian(Random& r, int32_t nBegin, int32_t nEnd);
+
+<span class="code-example-comment">/// RandomDoubleGaussian
+/// Return value is in the range [0, nLimit) with Gaussian (a.k.a 'normal') distribution.
+</span>template &lt;typename Random&gt;
+double RandomDoubleGaussian(Random& r, double nBegin, double nEnd);
+
+<span class="code-example-comment">/// RandomUint32Triangle
+/// Return value is in the range [0, nLimit) with triangular distribution.
+</span>template &lt;typename Random&gt;
+uint32_t RandomUint32Triangle(Random& r, int32_t nBegin, int32_t nEnd);
+
+<span class="code-example-comment">/// RandomDoubleTriangle
+/// Return value is in the range [0, nLimit) with triangular distribution.
+</span>template &lt;typename Random&gt;
+double RandomDoubleTriangle(Random& r, double nBegin, double nEnd);
+
+</pre>
+<h3>Random fills and shuffles</h3>
+<p>How to fill a container or sequence with random uint32_t values:</p>
+<pre class="code-example">#include &lt;eastl/algorithm.h&gt;  <span class="code-example-comment">                           // or #include &lt;algorithm&gt; to use std STL.</span>
+ 
+EA::StdC::Random rand(someSeedValue);                    <span class="code-example-comment">// We can just use EA::StdC::Random directly because</span> <br>eastl::generate(myVector.begin(), myVector.end(), rand); <span class="code-example-comment">// it has an operator() that returns uint32_t.</span></pre>
+<p>How to randomize (shuffle) an existing container or sequence of uint32_t values:</p>
+<pre class="code-example">#include &lt;eastl/algorithm.h&gt;  <span class="code-example-comment">                           // or #include &lt;algorithm&gt; to use std STL.</span>
+ 
+EA::StdC::Random rand(someSeedValue);
+eastl::random_shuffle(myVector.begin(), myVector.end(), rand);</pre>
+<p>How to fill a container or sequence with random double:</p>
+<pre class="code-example">#include &lt;eastl/algorithm.h&gt;  <span class="code-example-comment">                           // or #include &lt;algorithm&gt; to use std STL.</span>
+ 
+struct GenerateDouble
+{ <br>    EA::StdC::Random mRand;                              <span class="code-example-comment">// We need to make a tiny class that simply has</span> <br>                                                         <span class="code-example-comment">// an operator() member function that returns double.</span>
+    GenerateDouble(uint32_t seed) : mRand(seed){} <br>
+    double operator(){ mRand.RandomDoubleUniform(); }
+};<br>
+GenerateDouble randDouble(someSeedValue);<br>eastl::generate(myVector.begin(), myVector.end(), randDouble);</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 146 - 0
doc/EAScanf.html

@@ -0,0 +1,146 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <meta http-equiv="content-type"
+ content="text/html; charset=ISO-8859-1">
+  <title>EAScanf</title>
+  <link type="text/css" rel="stylesheet"
+ href="UTFDoc.css">
+  <meta name="author" content="Paul Pedriana">
+</head>
+<body style="background-color: rgb(255, 255, 255);">
+<h1>EAScanf</h1>
+<h2>Introduction</h2>
+<p>EAScanf provides a portable
+version of the C scanf family of functions which improves on
+the C family in the following ways:</p>
+<ul>
+  <li>Behaves identically on all
+platforms</li>
+  <li>Provides the full gamut of
+scanf functions, including vsscanf</li>
+  <li>Is more efficient</li>
+  <li>Never allocates memory</li>
+  <li>Provides both 8 and 16 bit
+versions for all platforms</li>
+  <li>Provides&nbsp;useful
+extended functionality</li>
+  <li>Complies with the C99
+standard (except for the %a format, as of this writing)</li>
+  <li>Is readable enough to be
+traced/debugged by non-experts</li>
+  <li>Is savvy to UTF8 Unicode</li>
+</ul>
+<p>The primary disadvantages of
+EAScanf are:</p>
+<ul>
+  <li>It doesn't use
+locale-specific formatting</li>
+  <li>It isn't part of the C/C++
+standard library</li>
+</ul>
+<p>EAScanf doesn't attempt to
+solve the locale formatting problem because users really are better off
+using serious locale libraries for such things rather than using the
+meager support provided by the C standard library. The EALocale library
+attempts to provide such functionality.</p>
+<p>EAScanf provides the
+following functions in 8 and 16 bit versions:
+</p>
+<pre><span class="code-example">int Cscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, ...);
+int Fscanf(FILE* pFile, const char8_t* pFormat, ...);
+int Scanf(const char8_t* pFormat, ...);
+int Sscanf(const char8_t*  pTextBuffer, const char8_t* pFormat, ...);
+
+int Cscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, ...);
+int Fscanf(FILE* pFile, const char16_t* pFormat, ...);
+int Scanf(const char16_t* pFormat, ...);
+int Sscanf(const char16_t* pTextBuffer, const char16_t* pFormat, ...);
+
+int Vcscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, va_list arguments);
+int Vfscanf(FILE* pFile, const char8_t* pFormat, va_list arguments);
+int Vscanf(const char8_t* pFormat, va_list arguments);
+int Vsscanf(const char8_t* pTextBuffer, const char8_t* pFormat, va_list arguments);
+
+int Vcscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments);
+int Vfscanf(FILE* pFile, const char16_t* pFormat, va_list arguments);
+int Vscanf(const char16_t* pFormat, va_list arguments);
+int Vsscanf(const char16_t* pTextBuffer, const char16_t* pFormat, va_list arguments);</span></pre>
+<blockquote> </blockquote>
+<h2>Extended Functionality</h2>
+<p> EAPrintf provides extended
+format functionality not found in the C99 standard but which is useful
+nevertheless:</p>
+<table style="text-align: left; width: 100%;" border="1" cellpadding="2"
+ cellspacing="2">
+  <tbody>
+    <tr style="font-weight: bold;">
+      <td>Format</td>
+      <td>Description</td>
+      <td>Example</td>
+      <td>Example output</td>
+    </tr>
+    <tr style="font-weight: bold;">
+      <td><span style="font-weight: normal;">b</span></td>
+      <td style="font-weight: normal;">Binary
+output field type (joins d, i, x, o, etc.).</td>
+      <td><span style="font-weight: normal;">sscanf(&quot;11111111&quot;, "%b", &amp;integer);</span></td>
+      <td><span style="font-weight: normal;">integer == 0xff </span></td>
+    </tr>
+    <tr>
+      <td>I8</td>
+      <td>8 bit integer field
+modifier.</td>
+      <td><span style="font-weight: normal;">sscanf</span>(&quot;0xff&quot;, "%I8d"<span style="font-weight: normal;">, &amp;int8</span>);</td>
+      <td><span style="font-weight: normal;"><span style="font-weight: normal;">int8</span> == <span style="font-weight: normal;">0xff </span></span></td>
+    </tr>
+    <tr>
+      <td>I16</td>
+      <td>16 bit integer field
+modifier.</td>
+      <td><span style="font-weight: normal;">sscanf</span>(&quot;0xffff&quot;, "%I16u"<span style="font-weight: normal;">, &amp;uint16</span>);</td>
+      <td><span style="font-weight: normal;"><span style="font-weight: normal;">uint16</span> == <span style="font-weight: normal;">0xffff</span></span></td>
+    </tr>
+    <tr>
+      <td>I32</td>
+      <td>32 bit integer field
+modifier.</td>
+      <td><span style="font-weight: normal;">sscanf</span>(&quot;0xffffffff&quot;, "%I32d"<span style="font-weight: normal;">, &amp;int32</span>);</td>
+      <td><span style="font-weight: normal;"><span style="font-weight: normal;">int32</span> == <span style="font-weight: normal;">0xffff</span></span>ffff</td>
+    </tr>
+    <tr>
+      <td>I64</td>
+      <td>64 bit integer field
+modifier.</td>
+      <td><span style="font-weight: normal;">sscanf</span>(&quot;0xffffffffffffffff&quot;, "%I64u"<span style="font-weight: normal;">, &amp;int64</span>);</td>
+      <td><span style="font-weight: normal;">int64</span> == -1 </td>
+    </tr>
+    <tr>
+      <td>I128</td>
+      <td>128 bit integer field
+modifier.</td>
+      <td><span style="font-weight: normal;">sscanf</span>(&quot;0xffffffffffffffffffffffffffffffff&quot;, "%I128d"<span style="font-weight: normal;">, &amp;int128</span>);</td>
+      <td><span style="font-weight: normal;">int128</span> = -1</td>
+    </tr>
+  </tbody>
+</table>
+<h2>Example usage</h2>
+<p>To do.<br>
+</p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body>
+</html>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 279 - 0
doc/EASprintf.html


BIN
doc/EAStdC Index.pdf


+ 81 - 0
doc/EAStdC.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAStdC</title>
+<link type="text/css" rel="stylesheet" href="UTFDoc.css">
+<meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAStdC</h1>
+<p>EAStdC is a package which implements a number of basic library facilities that are similar to those in the standard C library. This does not mean that its functionality is identical to the C standard library. It is an evolution and convergence of the rwstdc and UTFFoundation packages. </p>
+<p>The fundamental design characteristics of EAStdC are:</p>
+<ul>
+  <li>The package has minimal dependencies: EABase and EAAssert.</li>
+  <li>It consists of simple standalone functions and classes.</li>
+  <li>Memory is not allocated by any of the functions or classes, excepts possibly under pathological conditions.</li>
+  <li>There is no thread-safety. All thread safety must be coordinated at a higher level by the user. </li>
+  <li>It does not implement localization functionality. Localization functionality is left to specialized libraries for that purpose.</li>
+  <li>With few exceptions, both char8_t and char16_t string functionality are supported equivalently.</li>
+</ul>
+<h3> Should you use EAStdC functions that overlap with Standard C library functions?</h3>
+<p>A primary purpose of EAStdC is to provide a portable implementation of basic utility functions, many of which correspond the standard C library functions. Specifically: </p>
+<ul>
+  <li> Provide a standardizes portable header file to #include. </li>
+  <ul>
+    <li> e.g. C libraries don&rsquo;t use the same name for headers, such as malloc.h, sys/types.h, memory.h, etc. </li>
+  </ul>
+  <li> Provide standardized function names. </li>
+  <ul>
+    <li> e.g. EAStdC has Vsnprintf, as opposed to VC++ having _vsnprintf but GCC having vsnprintf. </li>
+  </ul>
+  <li> Provide standardized function implementations. </li>
+  <ul>
+    <li> e.g. Sprintf/Scanf work the same in EAStdC, unlike most C libraries. </li>
+  </ul>
+  <li> Provide faster versions. </li>
+  <ul>
+    <li> e.g. Faster strlen, memcpy, special fast memcpy versions. EAStdC sprintf is much faster than stdc. </li>
+  </ul>
+  <li> Provide versions that don&rsquo;t exist in compiler-provided libraries. </li>
+  <ul>
+    <li> e.g. EAStdC has vsnprintf, whereas some C libraries don&rsquo;t. </li>
+  </ul>
+  <li> Lower memory requirements. </li>
+  <ul>
+    <li> Some stdc functions tend to bring in a lot of object code, often because of their locale requirements. </li>
+  </ul>
+  <li> Provides consistent standardized encoding. </li>
+  <ul>
+    <li> EAStdC uses UTF8 for char8_t and UCS2 for char16_t, whereas C libraries aren&rsquo;t consistent. wchar_t could be 8, 16, or 32 bit (we&rsquo;ve seen each of these). </li>
+  </ul>
+  <li> Provide auxiliary or &ldquo;better&rdquo; versions of functions. </li>
+  <ul>
+    <li> e.g. EAStdC&rsquo;s Random, DateTime. </li>
+  </ul>
+  <li> EAStdC has additional functionality that doesn&rsquo;t directly match something from the Standard C library. </li>
+  <ul>
+    <li> e.g. Stopwatch, Hash, FixedPoint. </li>
+  </ul>
+</ul>
+<p> Primary downsides to EAStdC: </p>
+<ul>
+  <li> It doesn&rsquo;t have localization support. You can&rsquo;t call setlocale() with it and have it change how it interprets &ldquo;.&rdquo; And &ldquo;,&rdquo; in numbers. You need to use the EALocale or EAText package for that, though EALocale and EAText do a better job than stdc does. </li>
+  <li> EAStdC might have bugs that haven&rsquo;t been eradicated, while most stdc implementations are pretty good. </li>
+  <li> Stdc has better documentation, though the EAStdC functions that are stdc equivalents usually have the same specification. </li>
+</ul>
+<p> In the case of overlap between stdc and EAStdC, I think the policy of what to use depends on your team conventions and your project requirements. Some notes: </p>
+<ul>
+  <li> If you want to guarantee portability then you&rsquo;re better off using EAStdC. </li>
+  <li> Some functions (e.g. strlen and memcpy) are basic enough that the stdc versions are usually fine. </li>
+  <ul>
+    <li> Except EAStdC&rsquo;s Strlen is faster than most stdc versions, and a surprising number of memcpy implementations for uncommon platforms are slow. </li>
+  </ul>
+  <li> If you think your code will need to build outside our codebase then maybe you should try to stick with stdc. </li>
+</ul>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+</body>
+</html>

+ 289 - 0
doc/EAStopwatch.html

@@ -0,0 +1,289 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    
+<title>EARandom</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAStopwatch</h1>
+
+<h2>Introduction</h2>
+<p>EAStopwatch provides timing facilities for use in game development. It provides 
+  two C++ classes:</p>
+<blockquote>
+  <table width="100%" border="1">
+    <tr>
+      <td>Stopwatch</td>
+      <td>A general-purpose timer</td>
+    </tr>
+    <tr>
+      <td>LimitStopwatch</td>
+      <td>A special-purpose countdown-style timer</td>
+    </tr>
+  </table>
+</blockquote>
+<p>Stopwatch acts much like and classic hand-held stopwatch or like the stopwatch 
+  found on sports watches. It has additional flexibility, such as the ability 
+  to set an elapsed time to any value.</p>
+<p>LimitStopwatch is a stopwatch which simply tells you if a given amount of time 
+  has passed. You can accomplish this same functionality by polling a Stopwatch, 
+  but LimitStopwatch is more efficient.</p>
+<h2>Important things to remember</h2>
+<ul>
+  <li> You won't get very accurate timings if you use a millisecond stopwatch 
+    repeatedly to time tiny sections of code that take only nanoseconds.</li>
+  <li>You can start and stop a stopwatch repeatedly and it will do the right thing, 
+    which is sum up the times during which it was running.</li>
+  <li>Timing CPU cycles (clock ticks) accurately can be hard if you are trying 
+    to time very small pieces of code. Pipeline stalls and memory stalls can result 
+    in highly variable results.</li>
+  <li>A running stopwatch consumes no resources (CPU cycles nor memory) during 
+    its existence nor while it is running.</li>
+  <li>You don't have to stop a stopwatch that is running; it doesn't take up CPU 
+    time to be running. It is not an error to not stop a stopwatch.</li>
+  <li>There is a distinction between stopwatch cycles and CPU-based cycles. While 
+    it may be the case that the stopwatch uses a CPU cycle counter as its basis, 
+    this also may not be the case. In fact, using a CPU cycles counter as the 
+    basis for a stopwatch is often a dangerous thing to do because processors 
+    these days will sometimes switch frequencies on the fly. </li>
+  <li>You don't have to worry about multi-processor issues unless you are running 
+    on a desktop platform such as Windows. In the case of such platforms, you 
+    only need to worry about such issues if you happen to define EA_STOPWATCH_FORCE_CPU_CYCLE_USAGE = 1.</li>
+  <li>You can call safely GetElapsedTime while the stopwatch is running; it will 
+    simply return the currently elapsed time.</li>
+  <li>The construction and destruction instances of Stopwatch is fast; don't worry 
+    about it. Similarly, a Stopwatch instance is small in size.</li>
+</ul>
+<h2>Example usage </h2>
+<p>EAStopwatch is very easy to use, though curiously its simplicity actually confuses 
+  some users familiar with more cumbersome alternatives.</p>
+<p>Basic usage:</p>
+<pre class="code-example">Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+
+stopwatch.Start();
+DoSomethingThatYouWantToMeasure();
+printf("Time to do something: %u.\n", stopwatch.GetElapsedTime());</pre>
+Example of stopwatch reuse (via Restart):
+<pre class="code-example">Stopwatch stopwatch(Stopwatch::kUnitsCycles, true); // Note that 'true' tells the timer to auto-start here.
+
+DoSomethingSmall();
+printf("Time to do something small: %u.\n", stopwatch.GetElapsedTime());
+
+stopwatch.Restart();
+DoSomethingElseSmall();
+printf("Time to do something else small: %d.\n", stopwatch.GetElapsedTime());</pre>
+Example of SetElapsedTime usage.
+<pre class="code-example">Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+
+stopwatch.SetElapsedTime(100); // Give the stopwatch a &quot;100 ms head start.&quot;
+stopwatch.Start();
+printf("This should print out 100 (or maybe 101): %u.\n", stopwatch.GetElapsedTime());</pre>
+<p>Example of LimitStopwatch usage:</p>
+<pre class="code-example">LimitStopwatch limitStopwatch(Stopwatch::kUnitsMilliseconds, 1000, true);
+
+while(!limitStopwatch.IsTimeUp())
+    printf("waiting\n"); </pre>
+<h2>Don't do this!</h2>
+<p> It seems that quite often people unfamiliar with a C++ stopwatch tend to revert 
+  away from using the stopwatch as it was designed and try to do timings manually, 
+  like shown below. The code below is more complicated than it needs to be and 
+  is less precise than it can be, as the high internal resolution of the stopwatch 
+  is lost when using it like this. </p>
+<pre class="code-example"><font color="#CC0000">Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+
+stopwatch.Start();
+uint64_t timeStart = stopwatch.GetElapsedTime();
+DoSomething();
+uint64_t timeElapsed = stopwatch.GetElapsedTime() - time;</font>
+</pre>
+<h2>Interface </h2>
+<p>Stopwatch</p>
+<pre class="code-example">class Stopwatch
+{
+public:
+<span class="code-example-comment">    /// Units
+    /// Defines common timing units plus a user-definable set of units.
+</span>    enum Units
+    {
+        kUnitsCycles       =    0,  <font color="#999999">/// Stopwatch clock ticks. </font>
+        kUnitsCPUCycles    =    1,  <font color="#999999">/// CPU clock ticks (or similar equivalent for the platform).</font>
+        kUnitsNanoseconds  =    2,  <font color="#999999">/// Count in nanoseconds.</font>
+        kUnitsMicroseconds =    3,  <font color="#999999">/// Count in microseconds.</font>
+        kUnitsMilliseconds =    4,  <font color="#999999">/// Count in milliseconds.</font>
+        kUnitsSeconds      =    5,  <font color="#999999">/// Count in seconds.</font>
+        kUnitsMinutes      =    6,  <font color="#999999">/// Count in minutes.</font>
+        kUnitsUserDefined  = 1000   <font color="#999999">/// User defined units (animation frames, video vertical retrace, etc.).</font>
+    };
+
+public:
+<span class="code-example-comment">    /// Stopwatch
+    /// Constructor for Stopwatch, allows user to specify units. 
+    /// If units are kUnitsUserDefined,  you'll need to either subclass 
+    /// Stopwatch and implement GetUserDefinedStopwatchCycle or call 
+    /// SetUserDefinedUnitsToStopwatchCyclesRatio in order to allow it 
+    /// to know how to convert units.
+</span>    explicit Stopwatch(int nUnits = kUnitsCycles, bool bStartImmediately = false);
+
+<span class="code-example-comment">    /// Stopwatch
+    /// Copy constructor.
+</span>    Stopwatch(const Stopwatch& stopwatch);
+
+<span class="code-example-comment">    /// ~Stopwatch
+    /// Destructor.
+</span>    ~Stopwatch();
+
+<span class="code-example-comment">    /// operator=
+    /// Assignment operator.
+</span>    Stopwatch& operator=(const Stopwatch& stopwatch);
+
+<span class="code-example-comment">    /// GetUnits
+    /// Gets the current units. Returns one of enum Units or kUnitsUserDefined+.
+</span>    int GetUnits() const;
+
+<span class="code-example-comment">    /// SetUnits
+    /// Sets the current units. One of enum Units or kUnitsUserDefined+.
+</span>    void SetUnits(int nUnits);
+
+<span class="code-example-comment">    /// Start
+    /// Starts the stopwatch. Continues where it was last stopped. 
+    /// Does nothing if the stopwatch is already started.
+</span>    void Start();
+
+<span class="code-example-comment">    /// Stop
+    /// Stops the stopwatch it it was running and retaines the elasped time.
+</span>    void Stop();
+
+<span class="code-example-comment">    /// Reset
+    /// Stops the stopwatch if it was running and clears the elapsed time.
+</span>    void Reset();
+
+<span class="code-example-comment">    /// Restart
+    /// Clears the elapsed time and starts the stopwatch if it was not 
+    /// already running. Has the same effect as Reset(), Start().
+</span>    void Restart();
+
+<span class="code-example-comment">    /// IsRunning
+    /// Returns true if the stopwatch is running.
+</span>    bool IsRunning() const;
+
+<span class="code-example-comment">    /// GetElapsedTime
+    /// Gets the elapsed time, which properly takes into account any 
+    /// intervening stops and starts. Works properly whether the stopwatch 
+    /// is running or not.
+</span>    uint64_t GetElapsedTime() const;
+
+<span class="code-example-comment">    /// SetElapsedTime
+    /// Allows you to set the elapsed time. Erases whatever is current. 
+    /// Works properly whether the stopwatch is running or not.
+</span>    void SetElapsedTime(uint64_t nElapsedTime);
+
+<span class="code-example-comment">    /// GetElapsedTimeFloat
+    /// Float version, which is useful for counting fractions of 
+    /// seconds or possibly milliseconds.
+</span>    float GetElapsedTimeFloat() const;
+
+<span class="code-example-comment">    /// SetElapsedTimeFloat
+    /// Allows you to set the elapsed time. Erases whatever is current. 
+    /// Works properly whether the stopwatch is running or not.
+</span>    void SetElapsedTimeFloat(float fElapsedTime);
+
+<span class="code-example-comment">    /// SetCyclesPerUnit
+    /// Allows the user to manually override the frequency of the 
+    /// timer. 
+</span>    void SetCyclesPerUnit(float fCyclesPerUnit);
+
+<span class="code-example-comment">    /// GetCyclesPerUnit
+    /// Returns the value number of cycles per unit. If the user 
+    /// set a manual value via SetCyclesPerUnit, this function returns 
+    /// that value.
+</span>    float GetCyclesPerUnit() const;
+
+<span class="code-example-comment">    /// GetStopwatchCycle
+    /// Gets the current stopwatch cycle on the current machine.
+    /// Note that a stopwatch cyle may or may not be the same thing
+    /// as a CPU cycle. We provide the distinction between stopwatch
+    /// cycles and CPU cycles in order to accomodate platforms (e.g.
+    /// desktop platforms) in which CPU cycle counting is unreliable.
+</span>    static uint64_t GetStopwatchCycle();
+
+<span class="code-example-comment">    /// GetStopwatchFrequency
+    /// Note that the stopwatch freqency may or may not be the same thing
+    /// as the CPU freqency. We provide the distinction between stopwatch
+    /// cycles and CPU cycles in order to accomodate platforms (e.g.
+    /// desktop platforms) in which CPU cycle counting is unreliable.
+</span>    static uint64_t GetStopwatchFrequency();
+
+<span class="code-example-comment">    /// GetUnitsPerStopwatchCycle
+    /// Returns the number of stopwatch cycles per the given unit.  
+    /// If the unit is seconds, the return value would be the frequency of 
+    /// the stopwatch timer and thus be the same value as returned by
+    /// GetStopwatchFrequency().
+</span>    static float GetUnitsPerStopwatchCycle(Units units);
+
+<span class="code-example-comment">    /// GetCPUCycle
+    /// Gets the current CPU-based timer cycle on the current processor 
+    /// (if in a multiprocessor system). Note that this doesn't necessarily
+    /// get the actual machine CPU clock cycle; rather it returns the 
+    /// CPU-based timer cycle. One some platforms the CPU-based timer is
+    /// a 1:1 relation to the CPU clock, while on others it is some multiple
+    /// of it.
+</span>    static uint64_t GetCPUCycle();
+
+<span class="code-example-comment">    /// GetCPUFrequency
+    /// Gets the frequency of the CPU-based timer. Note that this doesn't 
+    /// necessarily get the actual machine CPU clock frequency; rather it returns  
+    /// the CPU-based timer frequency. One some platforms the CPU-based timer is
+    /// a 1:1 relation to the CPU clock, while on others it is some multiple of it.
+</span>    static uint64_t GetCPUFrequency(); 
+
+<span class="code-example-comment">    /// GetUnitsPerCPUCycle
+    /// Returns the number of CPU cycles per the given unit.  
+    /// If the unit is seconds, the return value would be the frequency 
+    /// of the CPU-based timer.
+</span>    static float GetUnitsPerCPUCycle(Units units);
+};
+  </pre>
+<p>LimitStopwatch</p>
+<pre class="code-example">class LimitStopwatch : public Stopwatch
+{
+public:
+<span class="code-example-comment">    /// LimitStopwatch
+    /// Constructor
+</span>    LimitStopwatch(int nUnits = kUnitsCycles, uint32_t nLimit = 0, bool bStartImmediately = false);
+
+<span class="code-example-comment">    /// SetTimeLimit
+    /// Sets the time limit and lets you start the stopwatch at the same time.
+</span>    void SetTimeLimit(uint32_t nLimit, bool bStartImmediately = false); 
+
+<span class="code-example-comment">    /// IsTimeUp
+    /// Returns true if the limit has been reached. Highly efficient.
+</span>    bool IsTimeUp();
+
+<span class="code-example-comment">    /// GetTimeRemaining
+    /// More expensive than IsTimeUp, as it returns a value.
+</span>    int64_t GetTimeRemaining();
+
+<span class="code-example-comment">    /// GetTimeRemainingFloat
+    /// More expensive than IsTimeUp, as it returns a value.
+</span>    float GetTimeRemainingFloat();
+};</pre>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 298 - 0
doc/EAString.html

@@ -0,0 +1,298 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EAString</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EAString</h1>
+
+<h2>Introduction</h2>
+<p>EAString implements an extensive suite of C string functionality from &lt;string.h&gt; in a portable way. Much of this functionality is the same as what 
+    is present in the C standard and in common (but non-portable) extensions to the C standard. EAString presents a consistent portable interface to its functionality 
+    in both 8 bit and 16 bit string form.</p>
+<p>The C language provides various functions which work with non-portable data types, such as long. EAString defines all significant data types in terms of portable 
+    types such as int32_t and int64_t. Additionally, the EAString versions of functions are usually faster than the C runtime library versions, usually because the EAString versions minimize cache misses 
+    and branching, whereas typical C runtime libraries optimize for the smallest memory footprint with little or no regard to other performance characteristics. Also, the EAString functions do not implement locale-specific functionality and thus save time and space as a result. Other packages within EA implement localization functionality in a way that is better suited to high performance game development.</p>
+<p>C-style printf functionality is separate from EAString and is found in <a href="EASprintf.html">EASprintf</a>. Memcpy functionality (which comes from the  &lt;string.h&gt; 
+    header file) is present in <a href="EAMemory.html">EAMemory</a>. </p>
+<h2>Extension functions</h2>
+<p> EAString provides extended functionality that isn't found in conventional C libraries, but it useful nevertheless.</p>
+<table width="100%" border="1" cellpadding="3">
+    <tr> 
+        <td><b>Function</b></td>
+        <td><b>Description</b></td>
+        <td><b>Signature</b></td>
+    </tr>
+    <tr> 
+        <td>Strlcat</td>
+        <td>Safe variation of strncat</td>
+        <td><font size="-1">char_t* Strlcat(char_t* pDestination, const char_t* pSource, size_t n); </font></td>
+    </tr>
+    <tr> 
+        <td>Strlcpy</td>
+        <td>Safe variation of strcpy</td>
+        <td><p><font size="-1">char_t* Strlcpy(char_t* pDestination, const char_t* pSource, size_t n); <br>
+        int Strlcpy(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char32_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char8_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char8_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0); <br>
+        int Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0); <br>
+        int Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        int Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0); <br>
+        int Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);<br>
+        bool Strlcpy(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char32_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char8_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char8_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);<br>
+        bool Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+        </font></p>
+        </td>
+    </tr>
+    <tr> 
+        <td>Stristr</td>
+        <td>Case insensitive version of strstr (find string within string)</td>
+        <td><font size="-1">char_t* Stristr(const char_t* pString, const char_t* pSubString);</font></td>
+    </tr>
+    <tr> 
+        <td>FtoaEnglish</td>
+        <td> 
+            <p>Float to ascii; always use English numeric format, regardless of the current locale.</p>
+        </td>
+        <td><font size="-1">char_t* FtoaEnglish(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled); </font></td>
+    </tr>
+    <tr> 
+        <td>AtofEnglish</td>
+        <td>Ascii to float; always use English numeric format, regardless of the current locale.</td>
+        <td><font size="-1">double AtofEnglish(const char_t* pString); </font></td>
+    </tr>
+    <tr> 
+        <td> StrtodEnglish</td>
+        <td>Same as strtod, but always use English numeric format, regardless of the current locale.</td>
+        <td><font size="-1">double StrtodEnglish(const char_t* pString, char_t** ppStringEnd);</font></td>
+    </tr>
+    <tr> 
+        <td>Memset16</td>
+        <td>Sets 16 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1">uint16_t* Memset16(void* pDestination, uint16_t c, size_t count); </font></td>
+    </tr>
+    <tr> 
+        <td>Memset32</td>
+        <td>Sets 32 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1"> uint32_t* Memset32(void* pDestination, uint32_t c, size_t count);</font></td>
+    </tr>
+    <tr> 
+        <td>Memset64</td>
+        <td>Sets 64 bit values in memory (as opposed to memset's 8 bit values)</td>
+        <td><font size="-1">uint64_t* Memset64(void* pDestination, uint64_t c, size_t count);</font></td>
+    </tr>
+    <tr> 
+        <td>MemsetN</td>
+        <td>Sets arbitrarily sized values in memory.</td>
+        <td><font size="-1">void* MemsetN (void* pDestination, const void* pSource, size_t sourceBytes, size_t count); </font></td>
+    </tr>
+    <tr> 
+        <td>EcvtBuf</td>
+        <td>Base function for converting a float to a %e string.</td>
+        <td> 
+            <p><font size="-1">char_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char_t* buffer); </font></p>
+        </td>
+    </tr>
+    <tr> 
+        <td>FcvtBuf</td>
+        <td>Base function for converting a float to a %f string.</td>
+        <td><font size="-1">char_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char_t* buffer); </font></td>
+    </tr>
+</table>
+<h2>Example usage </h2>
+<p>We provide a random smattering of example code here.</p>
+<pre class="code-example">char16_t buffer[32] = L"hello ";
+
+Strcat(buffer, L"world");
+</pre>
+<p>Strlcpy, Strlcat:</p>
+
+<pre class="code-example">char8_t buffer[32];
+
+
+Strlcpy(buffer, "Hello ", sizeof(buffer));<br>Strlcat(buffer, "world", sizeof(buffer));
+</pre>
+<p>Strlcpy, used to translate UTF8 to UTF16: </p>
+<pre class="code-example">char8_t  buffer8[64];
+char16_t buffer16[64];
+
+
+int length16 = Strlcpy(buffer16, buffer8, 64);</pre>
+<p>U64toa:</p>
+<pre class="code-example">char buffer[32];
+U64toa(UINT64_C(12345678901234567890), buffer, 16);
+</pre>
+<p>AtoI32:</p>
+
+<pre class="code-example">int32_t x = AtoI32(&quot;1234567890&quot;);
+</pre>
+<p>Strtod:</p>
+<pre class="code-example">const char16_t* pString = L"12345.678 123.456 1.23456";
+
+while(*pString)
+{
+    double value = Strtod(pString, &pString);
+    printf("Field = %f\n, value);
+}
+</pre>
+<h2>Interface</h2>
+<p>In each of the functions below, there is an char8_t and char16_t version. So for Strcat there are the following:</p>
+<blockquote>
+  <p><span class="code-example-span">char8_t*       &nbsp;Strcat(char8_t* &nbsp;pDestination, const char8_t* &nbsp;pSource)<br>
+  </span><span class="code-example-span">char16_t* Strcat(char16_t* pDestination, const char16_t* pSource)</span></p>
+</blockquote>
+<p>The list below may be out of date as you read this, so see EAString.h for a definitive list of supported functionality.</p>
+<pre class="code-example">char_t*       Strcat(char_t* pDestination, const char_t* pSource);
+char_t*       Strncat(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strlcat(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strcpy(char_t* pDestination, const char_t* pSource);
+char_t*       Strncpy(char_t* pDestination, const char_t* pSource, size_t n);
+char_t*       Strlcpy(char_t* pDestination, const char_t* pSource, size_t n);
+int           Strlcpy(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char32_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char8_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char8_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+int           Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+bool          Strlcpy(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char32_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char8_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char8_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed); 
+bool          Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed); 
+bool          Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed);
+bool          Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t&amp; nDestUsed, size_t&amp; nSourceUsed); 
+size_t        Strlen(const char_t* pString);
+size_t        Strxfrm(char_t* pDest, const char_t* pSource, size_t n);
+char_t*       Strdup(const char_t* pString);
+char_t*       Strlwr(char_t* pString);
+char_t*       Strupr(char_t* pString);
+char_t*       Strchr(const char_t* pString, char_t c);
+size_t        Strcspn(const char_t* pString1, const char_t* pString2);
+char_t*       Strpbrk(const char_t* pString1, const char_t* pString2);
+char_t*       Strrchr(const char_t* pString, char_t c);
+size_t        Strspn(const char_t* pString, const char_t* pSubString);
+char_t*       Strstr(const char_t* pString, const char_t* pSubString);
+char_t*       Stristr(const char_t* pString, const char_t* pSubString);
+char_t*       Strtok(char_t* pString, const char_t*  pTokens, char_t** pContext);
+char_t*       Strset(char_t* pString, char_t c);
+char_t*       Strnset(char_t* pString, char_t c, size_t n);
+char_t*       Strrev(char_t* pString);
+int           Strcmp(const char_t* pString1, const char_t* pString2);
+int           Strncmp(const char_t* pString1, const char_t* pString2, size_t n);
+int           Stricmp(const char_t*  pString1, const char_t* pString2);
+int           Strnicmp(const char_t* pString1, const char_t* pString2, size_t n);
+int           Strcoll(const char_t*  pString1, const char_t* pString2);
+int           Strncoll(const char_t* pString1, const char_t* pString2, size_t n);
+int           Stricoll(const char_t* pString1, const char_t* pString2);
+int           Strnicoll(const char_t* pString1, const char_t* pString1, size_t n);
+
+char_t*       EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char_t* buffer);
+char_t*       FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char_t* buffer);
+
+char_t*       I32toa(int32_t nValue, char_t* pResult, int nBase);
+char_t*       U32toa(uint32_t nValue, char_t* pResult, int nBase);
+char_t*       I64toa(int64_t nValue, char_t* pBuffer, int nBase);
+char_t*       U64toa(uint64_t nValue, char_t* pBuffer, int nBase);
+double        Strtod(const char_t* pString, char_t** ppStringEnd);
+double        StrtodEnglish(const char_t* pString, char_t** ppStringEnd);
+int64_t       StrtoI64(const char_t* pString, char_t** ppStringEnd, int nBase);
+uint64_t      StrtoU64(const char_t* pString, char_t** ppStringEnd, int nBase);
+int32_t       StrtoI32(const char_t* pString, char_t** ppStringEnd, int nBase);
+uint32_t      StrtoU32(const char_t* pString, char_t** ppStringEnd, int nBase);
+int32_t       AtoI32(const char_t* pString);
+uint32_t      AtoU32(const char_t* pString);
+int64_t       AtoI64(const char_t* pString);
+uint64_t      AtoU64(const char_t* pString);
+double        Atof(const char_t* pString);
+double        AtofEnglish(const char_t* pString);
+char_t*       Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+char_t*       FtoaEnglish(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+</pre>
+<h2>Encoded Strlcpy Issue </h2>
+<p>The Strlcpy function <span class="code-example-comment"> size_t Strlcpy(char16_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength)</span> converts UTF8 text (char8_t) to UCS2  (char16_t). It doesn't work if your pSource string isn't truly UTF8-encoded. Typically this would happen because the source string is using Windows code page 1252 or HTML's ISO 8859-1. All 8 bit strings in EAStdC are assumed to be UTF8 unless specifically documented otherwise. </p>
+<p>Any 8 bit text that's not UTF8 has to be defined within the context of a code page; otherwise any chars above 127 have an arbitrary meaning, and may include multi-byte characters for some code pages. If you need to convert code page 1252 to UCS2, you need to implement a function which uses a  table that maps the high byte values into their proper Unicode values. It's not so simple to just cast the byte value to char16_t, as some of the code page 1252 characters don't map directly to their numerical values in Unicode, though ISO 8859-1 characters to completely map to their equivalent Unicode values. </p>
+<p>The following is an implementation of Strcpy and Strlcpy for ISO 8859-1 char8_t text which isn't currently part of EAStdC: </p>
+<p class="code-example">char16_t* StrcpyISO8859_1(char16_t* pDestination, const char8_t* pSource)<br>
+{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;char16_t* pDestSaved = pDestination;<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;while(*pSource)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(pDestination++) = (uint8_t)*pSource++;<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;*pDestination = 0;<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;return pDestSaved;<br>
+}</p>
+<p> </p>
+<p class="code-example">size_t StrlcpyISO8859_1(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity)<br>
+{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;const char8_t* s = pSource;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;size_t n = nDestCapacity;<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;if(n &amp;&amp; --n)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((*pDestination++ = (uint8_t)*s++) == 0)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} while(--n);<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;if(!n)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;{<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(nDestCapacity)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*pDestination = 0;<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(*s)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++s;<br>
+&nbsp;&nbsp;&nbsp;&nbsp;}<br>
+<br>
+&nbsp;&nbsp;&nbsp;&nbsp;return (s - pSource - 1);<br>
+}</p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body>
+</html>
+
+
+

+ 724 - 0
doc/EATextUtil.html

@@ -0,0 +1,724 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<Title>EATextUtil</title>
+
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>EATextUtil</h1>
+
+<h2>Introduction</h2>
+<p>EATextUtil is a collection of string utilities that are of a higher-level nature than those found in the C runtime library or our <a href="EAString.html">EAString</a> 
+    module. EASTL/string.h and EASTL/algorithm.h contain C++/STL variations of functions that are similar to the C runtime library functions, but which are generally 
+    more powerful and flexible than the C functions while usually being more efficient. </p>
+<p>All functions in EATextUtil are present in char8_t versions and char16_t versions, and assume UTF8 and UCS2 Unicode encoding respectively. Recall that these encodings 
+    are backward-compatible with ASCII and so will work for most or all of the text that you give them. </p>
+<p>Each of the functions comes in a version that doesn't allocate any memory but instead uses user-supplied memory in-place. In a few cases, there are String versions 
+    that will allocate memory if they need to increase the size of the user-supplied string.</p>
+<p>Here's a brief summary of the functions currently found in EATextUtil. We use char_t in the declarations below to refer to both char8_t and char16_t; there are 
+    thus two versions of each function. </p>
+<pre class="code-example">bool WildcardMatch(const char_t* pString, const char_t* pPattern, bool bCaseSensitive);
+ 
+bool ParseDelimitedText(const char_t*  pText,  const char_t*  pTextEnd,  char_t cDelimiter, 
+                        const char_t*& pToken, const char_t*& pTokenEnd, const char_t** ppNewText);
+  
+const char_t* GetTextLine(const char_t* pText, const char_t* pTextEnd, const char_t** ppNewText);
+ 
+bool SplitTokenDelimited(const char_t* pSource, size_t nSourceLength, char_t cDelimiter, 
+                         char_t* pToken, size_t nTokenLength, const char_t** ppNewSource = NULL);
+bool SplitTokenSeparated(const char_t* pSource, size_t nSourceLength, char_t cDelimiter, 
+                         char_t* pToken, size_t nTokenLength, const char_t** ppNewSource = NULL);
+
+void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char_t* pASCIIArray);
+bool ConvertASCIIArrayToBinaryData(const char_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+ 
+int BoyerMooreSearch(const char8_t* pPattern, int nPatternLength, const char8_t* pSearchString, int nSearchStringLength, 
+                     int* pPatternBuffer1, int* pPatternBuffer2, int* pAlphabetBuffer, int nAlphabetBufferSize);
+</pre>
+<p>We will proceed to address each of the above functions.</p>
+<h2>WildcardMatch</h2>
+<pre class="code-example">bool WildcardMatch(const char16_t* pString, const char16_t* pPattern, bool bCaseSensitive);
+bool WildcardMatch(const char8_t*  pString, const char8_t*  pPattern, bool bCaseSensitive);
+</pre>
+<p>These functions match source strings to wildcard patterns like those used in file specifications. '*' in the pattern means match zero or more consecutive source 
+    characters. '?' in the pattern means match exactly one source character. Multiple * and ? characters may be used. Two consecutive * characters are treated as 
+    if they were one. Here are some examples:</p>
+<table width="500" border="1" hspace="32">
+    <tr> 
+        <td><b>Source</b></td>
+        <td><b>Pattern</b></td>
+        <td><b>Result</b></td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>*e</td>
+        <td>true</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>*f</td>
+        <td>false</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>???de</td>
+        <td>true</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>????g</td>
+        <td>false</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>*c??</td>
+        <td>true</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>*e??</td>
+        <td>false</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>*????</td>
+        <td>true</td>
+    </tr>
+    <tr> 
+        <td>abcde</td>
+        <td>bcdef</td>
+        <td>false</td>
+    </tr>
+    <tr>
+        <td>abcde</td>
+        <td> *?????</td>
+        <td>true</td>
+    </tr>
+</table>
+<p>Example usage:</p>
+<pre class="code-example">bool result = WildcardMatch(&quot;Hello world&quot;, &quot;hello*&quot;, false);   <font color="#0033CC">    // result becomes true</font>
+bool result = WildcardMatch(&quot;Hello world&quot;, &quot;hello?&quot;, false);       <font color="#0033CC">// result becomes false</font>
+bool result = WildcardMatch(&quot;Hello world&quot;, &quot;Hello??orld&quot;, true);   <font color="#0033CC">// result becomes true</font>
+bool result = WildcardMatch(&quot;Hello world&quot;, &quot;*Hello*world*&quot;, true); <font color="#0033CC">// result becomes true</font>
+</pre>
+<h2>ParseDelimitedText (iterative version)</h2>
+<pre class="code-example">bool ParseDelimitedText(const char16_t* pText, const char16_t* pTextEnd, char16_t cDelimiter, 
+                        const char16_t*& pToken, const char16_t*& pTokenEnd, const char16_t** ppNewText);
+
+bool ParseDelimitedText(const char8_t* pText, const char8_t* pTextEnd, char8_t cDelimiter, 
+                        const char8_t*& pToken, const char8_t*& pTokenEnd, const char8_t** ppNewText);
+</pre>
+<p>Given a line of text (e.g. like this:)</p>
+<pre class="code-example">342.5, &quot;This is a string&quot;, test, &quot;This is a string, with a comma&quot;</pre>
+<p>ParseDelimitedText parses it into separate fields (e.g. like this:)</p>
+<pre class="code-example">342.5
+This is a string
+test
+This is a string, with a comma </pre>
+<p> ParseDelimitedText lets you dynamically specify the delimiter. The delimiter can be any char (e.g. space, tab, semicolon) except the quote char itself, which 
+    is reserved for the purpose of grouping. See the source code comments for more details. However, in the case of text that is UTF8-encoded, you need to make sure 
+    the delimiter char is a value that is less than 127, so as not to collide with UTF8 encoded chars.<br>
+    <br>
+    The input is a pointer to text and the text length. For ASCII, MBCS, and UTF8, this is the number of bytes or chars. For UTF16 (Unicode) it is the number of characters. 
+    There are two bytes (two chars) per character in UTF16. The input nTextLength can be -1 (kLengthNull) to indicate that the string is null-terminated. </p>
+<p>Example behaviour for string array version (which you can extrapolate to the iterative version):</p>
+<table width="100%" border="1">
+    <tr> 
+        <td><b>Input string</b></td>
+        <td><b>MaxResults</b></td>
+        <td><b>Delimiter</b></td>
+        <td><b>Return value</b></td>
+        <td>
+            <p><b>O</b><b>utput array size</b></p>
+      </td>
+        <td><b>Output array value</b></td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">""</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">' '</font></pre>
+        </td>
+        <td><font size="-1">0</font></td>
+        <td><font size="-1">0</font></td>
+        <td> 
+            <pre><font size="-1">""</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"000 111"</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">' '</font></pre>
+        </td>
+        <td><font size="-1">2</font></td>
+        <td><font size="-1">2</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"000 111   222   333 444 \"555 555\" 666"</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">' '</font></pre>
+        </td>
+        <td><font size="-1">7</font></td>
+        <td><font size="-1">7</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"  "222"  "333"  "444"  "555 555"  "666"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"     000 111 222         333                "</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">' '</font></pre>
+        </td>
+        <td><font size="-1">4</font></td>
+        <td><font size="-1">4</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"  "222"  "333"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"     000 111 222         333                "</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">' '</font></pre>
+        </td>
+        <td><font size="-1">2</font></td>
+        <td><font size="-1">2</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">""</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">','</font></pre>
+        </td>
+        <td><font size="-1">0</font></td>
+        <td><font size="-1">0</font></td>
+        <td> 
+            <pre><font size="-1">""</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"000,111"</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">','</font></pre>
+        </td>
+        <td><font size="-1">2</font></td>
+        <td><font size="-1">2</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"000,  111 , 222   333 ,444 \"555,  555  \" 666"</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">','</font></pre>
+        </td>
+        <td><font size="-1">4</font></td>
+        <td><font size="-1">4</font></td>
+        <td> 
+            <pre><font size="-1">"000"  "111"  "222   333"  "444 \"555,  555  \" 666"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"  ,, 000 ,111, 222,         333          ,     "</font></pre>
+        </td>
+        <td><font size="-1">-1</font></td>
+        <td> 
+            <pre><font size="-1">','</font></pre>
+        </td>
+        <td><font size="-1">6</font></td>
+        <td><font size="-1">6</font></td>
+        <td> 
+            <pre><font size="-1">""   ""   "000"   "111"   "222"   "333"</font></pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre><font size="-1">"  ,, 000 ,111, 222,         333          ,     "</font></pre>
+        </td>
+        <td><font size="-1">2 </font></td>
+        <td> 
+            <pre><font size="-1">','</font></pre>
+        </td>
+        <td><font size="-1">0</font></td>
+        <td><font size="-1">0</font></td>
+        <td> 
+            <pre><font size="-1">""   ""</font></pre>
+        </td>
+    </tr>
+</table>
+<h2>Convert binary ASCII</h2>
+<pre class="code-example">void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char16_t* pASCIIArray);
+void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char8_t*  pASCIIArray);
+ 
+bool ConvertASCIIArrayToBinaryData(const char8_t*  pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+bool ConvertASCIIArrayToBinaryData(const char16_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+</pre>
+<p> The first two functions convert an array of binary characters into an encoded ASCII format that can be later converted back to binary. You might want to do this 
+    if you are trying to embed binary data into a text file (e.g. .ini file) and need a way to encode the binary data as text.</p>
+<p> The second two functions take an ASCII string of text and converts it to binary data. This is the reverse of the ConvertBinaryDataToASCIIArray functions. If an 
+    invalid hexidecimal character is encountered, it is replaced with a '0' character. These functions return true if the input was entirely valid hexadecimal data.</p>
+<p>Example usage:</p>
+<pre class="code-example">const uint8_t data[4] = { 0x12, 0x34, 0x56, 0x78 };
+char8_t ascii[8];
+ 
+ConvertBinaryDataToASCIIArray(data, 4 * sizeof(uint8_t), ascii);    <font color="#0033CC">// ascii becomes "12345678"</font>
+</pre>
+<pre class="code-example">const char16_t ascii[8] = &quot;12345678&quot;;
+uint8_t data[4];
+ 
+ConvertASCIIArrayToBinaryData(ascii, 8, data);    <font color="#0033CC">// data becomes { 0x12, 0x34, 0x56, 0x78 }</font>
+</pre>
+<h2>GetTextLine</h2>
+<pre class="code-example">const char16_t* GetTextLine(const char16_t* pText, const char16_t* pTextEnd, const char16_t** ppNewText);
+const char8_t*  GetTextLine(const char8_t*  pText, const char8_t*  pTextEnd, const char8_t**  ppNewText);
+</pre>
+<p>Given a block of text, this function reads a line of text and moves to the beginning of the next line. The return value is the end of the current line, previous 
+    to the newline characters. If ppNewText is supplied, it holds the start of the new line, which will often be different from the return value, as the start of 
+    the new line is after any newline characters. The length of the current line is pTextEnd - pText.</p>
+<p>These functions are useful for reading lines of text from a text file via an iterative method, which is perhaps the most flexible way of doing this.</p>
+<p>Example usage:</p>
+<pre class="code-example">char  buffer[256];<br>char* pLineNext(buffer);<br>char* pLine;<br><br>do{<br>    pText = pLineNext;<br>    const char* pLineEnd = GetTextLine(pLine, buffer + 256, &amp;pLineNext);<br>    <span class="code-example-comment">// Use pLine - pLineEnd here</span><br>}while(pLineNext != (buffer + 256));</pre>
+<h2>SplitTokenDelimited </h2>
+<pre class="code-example">bool SplitTokenDelimited(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, 
+                         char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+
+
+bool SplitTokenDelimited(const char8_t* pSource, size_t nSourceLength, char8_t cDelimiter, 
+                         char8_t* pToken, size_t nTokenLength, const char8_t** ppNewSource = NULL);
+</pre>
+<p> SplitTokenDelimited returns tokens that are delimited by a single character -- repetitions of that character will result in empty tokens returned. This is most 
+    commonly useful when you want to parse a string of text delimited by commas or spaces. Returns true whenever it extracts a token. Note however that the extracted 
+    token may be empty. Note that the return value will be true if the source has length and will be false if the source is empty. If the input pToken is non-null, 
+    the text before the delimiter is copied to it. </p>
+<p>Example behaviour (delimiter is a comma):</p>
+<table width="500" border="1" hspace="32">
+    <tr> 
+        <td><b>Source</b></td>
+        <td><b>Token</b></td>
+        <td><b>New source</b></td>
+        <td><b>Return value</b></td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a,b"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" a , b "</pre>
+        </td>
+        <td> 
+            <pre>" a "</pre>
+        </td>
+        <td> 
+            <pre>" b "</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"ab,b"</pre>
+        </td>
+        <td> 
+            <pre>"ab"</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>",a,b"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>"a,b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>",b"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>",,b"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>",b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>",a,"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>"a,"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a,"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>","</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>", "</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>&quot; &quot;</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" "</pre>
+        </td>
+        <td> 
+            <pre>&quot; &quot;</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>&quot;&quot;</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>false</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>NULL</pre>
+        </td>
+        <td> 
+            <pre>&quot;&quot;</pre>
+        </td>
+        <td> 
+            <pre>NULL</pre>
+        </td>
+        <td> 
+            <pre>false</pre>
+        </td>
+    </tr>
+</table>
+<p>Example usage:</p>
+<pre class="code-example">const char16_t* pString = L"a, b, c, d";
+char16_t pToken[16];
+
+while(SplitTokenDelimited(pString, kLengthNull, ',', pToken, 16, &pString))
+    printf("%s\n", pToken);
+</pre>
+<h2>SplitTokenSeparated </h2>
+<pre class="code-example">bool SplitTokenSeparated(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, 
+                         char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+
+
+bool SplitTokenSeparated(const char8_t* pSource, size_t nSourceLength, char8_t cDelimiter, 
+                         char8_t* pToken, size_t nTokenLength, const char8_t** ppNewSource = NULL);</pre>
+<p>SplitTokenSeparated returns tokens that are separated by one or more instances of a character. Returns true whenever it extracts a token. </p>
+<p>Example behaviour (delimiter is a space char):</p>
+<table width="500" border="1" hspace="32">
+    <tr> 
+        <td><b>Source</b></td>
+        <td><b>Token</b></td>
+        <td><b>New source</b></td>
+        <td><b>Return value</b></td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a b"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>"a  b"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" a b"</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"b"</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" a b "</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>"b "</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" a "</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" a  "</pre>
+        </td>
+        <td> 
+            <pre>"a"</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>true</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>&quot;&quot;</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>false</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>" "</pre>
+        </td>
+        <td> 
+            <pre>&quot;&quot;</pre>
+        </td>
+        <td> 
+            <pre>""</pre>
+        </td>
+        <td> 
+            <pre>false</pre>
+        </td>
+    </tr>
+    <tr> 
+        <td> 
+            <pre>NULL</pre>
+        </td>
+        <td> 
+            <pre>&quot;&quot;</pre>
+        </td>
+        <td> 
+            <pre>NULL</pre>
+        </td>
+        <td> 
+            <pre>false</pre>
+        </td>
+    </tr>
+</table>
+<h2>BoyerMooreSearch</h2>
+<pre class="code-example"><span class="code-example-comment">/// BoyerMooreSearch
+///
+/// patternBuffer1 is a user-supplied buffer and must be at least as long as the search pattern.
+/// patternBuffer2 is a user-supplied buffer and must be at least as long as the search pattern.
+/// alphabetBuffer is a user-supplied buffer and must be at least as long as the highest character value 
+/// used in the searched string and search pattern.
+///
+</span>int BoyerMooreSearch(const char* pPattern, int nPatternLength, const char* pSearchString, int nSearchStringLength, 
+                     int* pPatternBuffer1, int* pPatternBuffer2, int* pAlphabetBuffer, int nAlphabetBufferSize)
+</pre>
+<p> Boyer-Moore is a very fast string search compared to most others, including those in the STL. However, you need to be searching a string of at least 100 chars 
+    and have a search pattern of at least 3 characters for the speed to show, as Boyer-Moore has a startup precalculation that costs some cycles. This startup precalculation 
+    is proportional to the size of your search pattern and the size of the alphabet in use. Thus, doing Boyer-Moore searches on the entire Unicode alphabet is going 
+    to incur a fairly expensive precalculation cost.</p>
+<hr>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+<p> </p>
+</body></html>
+
+
+

+ 219 - 0
doc/Int128_t.html

@@ -0,0 +1,219 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+    <title>Int128_t</title>
+    <link type="text/css" rel="stylesheet" href="UTFDoc.css">
+    <meta name="author" content="Paul Pedriana">
+</head>
+<body bgcolor="#FFFFFF">
+<h1>int128_t</h1>
+
+<h2>Introduction</h2>
+<p>The int128_t module implements the int128_t and uint128_t integer data types 
+  via C++ classes. These types are largely identical in behavior to how a compiler's 
+  built-in int128_t / uint128_t would be. These classes exist because compilers 
+  don't provide true 128 bit data types. The most you'll typically see from a 
+  compiler is int64_t / uint64_t. </p>
+<p>128 bit integers are useful for things such as the following:</p>
+<ul>
+  <li>Storing very large numbers or bitfields. This can be useful when attempting 
+    to simulate city or world economies, for example.</li>
+  <li>Implementing 64 * 64 bit multiplication without losing data.</li>
+  <li>Implementing very precise fixed point mathematics.</li>
+</ul>
+<h2> Example usage </h2>
+<p>For the most part, you can use int128_t the same way you would use int64_t. 
+</p>
+<p>Mixed integer math expressions:</p>
+<pre class="code-example">int            a(17);
+short          b(26);
+int128_t       c(45);
+int64_t        d(77);
+unsigned short e(25);
+uint128_t      x(13);
+uint128_t      y;
+     
+y = (((x + (a + b) * 37) / c) * x) % (d + e);</pre>
+<p>Logical expressions:</p>
+<pre class="code-example">uint128_t x;<br>uint128_t y(&quot;0x11111111000100001111111100000001&quot;, 16);   // Assign a full 128 bit value of base 16 via a string.<br>uint128_t z(&quot;0x22222222000100002222222200000001&quot;, 16);
+<br>x = (y ^ z) + (y &amp; z) + (y | z);</pre>
+<blockquote></blockquote>
+<p>Floating point:</p>
+<pre class="code-example">uint128_t x = 143249.f;
+x *= 32245.6f
+<br>printf(&quot;%f&quot;, x.AsFloat());</pre>
+<p>Mixture of int128_t and uint128_t:</p>
+<pre class="code-example">int128_t  x(-1000000000);<br>uint128_t y(120);
+<br>x *= y; </pre>
+<p>Converting int128_t to a string:</p>
+<pre class="code-example">char     buffer[64];
+int128_t x = 10345;
+
+s.Int128ToStr(buffer, NULL, 10); // Just like strtol().</pre>
+<p>Set int128_t as a very large value from a string:</p>
+<pre class="code-example">uint128_t x("141183460469231731687303715884105728");</pre>
+<p>Set int128_t as a very large value from two 64 bit values:</p>
+<pre class="code-example">uint128_t x(0x1122334455667788);
+x &lt;&lt;= 64;
+x |= 0x8877665544332211;</pre>
+<h2> 
+  Limitations</h2>
+<p>The primary differences between our int128_t and a hypothetical built-in version 
+  are:</p>
+<ul>
+  <li>int128_t doesn't implement cast operators to lesser integer types. Instead, 
+    provides explicit converters such as AsInt32, AsInt64, etc. This is by design, 
+    as such casting operators result in ambiguous conversions and would need built-in 
+    compiler knowledge to resolve the situation.</li>
+  <li>Standard library functions such as sprintf have no support for such 128 
+    bit types. We partially rectify this by providing StrToInt128 and Int128ToStr 
+    functions.</li>
+  <li>128 bit contant literals aren't supported by the compiler, so we implement 
+    them via strings (and indirectly via shifting). You can assign an 8 through 
+    64 bit integral constant to int128_t, but if you want to assign a full 128 
+    bit constant to an int128_t, you'll need to use a string or use two 64 bit 
+    constants, one for low and one for high.</li>
+</ul>
+<h2>Interface </h2>
+<pre class="code-example">class int128_t
+{
+public:
+    int128_t();
+    int128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
+    int128_t(uint64_t nPart0, uint64_t nPart1);
+
+    int128_t(int8_t value);
+    int128_t(uint8_t value);
+
+    int128_t(int16_t value);
+    int128_t(uint16_t value);
+
+    int128_t(int32_t value);
+    int128_t(uint32_t value);
+
+    int128_t(int64_t value);
+    int128_t(uint64_t value);
+
+    int128_t(const int128_t& value);
+
+    int128_t(const float value);
+    int128_t(const double value);
+
+    int128_t(const char* pValue, int nBase = 10);
+    int128_t(const wchar_t* pValue, int nBase = 10);
+
+    // Assignment operator
+    int128_t& operator=(const int128_t_base& value);
+
+    // Unary arithmetic/logic operators
+    int128_t  operator-() const;
+    int128_t& operator++();
+    int128_t& operator--();
+    int128_t  operator++(int);
+    int128_t  operator--(int);
+    int128_t  operator~() const;
+    int128_t  operator+() const;
+
+    // Math operators
+    friend int128_t operator+(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator-(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator*(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator/(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator%(const int128_t& value1, const int128_t& value2);
+
+    int128_t& operator+=(const int128_t& value);
+    int128_t& operator-=(const int128_t& value);
+    int128_t& operator*=(const int128_t& value);
+    int128_t& operator/=(const int128_t& value);
+    int128_t& operator%=(const int128_t& value);
+
+    // Shift operators
+    int128_t  operator>> (int nShift) const;
+    int128_t  operator<< (int nShift) const;
+    int128_t& operator>>=(int nShift);
+    int128_t& operator<<=(int nShift);
+
+    // Logical operators
+    friend int128_t operator^(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator|(const int128_t& value1, const int128_t& value2);
+    friend int128_t operator&(const int128_t& value1, const int128_t& value2);
+
+    int128_t& operator^= (const int128_t& value);
+    int128_t& operator|= (const int128_t& value);
+    int128_t& operator&= (const int128_t& value);
+
+    // Equality operators
+    friend int  compare   (const int128_t& value1, const int128_t& value2);
+    friend bool operator==(const int128_t& value1, const int128_t& value2);
+    friend bool operator!=(const int128_t& value1, const int128_t& value2);
+    friend bool operator> (const int128_t& value1, const int128_t& value2);
+    friend bool operator>=(const int128_t& value1, const int128_t& value2);
+    friend bool operator< (const int128_t& value1, const int128_t& value2);
+    friend bool operator<=(const int128_t& value1, const int128_t& value2);
+
+    // Operators to convert back to basic types
+    int8_t  AsInt8()   const;
+    int16_t AsInt16()  const;
+    int32_t AsInt32()  const;
+    int64_t AsInt64()  const;
+    float   AsFloat()  const;
+    double  AsDouble() const;
+
+    // Misc. Functions
+    void    Negate();
+    bool    IsNegative() const;
+    bool    IsPositive() const;
+    void    Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const;
+
+    // String conversion functions
+    int128_t StrToInt128(const char* pValue, char** ppEnd, int base) const;
+    int128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base) const;
+    void     Int128ToStr(char* pValue, char** ppEnd, int base) const;
+    void     Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base) const;
+
+}; <span class="code-example-comment">// class int128_t</span>
+
+
+
+class uint128_t<br>{<br>public:<br>    uint128_t();<br>    uint128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);<br>    uint128_t(uint64_t nPart0, uint64_t nPart1);
+
+    uint128_t(int8_t value);
+    uint128_t(uint8_t value);
+
+    uint128_t(int16_t value);<br>    uint128_t(uint16_t value);
+<br>    uint128_t(int32_t value);<br>    uint128_t(uint32_t value);
+<br>    uint128_t(int64_t value);<br>    uint128_t(uint64_t value);
+<br>    uint128_t(const int128_t&amp; value);<br>    uint128_t(const uint128_t&amp; value);
+<br>    uint128_t(const float value);<br>    uint128_t(const double value);
+<br>    uint128_t(const char* pValue, int nBase = 10);<br>    uint128_t(const wchar_t* pValue, int nBase = 10);
+<br>    // Assignment operator<br>    uint128_t&amp; operator=(const int128_t_base&amp; value);
+<br>    // Unary arithmetic/logic operators<br>    uint128_t  operator-() const;<br>    uint128_t&amp; operator++();<br>    uint128_t&amp; operator--();<br>    uint128_t  operator++(int);<br>    uint128_t  operator--(int);<br>    uint128_t  operator~() const;<br>    uint128_t  operator+() const;
+<br>    // Math operators<br>    friend uint128_t operator+(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator-(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator*(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator/(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator%(const uint128_t&amp; value1, const uint128_t&amp; value2);
+<br>    uint128_t&amp; operator+=(const uint128_t&amp; value);<br>    uint128_t&amp; operator-=(const uint128_t&amp; value);<br>    uint128_t&amp; operator*=(const uint128_t&amp; value);<br>    uint128_t&amp; operator/=(const uint128_t&amp; value);<br>    uint128_t&amp; operator%=(const uint128_t&amp; value);
+<br>    // Shift operators<br>    uint128_t  operator&gt;&gt; (int nShift) const;<br>    uint128_t  operator&lt;&lt; (int nShift) const;<br>    uint128_t&amp; operator&gt;&gt;=(int nShift);<br>    uint128_t&amp; operator&lt;&lt;=(int nShift);
+<br>    // Logical operators<br>    friend uint128_t operator^(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator|(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend uint128_t operator&amp;(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    uint128_t&amp; operator^= (const uint128_t&amp; value);<br>    uint128_t&amp; operator|= (const uint128_t&amp; value);<br>    uint128_t&amp; operator&amp;= (const uint128_t&amp; value);
+<br>    // Equality operators<br>    friend int  compare   (const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator==(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator!=(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator&gt; (const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator&gt;=(const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator&lt; (const uint128_t&amp; value1, const uint128_t&amp; value2);<br>    friend bool operator&lt;=(const uint128_t&amp; value1, const uint128_t&amp; value2);
+<br>    // Operators to convert back to basic types<br>    int8_t  AsInt8()   const;<br>    int16_t AsInt16()  const;<br>    int32_t AsInt32()  const;<br>    int64_t AsInt64()  const;<br>    float   AsFloat()  const;<br>    double  AsDouble() const;
+<br>    // Misc. Functions<br>    void    Negate();<br>    bool    IsNegative() const;<br>    bool    IsPositive() const;<br>    void    Modulus(const uint128_t&amp; divisor, uint128_t&amp; quotient, uint128_t&amp; remainder) const;
+<br>    // String conversion functions<br>    uint128_t StrToInt128(const char* pValue, char** ppEnd, int base) const;<br>    uint128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base) const;<br>    void      Int128ToStr(char* pValue, char** ppEnd, int base) const;<br>    void      Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base) const;
+<br>}; <span class="code-example-comment">// class uint128_t</span>
+</pre>
+<p></p>
+<hr>
+<p><br>
+  <br>
+  <br>
+  <span style="font-family: monospace;">&nbsp;&nbsp; </span><br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+  <br>
+</p>
+</body></html>

+ 83 - 0
doc/UTFDoc.css

@@ -0,0 +1,83 @@
+body 
+{
+	font-family: Palatino Linotype, Book Antiqua, Times New Roman;
+	font-size: 11pt;
+}
+
+h1
+{
+	font-family: Verdana;
+	display: block;
+	background-color: #FFF0B0;
+	border: solid 2px black;
+	font-size: 16pt;
+	font-weight: bold;
+	padding: 6px;
+}
+
+h2 
+{
+	font-size: 14pt;
+	font-family: Verdana;
+	border-bottom: 2px solid black;
+}
+
+h3
+{
+	font-family: Verdana;
+	font-size: 13pt;
+	font-weight: bold;
+}
+
+.code-example 
+{
+	display: block;
+	background-color: #e0e0f0;
+	margin-left: 3em;
+	margin-right: 3em;
+	margin-top: 1em;
+	margin-bottom: 1em;
+	padding: 8px;
+	border: solid 2px #a0a0d0;
+	font-family: monospace;
+	font-size: 10pt;
+	white-space: pre;
+}
+
+.code-example-span 
+{
+	font-family: monospace;
+	font-size: 10pt;
+	white-space: pre;
+}
+
+.code-example-comment
+{
+	background-color: #e0e0f0; 
+	padding: 0px 0px; 
+	font-family: monospace; 
+	font-size: 10pt; 
+	white-space: pre; 
+	color: #999999; 
+	margin: auto auto; 
+}
+
+
+.faq-question
+{
+	background-color: #D0E0D0;
+	font-size: 12pt;
+	font-weight: bold;
+	margin-bottom: 0.5em;
+	margin-top: 0em;
+	padding-left:8px;
+	padding-right:8px;
+	padding-top:2px;
+	padding-bottom:2px
+}
+
+.faq-answer
+{
+	display: block;
+	margin: 4pt 1em 0.5em 1em;
+}

+ 698 - 0
include/EAStdC/EAAlignment.h

@@ -0,0 +1,698 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This header provides:
+//    EAAlignOf
+//    AlignOf<T>
+//
+//    AlignUp<T>            -- Provides basic address and object alignment.
+//    AlignDown<T>
+//    GetAlignment
+//    IsAligned<T>
+//    
+//    AlignedArray           -- Allows for specifying alignment of objects at runtime and in a compiler-independent way.
+//    AlignedObject
+//    
+//    ReadMisalignedUint16   -- Allows for reading from misaligned memory.
+//    ReadMisalignedUint32
+//    ReadMisalignedUint64
+//    WriteMisalignedUint16  -- Allows for writing to misaligned memory.
+//    WriteMisalignedUint32
+//    WriteMisalignedUint64
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAALIGNMENT_H
+#define EASTDC_EAALIGNMENT_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+EA_DISABLE_VC_WARNING(4548 4836 4574)
+#include <stddef.h>
+#include <new>
+EA_RESTORE_VC_WARNING()
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+
+// The default HasTrivialDestructor template implementation doesn't always work on all 
+// compilers for vector intrinsic types. It is more reliable for us to use the built-in 
+// compiler traits. It's possible we may want to do this for other compilers as well. 
+// Currently this is only supported on newer versions of GCC and Clang.
+#if defined EA_COMPILER_GNUC || defined EA_COMPILER_CLANG
+	#if defined EA_COMPILER_GNUC && (EA_COMPILER_VERSION <= 4004)
+		#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
+	#elif defined EA_PLATFORM_APPLE
+		#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
+	#else
+		#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 1
+	#endif
+#else
+	#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
+#endif
+
+
+
+/// EAAlignOf
+/// 
+/// Note: This has been superceded by EABase's EA_ALIGN_OF.
+///
+/// Gives you the alignment of a given data type. If you are using 
+/// a compiler that doesn't support a built-in alignof then you 
+/// are stuck with being able to use EAAlignOf only on "POD" types
+/// (i.e. basic C structures).
+///
+/// Example usage:
+///    printf("Alignment of type 'int' is %u.", (unsigned)EAAlignOf(int)); 
+/// 
+#ifndef EAAlignOf
+	#define EAAlignOf(x) EA_ALIGN_OF(x)
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+	///////////////////////////////////////////////////////////////////////////
+	/// Type Traits
+	template <typename T, T v>
+	struct IntegralConstant
+	{
+		static const T value = v;
+		typedef T value_type;
+		typedef IntegralConstant<T, v> type;
+	};
+
+	typedef IntegralConstant<bool, true>  TrueType;
+	typedef IntegralConstant<bool, false> FalseType;
+
+	#if EASTDC_TRIVIAL_DTOR_VIA_TRAITS
+		template <typename T> struct HasTrivialDestructor : public IntegralConstant<bool, __has_trivial_destructor(T) || __is_pod(T)> {};
+	#else
+		template <typename T> struct HasTrivialDestructor : public FalseType{};
+	#endif
+
+	// Example usage: 
+	//     EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128)
+	#define EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(T) namespace EA {namespace StdC { \
+		template <> struct HasTrivialDestructor<T> : public TrueType{}; \
+		template <> struct HasTrivialDestructor<const T> : public TrueType{}; \
+		template <> struct HasTrivialDestructor<volatile T> : public TrueType{}; \
+		template <> struct HasTrivialDestructor<const volatile T> : public TrueType{}; } }
+
+
+
+	/// AlignOf
+	/// 
+	/// Gives you the alignment of a given data type. If you are using 
+	/// a compiler that doesn't support a built-in alignof then you 
+	/// are stuck with being able to use EAAlignOf only on "POD" types
+	/// (i.e. basic C structures).
+	///
+	/// Example usage:
+	///    int x;
+	///    printf("Alignment of type int is %u.", (unsigned)AlignOf<int>()); 
+	/// 
+	template <typename T>
+	inline size_t AlignOf()
+	{
+		return EAAlignOf(T);
+	}
+
+
+	/// AlignOf
+	/// 
+	/// This is an instance-based version of AlignOf. 
+	///
+	/// Example usage:
+	///    int x;
+	///    printf("Alignment of x is %u.", (unsigned)AlignOf(x)); 
+	/// 
+	template <typename T>
+	inline size_t AlignOf(const T&)
+	{
+		return EAAlignOf(T);
+	}
+
+
+	/// AlignUp
+	/// Rounds a scalar value (integer or pointer) up to the nearest multiple of 
+	/// Rounds values towards positive infinity.
+	/// Returns 0 for an input of 0.
+	/// The alignment 'a' must be an even power of 2.
+	/// This version of AlignUp is at least as efficient as the version which takes alignment as an argument.
+	/// Example:
+	///    AlignUp<int, 4>(3)  ->  4
+	///    AlignUp<int, 4>(8)  ->  8
+	///    AlignUp<int, 4>(0)  ->  0
+	///    AlignUp<int, 4>(-7) -> -4
+	template <typename T, size_t a>
+	inline T AlignUp(T x){
+		return (T)((x + (a - 1)) & ~(a - 1));
+	}
+
+	template <typename T, size_t a>
+	inline T* AlignUp(T* p){
+		return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
+	}
+
+
+
+	/// AlignUp
+	/// Rounds values towards positive infinity.
+	/// Returns 0 for an input of 0.  
+	/// The alignment 'a' must be an even power of 2.
+	/// Example:
+	///    AlignUp(3, 4)  ->  4
+	///    AlignUp(8, 4)  ->  8
+	///    AlignUp(0, 4)  ->  0
+	///    AlignUp(-7, 4) -> -4
+	template <typename T>
+	inline T AlignUp(T x, size_t a){
+		return (T)((x + (a - 1)) & ~(a - 1));
+	}
+
+	template <typename T>
+	inline T* AlignUp(T* p, size_t a){
+		return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
+	}
+
+
+
+	/// AlignDown
+	/// Rounds a scalar value (integer or pointer) down to nearest multiple of template parameter <a>.
+	/// Returns 0 for an input of 0.
+	/// Rounds values towards negative infinity.
+	/// The alignment 'a' must be an even power of 2.
+	/// This version of AlignDown is at least as efficient as the version which takes alignment as an argument.
+	/// Example:
+	///    AlignDown<int, 4>(5)  ->  4
+	///    AlignDown<int, 4>(4)  ->  4
+	///    AlignDown<int, 4>(0)  ->  0
+	///    AlignDown<int, 4>(-7) -> -8
+	template <typename T, size_t a>
+	inline T AlignDown(T x){
+		return (T)(x & ~(a - 1));
+	}
+
+	template <typename T, size_t a>
+	inline T* AlignDown(T* p){
+		return (T*)((uintptr_t)p & ~(a - 1));
+	}
+
+
+
+	/// AlignDown
+	/// Rounds a scalar value (integer or pointer) down to nearest multiple of of template parameter <a>.
+	/// x must be a scalar value (integer or pointer), else the results are undefined.
+	/// Returns 0 for an input of 0.
+	/// Rounds values towards negative infinity.
+	/// The alignment 'a' must be an even power of 2.
+	/// Example:
+	///    AlignDown(5, 4)  ->  4
+	///    AlignDown(4, 4)  ->  4
+	///    AlignDown(0, 4)  ->  0
+	///    AlignDown(-7, 4) -> -8
+	template <typename T>
+	inline T AlignDown(T x, size_t a){
+		return (T)(x & ~(a - 1));
+	}
+
+	template <typename T>
+	inline T* AlignDown(T* p, size_t a){
+		return (T*)((uintptr_t)p & ~(a - 1));
+	}
+
+
+	/// GetAlignment
+	/// 
+	/// Returns the highest power-of-two alignment of the given value x.
+	/// x must be a scalar value (integer or pointer), else the results are undefined.
+	/// Returns 0 for an input a value of 0.
+	/// Beware that GetAlignment returns the highest power-of-two alignment, which 
+	/// may result in a return value that is higher than you expect. Consider using
+	/// the IsAligned functions to test for a specific alignment.
+	/// Example:
+	///    GetAlignment(0)  ->  0
+	///    GetAlignment(1)  ->  1
+	///    GetAlignment(2)  ->  2
+	///    GetAlignment(3)  ->  1
+	///    GetAlignment(4)  ->  4
+	///    GetAlignment(5)  ->  1
+	///    GetAlignment(6)  ->  2
+	///    GetAlignment(7)  ->  1
+	///    GetAlignment(8)  ->  8
+	///    GetAlignment(9)  ->  1
+	template <typename T>
+	inline size_t GetAlignment(T x)
+	{
+		return (size_t)((x ^ (x - 1)) >> 1) + 1;
+	}
+
+	template <typename T>
+	inline size_t GetAlignment(T* p)
+	{
+		return (size_t)(((uintptr_t)p ^ ((uintptr_t)p - 1)) >> 1) + 1;
+	}
+
+
+	/// IsAligned
+	/// 
+	/// Tells if a given integer is aligned to a given power-of-2 boundary.
+	/// Returns true for an input x value of 0, regardless of the value of a.
+	/// The template <a> value must be >= 1.
+	/// Example:
+	///    IsAligned<int, 8>(64)  ->  true
+	///    IsAligned<int, 8>(67)  ->  false
+	///
+	/// To consider: wouldn't it be better if the template arguments were reversed?
+	///
+	template <typename T, int a>
+	inline bool IsAligned(T x)
+	{
+		return (x & (a - 1)) == 0;
+	}
+
+	template <typename T, int a>
+	inline bool IsAligned(T* p)
+	{
+		return ((uintptr_t)p & (a - 1)) == 0;
+	}
+
+
+	/// IsAligned
+	/// 
+	/// Tells if a given integer is aligned to a given power-of-2 boundary.
+	/// Returns true for an input x value of 0, regardless of the value of a.
+	/// The alignment value a must be >= 1.
+	/// Example:
+	///    IsAligned(64, 8)  ->  true
+	///    IsAligned(67, 8)  ->  false
+	///
+	template <typename T>
+	inline bool IsAligned(T x, size_t a)
+	{
+		return (x & (a - 1)) == 0;
+	}
+
+	template <typename T>
+	inline bool IsAligned(T* p, size_t a)
+	{
+		return ((uintptr_t)p & (a - 1)) == 0;
+	}
+
+
+	/// AlignedType
+	///
+	/// This class makes an aligned typedef for a given class based on a user-supplied 
+	/// class and alignment. This class exists because of a weakness in VC++ whereby 
+	/// it can only accept __declspec(align) and thus EA_PREFIX_ALIGN usage via an
+	/// integer literal (e.g. "16") and not an otherwise equivalent constant integral
+	/// expression (e.g. sizeof Foo). 
+	///
+	/// Example usage:
+	///     const size_t kAlignment = 32;
+	///     AlignedType<int, kAlignment>::Type intAlignedAt128;
+	///     intAlignedAt128 = 12345;
+	///
+	#if !defined(__GNUC__) || (__GNUC__ < 4) // GCC4 lost the ability to do this
+		template <typename T, size_t alignment> struct AlignedType { };
+
+		#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900	// VS2015+
+			EA_DISABLE_VC_WARNING(5029);  // nonstandard extension used: alignment attributes in C++ apply to variables, data members and tag types only
+		#endif
+		template<typename T> struct AlignedType<T,    2> { typedef EA_PREFIX_ALIGN(   2)    T    EA_POSTFIX_ALIGN(   2)    Type; };
+		template<typename T> struct AlignedType<T,    4> { typedef EA_PREFIX_ALIGN(   4)    T    EA_POSTFIX_ALIGN(   4)    Type; };
+		template<typename T> struct AlignedType<T,    8> { typedef EA_PREFIX_ALIGN(   8)    T    EA_POSTFIX_ALIGN(   8)    Type; };
+		template<typename T> struct AlignedType<T,   16> { typedef EA_PREFIX_ALIGN(  16)    T    EA_POSTFIX_ALIGN(  16)    Type; };
+		template<typename T> struct AlignedType<T,   32> { typedef EA_PREFIX_ALIGN(  32)    T    EA_POSTFIX_ALIGN(  32)    Type; };
+		template<typename T> struct AlignedType<T,   64> { typedef EA_PREFIX_ALIGN(  64)    T    EA_POSTFIX_ALIGN(  64)    Type; };
+		template<typename T> struct AlignedType<T,  128> { typedef EA_PREFIX_ALIGN( 128)    T    EA_POSTFIX_ALIGN( 128)    Type; };
+		template<typename T> struct AlignedType<T,  256> { typedef EA_PREFIX_ALIGN( 256)    T    EA_POSTFIX_ALIGN( 256)    Type; };
+		template<typename T> struct AlignedType<T,  512> { typedef EA_PREFIX_ALIGN( 512)    T    EA_POSTFIX_ALIGN( 512)    Type; };
+		template<typename T> struct AlignedType<T, 1024> { typedef EA_PREFIX_ALIGN(1024)    T    EA_POSTFIX_ALIGN(1024)    Type; };
+		template<typename T> struct AlignedType<T, 2048> { typedef EA_PREFIX_ALIGN(2048)    T    EA_POSTFIX_ALIGN(2048)    Type; };
+		template<typename T> struct AlignedType<T, 4096> { typedef EA_PREFIX_ALIGN(4096)    T    EA_POSTFIX_ALIGN(4096)    Type; };
+		#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900	// VS2015+
+			EA_RESTORE_VC_WARNING();
+		#endif
+	#else
+		// Need to deal with this under GCC 4, now that they took this functionality away.
+	#endif
+
+
+	/// AlignedArray
+	///
+	/// Allows you to align an array of objects, regardless of when or where they are declared.
+	/// Alignment must be a power-of-two value, such as 2, 4, 8, 16, 32, etc.
+	/// A common use of this would be to have an object on the stack be aligned to a specific
+	/// value. It also allows for a member variable of a struct to be aligned to a given size
+	/// without having any special requirements about the alignment of the struct itself.
+	/// This is advantageous relative to compiler-specific alignment mechanisms.
+	/// The downside of this mechanism relative to compiler-specific alignment mechanisms is
+	/// that this mechanism may use more memory.
+	///
+	/// Question: Should each element in the array be aligned or should just the beginning of
+	/// the array be aligned? Normally the answer should be just the beginning. The reason for
+	/// this is that the C/C++ standard expects array elements to be sizeof(T) apart from each
+	/// other when in an array.
+	///
+	/// Example usage:
+	///   AlignedArray<Vector, 10, 64> mVectorArray;
+	///   mVectorArray[0] = mVectorArray[1];
+	///   NormalizeVector(mVectorArray[3]);
+	///   ProcessVectorArray(mVectorArray);
+	///
+	/// To do: Make this class act like AlignedType and retain the smart pointer functionality.
+	///
+	template <class T, int count, int alignment>
+	class AlignedArray
+	{
+	public:
+		typedef T                                 value_type;
+		typedef AlignedArray<T, count, alignment> this_type;
+
+		AlignedArray()
+		{
+			mpT = (T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1));
+
+			for(T *pT = mpT, *pTEnd = mpT + count; pT < pTEnd; ++pT)
+				new(pT) T; // Note that we are not allocating memory here; we are constructing existing memory.
+		}
+
+		AlignedArray(const this_type& x)
+		{
+			mpT = (T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1));
+
+			for(T *pT = mpT, *pTSrc = x.mpT, *pTEnd = mpT + count; pT < pTEnd; ++pT, ++pTSrc)
+				new(pT) T(*pTSrc); // Note that we are not allocating memory here; we are constructing existing memory.
+		}
+
+		void DeleteMembers(TrueType) // true_type means yes it's true that T has a trivial destructor.
+		{
+		}
+
+		void DeleteMembers(FalseType) // false_type means the destructor is non-trivial.
+		{
+			for(T *pT = mpT + count - 1, *pTEnd = mpT; pT >= pTEnd; --pT)
+				pT->~T();
+		}
+
+		~AlignedArray()
+		{
+			DeleteMembers(HasTrivialDestructor<value_type>());
+		}
+
+		// The following set of supported operators is currently experimental. A little more
+		// thinking and testing will be needed to determine the proper best set of operators.
+
+		this_type& operator=(const this_type& x)
+		{
+			for(int i = 0; i < count; i++)  // To consider: We could possibly improve this a little bit by
+				mpT[i] = x.mpT[i];          // taking advantage of type traits or the STL/EASTL copy algorithm.
+			return *this;
+		}
+
+		operator T*()
+		{ return mpT; }
+
+		operator const T*() const
+		{ return mpT; }
+
+	protected:
+		T*   mpT;                                      // Points to a place within mBuffer.
+		char mBuffer[(sizeof(T) * count) + alignment]; // This max possible space usage required to align an array of type T.
+	};
+		
+	 
+	/// AlignedObject
+	///
+	/// Allows you to align an  object, regardless of when or where it is declared.
+	/// Alignment must be a power-of-two value, such as 2, 4, 8, 16, 32, etc.
+	/// A common use of this would be to have an object on the stack be aligned to a specific 
+	/// value. It also allows for a member variable of a struct to be aligned to a given size
+	/// without having any special requirements about the alignment of the struct itself. 
+	/// This is advantageous relative to compiler-specific alignment mechansims. 
+	/// The downside of this mechanism relative to compiler-specific alignment mechanisms is 
+	/// that this mechanism may use more memory.
+	///
+	/// This class lets you have most of the functionality you would have if the 
+	/// alignment functionality was native. For the most part, the only limitations
+	/// are that you sometimes need reference the object in a slightly different
+	/// way than usual. For example, you need to use operator->() to reference a 
+	/// class member instead of operator.() even though the object is not a pointer.
+	///
+	/// Example usage:
+	///    AlignedObject<Matrix, 64> matrix;
+	///    matrix->Normalize();
+	///    NormalizeMatrix(&matrix);
+	///
+	/// To do: Make this class act like AlignedType and retain the smart pointer functionality.
+	///
+	template <class T, int alignment>
+	class AlignedObject
+	{
+	public:
+		typedef T                           value_type;
+		typedef AlignedObject<T, alignment> this_type;
+
+		AlignedObject()
+			: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
+			{ new((void*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1))) T; } // Note that we are not allocating memory here; we are constructing existing memory.
+
+		AlignedObject(const this_type& x)
+			: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
+			{ mT = x.mT; }
+
+		AlignedObject(const T& t)
+			: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
+			{ mT = t; }
+
+	   ~AlignedObject()
+			{ mT.~T(); }
+
+		// The following set of supported operators is currently experimental. A little more
+		// thinking and testing will be needed to determine the proper best set of operators.
+
+		this_type& operator=(const this_type& x)
+			{ mT = x.mT; return *this; }
+
+		this_type& operator=(const T& t)
+			{ mT = t; return *this; }
+
+		T* operator &()
+			{ return &mT; }
+
+		T* Get()
+			{ return &mT; }
+
+		const T* Get() const
+			{ return &mT; }
+
+		operator T&()
+			{ return mT; }
+
+		operator const T&() const
+			{ return mT; }
+
+		// C++ doesn't allow overloading the . operator. 
+		// Use operator -> or the T& cast operator instead.
+		//T& operator.() const
+		//    { return mT; }
+
+		T* operator->()
+			{ return &mT; }
+
+		const T* operator->() const
+			{ return &mT; }
+		
+		operator T*()
+			{ return &mT; }
+
+		operator const T*() const
+			{ return &mT; }
+
+	protected:
+		T&   mT;                             // Points to a place within mBuffer.
+		char mBuffer[sizeof(T) + alignment]; // This max possible space usage required to align an object of type T.
+	};
+
+
+
+
+	/// ReadMisalignedUint16
+	///
+	/// Get an unsigned integer from a possibly non-aligned address.
+	/// The MIPS processor on the PS2 cannot read a 32-bit value
+	/// from an unaligned address, so this function can be used
+	/// to make reading misaligned data portable.
+	///
+	inline uint16_t ReadMisalignedUint16(const void* p)
+	{
+		return *((const uint16_t*)p);
+	}
+
+
+	/// ReadMisalignedUint32
+	///
+	/// Get an unsigned integer from a possibly non-aligned address.
+	/// The MIPS processor on the PS2 cannot read a 32-bit value
+	/// from an unaligned address, so this function can be used
+	/// to make reading misaligned data portable.
+	///
+	inline uint32_t ReadMisalignedUint32(const void* p)
+	{
+		return *((const uint32_t*)p);
+	}
+
+	/// ReadMisalignedUint64
+	///
+	/// Get an unsigned integer from a possibly non-aligned address.
+	/// The MIPS processor on the PS2 cannot read a 32-bit value
+	/// from an unaligned address, so this function can be used
+	/// to make reading misaligned data portable.
+	///
+	inline uint64_t ReadMisalignedUint64(const void* p)
+	{
+		return *((const uint64_t*)p);
+	}
+
+
+
+	/// WriteMisalignedUint16
+	inline void WriteMisalignedUint16(uint16_t n, void* p)
+	{
+		*(uint16_t*)p = n;
+	}
+
+	/// WriteMisalignedUint32
+	inline void WriteMisalignedUint32(uint32_t n, void* p)
+	{
+		*(uint32_t*)p = n;
+	}
+
+	/// WriteMisalignedUint64
+	inline void WriteMisalignedUint64(uint64_t n, void* p)
+	{
+		*(uint64_t*)p = n;
+	}
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+namespace EA
+{
+namespace StdC
+{
+	///////////////////////////////////////////////////////////////////////////
+	// Deprecated functions
+	///////////////////////////////////////////////////////////////////////////
+
+	/// AlignAddressUp
+	/// 
+	/// Aligns a given addess up to a specified power-of-two.
+	/// Example usage:
+	///    void* p;
+	///    p = AlignAddressUp(p, 64); 
+	/// 
+	inline void* AlignAddressUp(const void* p, size_t a)
+	{
+		return (void*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
+	}
+
+
+	/// AlignObjectUp
+	/// 
+	/// Aligns a given object up to an address of a specified power-of-two.
+	/// Example usage:
+	///    Matrix* pM;
+	///    pM = AlignObjectUp(pM, 16); 
+	/// 
+	template <typename T>
+	inline T* AlignObjectUp(const T* p, size_t a)
+	{
+		return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
+	}
+
+	/// AlignAddressDown
+	/// 
+	/// Aligns a given addess down to a specified power-of-two.
+	/// Example usage:
+	///    void* p;
+	///    p = AlignAddressDown(p, 8); 
+	/// 
+	inline void* AlignAddressDown(const void* p, size_t a)
+	{
+		return (void*)((uintptr_t)p & ~(a - 1));
+	}
+
+
+	/// AlignObjectDown
+	/// 
+	/// Aligns a given object down to an address of a specified power-of-two.
+	/// Example usage:
+	///    Matrix* pM;
+	///    pM = AlignObjectDown(pM, 16); 
+	/// 
+	template <typename T>
+	inline T* AlignObjectDown(const T* p, size_t a)
+	{
+		return (T*)((uintptr_t)p & ~(a - 1));
+	}
+
+
+	/// IsAddressAligned
+	/// 
+	/// Tells if a given address is aligned to a given power-of-2 boundary.
+	/// Always returns true for a pointer 'p' value of NULL.
+	/// The alignment 'a' must be >= 1.
+	/// Example:
+	///    IsAddressAligned((char*)0x00000000, 8)  ->  true
+	///    IsAddressAligned((char*)0x00000001, 8)  ->  false
+	///
+	inline bool IsAddressAligned(const void* p, size_t a)
+	{
+		return ((uintptr_t)p & (a - 1)) == 0;
+	}
+
+
+	/// IsObjectAligned
+	/// 
+	/// Tells if a given object is aligned to a given power-of-2 boundary.
+	/// This is redundant with respect to IsAddressAligned, but is provided
+	/// for consistency.
+	/// The alignment 'a' must be >= 1.
+	///
+	template <typename T>
+	inline bool IsObjectAligned(const T* p, size_t a)
+	{
+		return ((uintptr_t)p & (a - 1)) == 0;
+	}
+
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+

+ 1570 - 0
include/EAStdC/EABitTricks.h

@@ -0,0 +1,1570 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// This is a list of C/C++ bit manipulation tricks. For example, it is 
+// well-known that (x * 2) can be also accomplished with (x << 1). 
+// And while this example may not be useful in practice, there are many 
+// more such tricks which have real use, particularly for speeding up code.
+// 
+// Notes:
+//     * Twos-complement integer storage is assumed. Nearly all modern 
+//       processors use twos-complement storage.
+//     * Some tricks assume that right shifts of signed values preserve 
+//       the sign. While nearly all modern processors and C/C++ compilers 
+//       support this, some primitive processors don't. By preserving sign, 
+//       we mean that signed binary (10000000 >> 1) gives (11000000).
+//     * Only 'tricky' efficient solutions are provided. Obvious brute force loops 
+//       to do useful things aren't included. We attempt to use branchless and  
+//       loopless logic where possible.
+//     * We don't cover magic number tricks for simplifying multiplication and 
+//       division by constants. For example (x * 17) can also be quickly accomplished 
+//       with ((x << 4) + x). Optimized integer multiplication and division tricks 
+//       such as this is something for a separate library.
+//     * We don't cover floating point tricks. That is something for a separate library. 
+//     * Implementations here are written as standalone functions for readability. 
+//       However, the user may find that it's better in some cases to copy the implementation 
+//       to a macro or to simply copy the implementation directly inline into source 
+//       code. EABitTricks is meant to be a reference for copy and paste as much as 
+//       it is meant to be used as-is.
+//     * Many of these functions are templated instead of taking a given integer 
+//       type such as uint32_t. The reason for this is that we want 64 bit support 
+//       and that can be had automatically in most cases by the use of templates. The 
+//       generated code will be exactly as fast as the case when templates are not used.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EABITTRICKS_H
+#define EASTDC_EABITTRICKS_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+#include <limits.h>
+
+
+
+#if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64))
+	// We can use assembly intrinsics for some of these functions.
+	EA_DISABLE_ALL_VC_WARNINGS()
+	#include <math.h>       // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
+	#include <intrin.h>
+	EA_RESTORE_ALL_VC_WARNINGS()
+	#pragma intrinsic(_BitScanForward)
+	#pragma intrinsic(_BitScanReverse)
+	#if defined(EA_PROCESSOR_X86_64)
+		#pragma intrinsic(_BitScanForward64)
+		#pragma intrinsic(_BitScanReverse64)
+	#endif
+#elif (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64))
+    #include <x86intrin.h>
+#endif
+
+EA_DISABLE_VC_WARNING(6326 4146) // C6326: Potential comparison of a constant with another constant
+								 // C4146: unary minus operator applied to unsigned type, result still unsigned
+
+
+
+namespace EA
+{
+namespace StdC
+{
+	namespace helper
+	{
+		///////////////////////////////////////////////////////////////////////
+		// is_signed
+		///////////////////////////////////////////////////////////////////////
+
+		struct false_type { static const bool value = false; };
+		struct true_type  { static const bool value = true;  };
+
+		template <typename T> struct is_signed : public false_type{};
+
+		template <> struct is_signed<signed char>              : public true_type{};
+		template <> struct is_signed<const signed char>        : public true_type{};
+		template <> struct is_signed<signed short>             : public true_type{};
+		template <> struct is_signed<const signed short>       : public true_type{};
+		template <> struct is_signed<signed int>               : public true_type{};
+		template <> struct is_signed<const signed int>         : public true_type{};
+		template <> struct is_signed<signed long>              : public true_type{};
+		template <> struct is_signed<const signed long>        : public true_type{};
+		template <> struct is_signed<signed long long>         : public true_type{};
+		template <> struct is_signed<const signed long long>   : public true_type{};
+
+		#if (CHAR_MAX == SCHAR_MAX)
+			template <> struct is_signed<char>            : public true_type{};
+			template <> struct is_signed<const char>      : public true_type{};
+		#endif
+		#ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type...
+			#if defined(__WCHAR_MAX__) && ((__WCHAR_MAX__ == 2147483647) || (__WCHAR_MAX__ == 32767)) // GCC defines __WCHAR_MAX__ for most platforms.
+				template <> struct is_signed<wchar_t>         : public true_type{};
+				template <> struct is_signed<const wchar_t>   : public true_type{};
+			#endif
+		#endif
+	
+		#define bt_is_signed helper::is_signed<T>::value
+		
+
+		//////////////////////////////////////////////
+		// add_signed
+		//////////////////////////////////////////////
+		
+		template<typename T>
+		struct add_signed
+		{ typedef T type; };
+
+		template<>
+		struct add_signed<unsigned char>
+		{ typedef signed char type; };
+
+		#if (defined(CHAR_MAX) && defined(UCHAR_MAX) && (CHAR_MAX == UCHAR_MAX)) // If char is unsigned (which is usually not the case)...
+			template<>
+			struct add_signed<char>
+			{ typedef signed char type; };
+		#endif
+
+		template<>
+		struct add_signed<unsigned short>
+		{ typedef short type; };
+
+		template<>
+		struct add_signed<unsigned int>
+		{ typedef int type; };
+
+		template<>
+		struct add_signed<unsigned long>
+		{ typedef long type; };
+
+		template<>
+		struct add_signed<unsigned long long>
+		{ typedef long long type; };
+
+		#ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type...
+			#if (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 4294967295U)) // If wchar_t is a 32 bit unsigned value...
+				template<>
+				struct add_signed<wchar_t>
+				{ typedef int32_t type; };
+			#elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 65535)) // If wchar_t is a 16 bit unsigned value...
+				template<>
+				struct add_signed<wchar_t>
+				{ typedef int16_t type; };
+			#endif
+		#endif
+
+		#define bt_signed typename helper::add_signed<T>::type // Must be used as part of a cast, as in (bt_signed) or static_cast<bt_signed>()
+	}
+	
+
+	////////////////////////////////////////////////////////////////////////////
+	// Bit manipulation
+	////////////////////////////////////////////////////////////////////////////
+
+	/// TurnOffLowestBit
+	/// How to turn off the lowest 1 bit in an integer.
+	/// Returns 0 for an input of 0.
+	/// Works with signed and unsigned integers.
+	/// Example:
+	///     01011000 -> 01010000
+	template <typename T>
+	inline T TurnOffLowestBit(T x){
+		return (T)(x & (x - 1));
+	}
+
+	/// IsolateLowestBit
+	/// How to isolate the lowest 1 bit.
+	/// Returns 0 for an input of 0.
+	/// Example:
+	///     01011000 -> 00001000
+	template <typename T>
+	inline T IsolateLowestBit(T x){
+		return (T)(x & (0 - x));
+	}
+
+	/// IsolateLowest0Bit
+	/// How to isolate the lowest 0 bit.
+	/// Returns 0 for an input of all bits set.
+	/// Example:
+	///     10100111 -> 00001000
+	template <typename T>
+	inline T IsolateLowest0Bit(T x){
+		return (T)(~x & (x + 1));
+	}
+
+	/// GetTrailing0Bits
+	/// How to produce a mask of all low zeroes.
+	/// Returns 0 for an input of all bits set.
+	/// Example:
+	///     01011000 -> 00000111
+	template <typename T>
+	inline T GetTrailing0Bits(T x){
+		return (T)(~x & (x - 1));
+	}
+
+	/// GetTrailing1And0Bits
+	/// How to produce a mask of lowest 1 bit and all lower zeroes.
+	/// Returns all bits set for an input of 0.
+	/// Returns 1 for an input of all bits set.
+	/// Example:
+	///     01011000 -> 00001111
+	template <typename T>
+	inline T GetTrailing1And0Bits(T x){
+		return (T)(x ^ (x - 1));
+	}
+
+	/// PropogateLowestBitDownward
+	/// How to propogate the lowest 1 bit downward.
+	/// Returns all bits set for an input of 0.
+	/// Example:
+	///     01011000 -> 01011111
+	template <typename T>
+	inline T PropogateLowestBitDownward(T x){
+		return (T)(x | (x - 1));
+	}
+
+	/// TurnOffLowestContiguousBits
+	/// How to turn off the lowest contiguous string of 1 bits.
+	/// Returns 0 for an input of 0.
+	/// Returns 0 for an input of all bits set.
+	/// Example:
+	///     01011000 -> 01000000
+	template <typename T>
+	inline T TurnOffLowestContiguousBits(T x){
+		return (T)(((x | (x - 1)) + 1) & x);
+	}
+
+	/// TurnOnLowest0Bit
+	/// How to turn off the lowest 0 bit in an integer.
+	/// Returns all bits set for an input of all bits set.
+	/// Example:
+	///     10100111 -> 10101111
+	template <typename T>
+	inline T TurnOnLowest0Bit(T x){
+		return (T)(x | (x + 1));
+	}
+
+	/// GetNextWithEqualBitCount
+	/// How to get the next higher integer with the same number of bits set.
+	/// This function is supposed (claim by original author) to be able to 
+	/// wrap around and continue properly, but testing indicates otherwise.
+	/// Do not call this with x = 0; as that causes a divide by 0.
+	/// Doesn't work for an input of all bits set.
+	/// Do not assign the result of this to a type of less size than the input argument.
+	/// This function has certain real-world applications.
+	/// This function has been called 'snoob' by some. 
+	/// Example:
+	///     01010110 -> 01011001
+	template <typename T>
+	inline T GetNextWithEqualBitCount(T x){
+		T smallest, ripple, ones;
+
+		smallest = x & -(bt_signed)x;
+		ripple   = x + smallest;
+		ones     = x ^ ripple;
+		ones     = (ones >> 2) / smallest;
+		return ripple | ones;
+	}
+
+
+	/// IsolateSingleBits
+	/// How to isolate single bits in an integer.
+	/// Example:
+	///        10101011 -> 10101000
+	template <typename T>
+	inline T IsolateSingleBits(T x){
+		return x & ~((x << 1) | (x >> 1));
+	}
+
+	template <typename T>
+	inline T IsolateSingle0Bits(T x){
+		return IsolateSingleBits(~x);
+	}
+
+	template <typename T>
+	inline T IsolateSingle0And1Bits(T x){
+		return (x ^ (x << 1)) & (x ^ (x >> 1));
+	}
+
+	/// ShiftRightSigned
+	/// How to do a signed right shift portably.
+	/// Some platform/compiler combinations don't support sign extension 
+	/// with right shifts of signed values. The C language standard doesn't 
+	/// guarantee such functionality. Most advanced CPUs and nearly all C 
+	/// compilers support this. Weak embedded processors may possibly not.
+	/// Example:
+	///     10000000 - by 2-> 11100000
+	///     00000000 - by 2-> 00000000
+	inline int32_t ShiftRightSigned(int32_t x, uint32_t n){
+		return int32_t((((uint32_t)x + UINT32_C(0x80000000)) >> n) - (UINT32_C(0x80000000) >> n));
+	}
+
+	inline int64_t ShiftRightSigned(int64_t x, uint64_t n){
+		return int64_t((((uint64_t)x + UINT64_C(0x8000000000000000)) >> n) - (UINT64_C(0x8000000000000000) >> n));
+	}
+
+	/// CountTrailing0Bits
+	/// How to count the number of trailing zeroes in an unsigned 32 bit integer.
+	/// Example:
+	///     ...10101000 -> 3
+	///     ...11111111 -> 0
+	///     ...00000000 -> 32
+	inline int CountTrailing0Bits(uint32_t x){
+		#if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64))
+			// This has been benchmarked as significantly faster than the generic code below.
+			unsigned char isNonZero;
+			unsigned long index;
+			isNonZero = _BitScanForward(&index, x);
+			return isNonZero ? (int)index : 32;
+		#elif defined(EA_COMPILER_GNUC) && !defined(EA_COMPILER_EDG)
+			if(x)
+				return __builtin_ctz(x);
+			return 32;
+		#else
+			if(x){
+				int n = 1;
+				if((x & 0x0000FFFF) == 0) {n += 16; x >>= 16;}
+				if((x & 0x000000FF) == 0) {n +=  8; x >>=  8;}
+				if((x & 0x0000000F) == 0) {n +=  4; x >>=  4;}
+				if((x & 0x00000003) == 0) {n +=  2; x >>=  2;}
+				return n - int(x & 1);
+			}
+			return 32;
+		#endif
+	}
+
+	/// CountTrailing0Bits
+	/// How to count the number of trailing zeroes in an unsigned 64 bit integer.
+	/// Example:
+	///     ...10101000 -> 3
+	///     ...11111111 -> 0
+	///     ...00000000 -> 64
+	inline int CountTrailing0Bits(uint64_t x){
+		#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64)
+			// This has been benchmarked as significantly faster than the generic code below.
+			unsigned char isNonZero;
+			unsigned long index;
+			isNonZero = _BitScanForward64(&index, x);
+			return isNonZero ? (int)index : 64;
+		#elif defined(EA_COMPILER_GNUC) && !defined(EA_COMPILER_EDG)
+			if(x)
+				return __builtin_ctzll(x);
+			return 64;
+		#else
+			if(x){
+				int n = 1;
+				if((x & 0xFFFFFFFF) == 0) { n += 32; x >>= 32; }
+				if((x & 0x0000FFFF) == 0) { n += 16; x >>= 16; }
+				if((x & 0x000000FF) == 0) { n +=  8; x >>=  8; }
+				if((x & 0x0000000F) == 0) { n +=  4; x >>=  4; }
+				if((x & 0x00000003) == 0) { n +=  2; x >>=  2; }
+				return n - (int)(unsigned)(x & 1);
+			}
+			return 64;
+		#endif
+	}
+
+	/// CountLeading0Bits
+	/// How to count the number of leading zeroes in an unsigned integer.
+	/// Example:
+	///   ..00000000 -> 32
+	///     00110111 ->  2
+	///     11111111 ->  0
+	///
+	inline int CountLeading0Bits(uint32_t x){
+		#if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64))
+			// This has been benchmarked as significantly faster than the generic code below.
+			unsigned char isNonZero;
+			unsigned long index;
+			isNonZero = _BitScanReverse(&index, x);
+			return isNonZero ? (31 - (int)index) : 32;
+		#elif defined(EA_COMPILER_GNUC) && !defined(EA_COMPILER_EDG)
+			if(x)
+				return __builtin_clz(x);
+			return 32;
+		#else
+			/// This implementation isn't highly compact, but would likely be 
+			/// faster than a brute force solution.
+			/// x86 function which is claimed to be faster:
+			///    double ff = (double)(v|1);
+			///    return ((*(1+(unsigned long *)&ff))>>20)-1023;
+			if(x){
+				int n = 0;
+				if(x <= 0x0000FFFF) { n += 16; x <<= 16; }
+				if(x <= 0x00FFFFFF) { n +=  8; x <<=  8; }
+				if(x <= 0x0FFFFFFF) { n +=  4; x <<=  4; }
+				if(x <= 0x3FFFFFFF) { n +=  2; x <<=  2; }
+				if(x <= 0x7FFFFFFF) { n +=  1;           }
+				return n;
+			}
+			return 32;
+		#endif
+	}
+
+	/// CountLeading0Bits
+	/// How to count the number of leading zeroes in an unsigned integer.
+	/// Example:
+	///   ..00000000 -> 64
+	///   ..00110111 ->  2
+	///   ..11111111 ->  0
+	///
+	inline int CountLeading0Bits(uint64_t x){
+		#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64)
+			// This has been benchmarked as significantly faster than the generic code below.
+			unsigned char isNonZero;
+			unsigned long index;
+			isNonZero = _BitScanReverse64(&index, x);
+			return isNonZero ? (63 - (int)index) : 64;
+		#elif defined(EA_COMPILER_GNUC) && !defined(EA_COMPILER_EDG)
+			if(x)
+				return __builtin_clzll(x);
+			return 64;
+		#else
+			if(x){ // We use a slightly different algorithm than the 32 bit version above because this version avoids slow large 64 bit constants, especially on RISC processors.
+				int n = 0;
+				if(x & UINT64_C(0xFFFFFFFF00000000)) { n += 32; x >>= 32; }
+				if(x & 0xFFFF0000)                   { n += 16; x >>= 16; }
+				if(x & 0xFFFFFF00)                   { n +=  8; x >>=  8; }
+				if(x & 0xFFFFFFF0)                   { n +=  4; x >>=  4; }
+				if(x & 0xFFFFFFFC)                   { n +=  2; x >>=  2; }
+				if(x & 0xFFFFFFFE)                   { n +=  1;           }
+				return 63 - n;
+			}
+			return 64;
+		#endif
+	}
+
+
+	/// CountBits
+	/// How to count the number of bits in an unsigned integer.
+	/// There are multiple variations of this function available, 
+	/// each tuned to the expected bit counts and locations. 
+	/// The version presented here has a large number of logical 
+	/// operations but has no looping or branching.
+	/// This implementation is taken from the AMD x86 optimization guide.
+	/// Example:
+	///     11001010 -> 4
+	inline int CountBits(uint32_t x){ // Branchless version
+#if defined(EASTDC_SSE_POPCNT)
+
+			return _mm_popcnt_u32(x);
+#else
+
+		x = x - ((x >> 1) & 0x55555555);
+		x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+		x = (x + (x >> 4)) & 0x0F0F0F0F;
+		return (int)((x * 0x01010101) >> 24);
+
+#endif
+	}
+
+	inline int CountBits64(uint64_t x){ // Branchless version
+#if defined(EASTDC_SSE_POPCNT) && defined(EA_PROCESSOR_X86_64)
+
+		return (int)_mm_popcnt_u64(x);
+#else
+
+		#if (EA_PLATFORM_WORD_SIZE < 8)
+			return CountBits((uint32_t)(x >> 32)) + CountBits((uint32_t)x);
+		#else
+			x = x - ((x >> 1) & UINT64_C(0x5555555555555555));
+			x = (x & UINT64_C(0x3333333333333333)) + ((x >> 2) & UINT64_C(0x3333333333333333));
+			x = (x + (x >> 4)) & UINT64_C(0x0F0F0F0F0F0F0F0F);
+			return (int)((x * UINT64_C(0x0101010101010101)) >> 56);
+		#endif
+
+#endif
+	}
+
+	/// RotateLeft
+	/// How to rotate an integer left.
+	/// Bit rotations can often be accomplished with inline assembly 
+	/// that uses the processor's native bit rotation capabilities.
+	/// Example:
+	///     11110000 -> left 2 -> 11000011
+	inline uint32_t RotateLeft(uint32_t x, uint32_t n){
+		return (uint32_t)((x << n) | (x >> (32 - n)));
+	}
+
+	inline uint64_t RotateLeft(uint64_t x, uint64_t n){
+		return (uint64_t)((x << n) | (x >> (64 - n)));
+	}
+
+	inline uint32_t RotateRight(uint32_t x, uint32_t n){
+		return (uint32_t)((x >> n) | (x << (32 - n)));
+	}
+
+	inline uint64_t RotateRight(uint64_t x, uint64_t n){
+		return (uint64_t)((x >> n) | (x << (64 - n)));
+	}
+
+
+	/// ReverseBits
+	/// How to reverse the bits in an integer.
+	/// There are other mechansims for accomplishing this. 
+	/// The version presented here has no branches, looping, nor table lookups.
+	/// Table lookups are the fastest when done on large amounts of data, but slower
+	/// when just done once due to the initial lookup cost.
+	/// Some ARM results: http://corner.squareup.com/2013/07/reversing-bits-on-arm.html
+	/// http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
+	/// Example:
+	///    11100001 -> 10000111
+	inline uint32_t ReverseBits(uint32_t x){
+		x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555);
+		x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333);
+		x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F);
+		x = (x << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | (x >> 24);
+		return x;
+	}
+
+	// This is surely not the fastest way to do this, and is here for completeness only.
+	// To do: Make a better version of this.
+	inline uint64_t ReverseBits(uint64_t x){
+		uint32_t x1 = (uint32_t)(x & 0xffffffff);
+		uint32_t x2 = (uint32_t)(x >> 32);
+		return ((uint64_t)ReverseBits(x1) << 32) | ReverseBits(x2);
+	}
+
+
+	/// IsolateHighestBit
+	/// How to isolate the highest 1 bit.
+	/// Returns 0 for an input of 0.
+	/// Example:
+	///    01000100 -> 01000000
+	///    00000000 -> 00000000
+	inline uint32_t IsolateHighestBit(uint32_t x){
+		x |= x >> 1; 
+		x |= x >> 2; 
+		x |= x >> 4; 
+		x |= x >> 8; 
+		x |= x >> 16;
+		return x ^ (x >> 1);
+	}
+
+	inline uint64_t IsolateHighestBit(uint64_t x){
+		x |= x >> 1; 
+		x |= x >> 2; 
+		x |= x >> 4; 
+		x |= x >> 8; 
+		x |= x >> 16;
+		x |= x >> 32;
+		return x ^ (x >> 1);
+	}
+
+	inline uint32_t IsolateHighest0Bit(uint32_t x){
+		return IsolateHighestBit(~x);
+	}
+
+	inline uint64_t IsolateHighest0Bit(uint64_t x){
+		return IsolateHighestBit(~x);
+	}
+
+	/// PropogateHighestBitDownward
+	/// How to set all bits from the highest 1 bit downward.
+	/// Returns 0 for an input of 0.
+	/// Example:
+	///    01001000 -> 01111111
+	///    00000000 -> 00000000
+	inline uint32_t PropogateHighestBitDownward(uint32_t x){
+		x |= (x >>  1);
+		x |= (x >>  2);
+		x |= (x >>  4);
+		x |= (x >>  8);
+		x |= (x >> 16);
+		return  x;
+	}
+
+	inline uint64_t PropogateHighestBitDownward(uint64_t x){
+		x |= (x >>  1);
+		x |= (x >>  2);
+		x |= (x >>  4);
+		x |= (x >>  8);
+		x |= (x >> 16);
+		x |= (x >> 32);
+		return  x;
+	}
+
+	/// GetHighestContiguous0Bits
+	/// How to set the highest contiguous 0 bits.
+	/// Returns 0 for an input of all bits set.
+	/// Example:
+	///    00011001 -> 11100000
+	///    11111111 -> 00000000
+	inline uint32_t GetHighestContiguous0Bits(uint32_t x){
+		x |= (x >>  1);
+		x |= (x >>  2);
+		x |= (x >>  4);
+		x |= (x >>  8);
+		x |= (x >> 16);
+		return  ~x;
+	}
+
+	inline uint64_t GetHighestContiguous0Bits(uint64_t x){
+		x |= (x >>  1);
+		x |= (x >>  2);
+		x |= (x >>  4);
+		x |= (x >>  8);
+		x |= (x >> 16);
+		x |= (x >> 32);
+		return  ~x;
+	}
+
+	/// GetBitwiseEquivalence
+	/// How to calculate bitwise equivalence.
+	/// Bitwise equivalence is the opposite of xor. It sets all bits that 
+	/// are the same to 1, whereas xor sets all bits that are different to 1. 
+	/// Thus, you can simply use ~ to get bitwise equivalence from xor.
+	/// Example:
+	///    11001100, 11110000 -> 11000011
+	template <typename T>
+	inline T GetBitwiseEquivalence(T x, T y){
+		return (T)(~(x ^ y));
+	}
+
+	/// AreLessThan2BitsSet
+	/// How to tell if at least two bits are set in an integer.
+	/// Example:
+	///    00001000 -> true
+	///    01001110 -> false
+	template <typename T>
+	inline bool AreLessThan2BitsSet(T x){
+		return (x & (x - 1)) == 0;
+	}
+
+#if defined(EASTDC_SSE_POPCNT)
+	template <>
+	inline bool AreLessThan2BitsSet<uint32_t>(uint32_t x){
+		uint32_t rv = (uint32_t)_mm_popcnt_u32(x);
+		return (rv < 2);
+	}
+
+#if defined(EA_PROCESSOR_X86_64)
+	template <>
+	inline bool AreLessThan2BitsSet<uint64_t>(uint64_t x){
+		uint64_t rv = (uint64_t)_mm_popcnt_u64(x);
+		return (rv < 2);
+	}
+#endif
+#endif
+
+
+	/// GetHighestBit
+	/// How to refer to the high bit of any integer data type where the 
+	/// data type size is not known.
+	/// There are cases where you may want to refer to the higest bit in 
+	/// an integer in a portable way. If you can't know the size of the 
+	/// destination platform's int type, you can use this to refer to such a bit.
+	/// Example:
+	///    GetHighestBit(uint32_t(0)) -> 0x80000000
+	///    GetHighestBit(uint16_t(0)) ->     0x8000
+	template <typename T>
+	inline T GetHighestBit(T /*t*/){
+		return (T)((T)1 << (T)((sizeof(T) * 8) - 1));
+	}
+
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Alignment / Power of 2
+	////////////////////////////////////////////////////////////////////////////
+
+	/// IsPowerOf2
+	/// How to tell if an unsigned integer is a power of 2.
+	/// Works with unsigned integers only.
+	/// Returns true for x == 0.
+	/// Example:
+	///    01000010 (66) -> false
+	///    00001100 (12) -> false
+	///    00000100  (4) -> true
+	///    00000000  (0) -> true
+	template <typename T>
+	inline bool IsPowerOf2(T x){
+		return (x & (x - 1)) == 0;
+	}
+
+#if defined(EASTDC_SSE_POPCNT)
+	template <>
+	inline bool IsPowerOf2<uint32_t>(uint32_t x){
+		uint32_t rv = (uint32_t)_mm_popcnt_u32(x);
+		return (rv < 2);
+	}
+
+#if defined(EA_PROCESSOR_X86_64)
+	template <>
+	inline bool IsPowerOf2<uint64_t>(uint64_t x){
+		uint64_t rv = (uint64_t)_mm_popcnt_u64(x);
+		return (rv < 2);
+	}
+#endif
+#endif
+
+
+	/// RoundUpToPowerOf2
+	/// Rounds an integer up to the nearest power of 2.
+	/// Returns 16 for x == 16, 32 for x == 32, etc.
+	/// Returns 0 for x == 0.
+	/// Example:
+	///    01000010 (66) -> 10000000 (128)
+	///    00001100 (12) -> 00010000 (16)
+	///    00000100  (4) -> 00000100 (4)
+	///    00000000  (0) -> 00000000 (0)
+	inline uint32_t RoundUpToPowerOf2(uint32_t x){
+		--x;
+		x |= (x >> 16);
+		x |= (x >>  8);
+		x |= (x >>  4);
+		x |= (x >>  2);
+		x |= (x >>  1);
+		return ++x;
+	}
+
+	inline uint64_t RoundUpToPowerOf2(uint64_t x){
+		--x;
+		x |= (x >> 32);
+		x |= (x >> 16);
+		x |= (x >>  8);
+		x |= (x >>  4);
+		x |= (x >>  2);
+		x |= (x >>  1);
+		return ++x;
+	}
+
+
+	/// IsPowerOf2Multiple
+	/// How to tell if an unsigned integer is a multiple of some specific power of 2.
+	/// Template constant n is required to be a power of 2.
+	/// Works with an input unsigned integers only. 'k' must be an even power of 2, such as 1, 2, 4, 8, etc.
+	/// Returns true for x == 0.
+	/// Example:
+	///     IsPowerOf2Multiple<4>(3)   -> false
+	///     IsPowerOf2Multiple<16>(32) -> true
+	///
+	template <typename T, int n>
+	inline bool IsPowerOf2Multiple(T x){
+		return (x & (n - 1)) == 0;
+	}
+	// *** The following is deprecated, as it doesn't use "power of 2" in its name ***
+	template <typename T, int n>
+	inline bool IsMultipleOf(T x){
+		return (x & (n - 1)) == 0;
+	}
+
+
+	/// IsPowerOf2Minus1
+	/// How to tell if an unsigned integer is of the form 2n-1 (e.g. 0x0fff, 0x000fffff).
+	/// Returns true for an input of 0.
+	/// Example:
+	///    00001111 (15) -> true
+	///    00001011 (11) -> false
+	///    00000000  (0) -> true
+	template <typename T>
+	inline bool IsPowerOf2Minus1(T x){
+		return (x & (x + 1)) == 0;
+	}
+
+	/// CrossesPowerOf2
+	/// How to detect any power of two crossing between two integers.
+	/// Useful for telling if an increasing value is about to go over a power of two boundary.
+	/// Example:
+	///    4, 5, 8 -> false
+	///    5, 9, 8 -> true
+	template <typename T>
+	inline bool CrossesPowerOf2(T x, T y, T n){
+		return (n - (x & (n - 1))) < (y - x);
+	}
+
+	/// CrossesPowerOf2
+	/// How to detect a specific power of two crossing between two integers.
+	/// Template parameter n must be an even power of 2, such as 1, 2, 4, 8, 16, etc.
+	/// Example:
+	///    CrossesPowerOf2<8>(3, 5)  -> false
+	///    CrossesPowerOf2<8>(7, 30) -> true
+	template <typename T, int n>
+	inline bool CrossesPowerOf2(T x, T y){
+		return (n - (x & (n - 1))) < (y - x);
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////
+	// GetHighestBitPowerOf2 template
+	//
+	// There is a templated compile-time constant version of GetHighestBitPowerOf2
+	// but it is called Log2Uint32 / Log2Int32 / Log2Uint64 / Log2Int64. See below
+	// for that template definition.
+	/////////////////////////////////////////////////////////////////////////////////
+
+
+	/// GetHighestBitPowerOf2 (a.k.a. GetHighestBitIndex)
+	/// How to detect the power of 2 of the highest bit set in a uint32_t.
+	/// The power of 2 is the same as the bit index, so this could also be 
+	/// called GetHighestBitIndex.
+	/// Returns 0 for an input of 0.
+	/// Returns a value in the range of [0, 31]
+	/// Example:
+	///    GetHighestBitPowerOf2(0)  -> 0
+	///    GetHighestBitPowerOf2(1)  -> 0
+	///    GetHighestBitPowerOf2(2)  -> 1
+	///    GetHighestBitPowerOf2(3)  -> 1
+	///    GetHighestBitPowerOf2(4)  -> 2
+	///    GetHighestBitPowerOf2(7)  -> 2
+	///    GetHighestBitPowerOf2(8)  -> 3
+	inline uint32_t GetHighestBitPowerOf2(uint32_t x){
+		uint32_t r = 0;
+
+		if(x & 0xffff0000){
+			r  += 16;
+			x >>= 16;
+		}
+		if(x & 0xff00){
+			r  += 8;
+			x >>= 8;
+		}
+		if(x & 0xf0){
+			r  += 4;
+			x >>= 4;
+		}
+		if(x & 0x0c){
+			r  += 2;
+			x >>= 2;
+		}
+		if(x & 0x02){
+			r  += 1;
+		}
+
+		return r;
+	}
+
+
+	/// GetHighestBitPowerOf2 (a.k.a. GetHighestBitIndex)
+	/// How to detect the power of 2 of the highest bit set in a uint64_t.
+	/// The power of 2 is the same as the bit index, so this could also be 
+	/// called GetHighestBitIndex.
+	/// Returns a value in the range of [0, 63]
+	/// Returns 0 for an input of 0.
+	/// Example:
+	///    GetHighestBitPowerOf2(0)  -> 0
+	///    GetHighestBitPowerOf2(1)  -> 0
+	///    GetHighestBitPowerOf2(2)  -> 1
+	///    GetHighestBitPowerOf2(3)  -> 1
+	///    GetHighestBitPowerOf2(4)  -> 2
+	///    GetHighestBitPowerOf2(7)  -> 2
+	///    GetHighestBitPowerOf2(8)  -> 3
+	inline uint32_t GetHighestBitPowerOf2(uint64_t x){
+		uint32_t r = 0;
+
+		if(x & UINT64_C(0xffffffff00000000)){
+			r  += 32;
+			x >>= 32;
+		}
+
+		return GetHighestBitPowerOf2((uint32_t)x) + r;
+	}
+
+
+	/// GetNextGreaterEven
+	/// Returns the next higher even integer. 
+	/// Returns an even number, not necessarily a power of 2.
+	/// Example:
+	///    GetNextGreaterEven<int>(-3)  -> -2
+	///    GetNextGreaterEven<int>(-2)  ->  0
+	///    GetNextGreaterEven<int>(-1)  ->  0
+	///    GetNextGreaterEven<int>( 0)  ->  2
+	///    GetNextGreaterEven<int>( 1)  ->  2
+	///    GetNextGreaterEven<int>( 2)  ->  4
+	///    GetNextGreaterEven<int>( 3)  ->  4
+	template <typename T>
+	inline T GetNextGreaterEven(T x)
+	{
+		return (x + 2) & -2;
+	}
+
+	/// GetNextGreaterOdd
+	/// Returns the next higher odd integer. 
+	/// Returns an even number, not necessarily a power of 2.
+	/// Example:
+	///    GetNextGreaterOdd<int>(-3)  -> -1
+	///    GetNextGreaterOdd<int>(-2)  -> -1
+	///    GetNextGreaterOdd<int>(-1)  ->  1
+	///    GetNextGreaterOdd<int>( 0)  ->  1
+	///    GetNextGreaterOdd<int>( 1)  ->  3
+	///    GetNextGreaterOdd<int>( 2)  ->  3
+	///    GetNextGreaterOdd<int>( 3)  ->  5
+	template <typename T>
+	inline T GetNextGreaterOdd(T x)
+	{
+		return ((x + 1) & -2) + 1;
+	}
+
+	/// RoundUpTo
+	/// Template constant n is required to be a power of 2.
+	/// Rounds values towards positive infinity.
+	/// Returns 0 for an input of 0.  
+	/// Example:
+	///    RoundUpTo<int, 4>(3)  ->  4
+	///    RoundUpTo<int, 4>(8)  ->  8
+	///    RoundUpTo<int, 4>(0)  ->  0
+	///    RoundUpTo<int, 4>(-7) -> -4
+	template <typename T, int n>
+	inline T RoundUpTo(T x){
+		return (T)((x + (n - 1)) & (T)-n);
+	}
+
+
+	/// RoundUpToEx
+	/// Template constant n is required to be a power of 2.
+	/// Rounds values away from zero.
+	/// Returns 0 for an input of 0.
+	/// Example:
+	///    RoundUpToEx<int, 4>(3)  ->  4
+	///
+	template <bool is_signed>
+	struct RoundUpToExStruct
+	{
+		template <typename T, int n>
+		static T RoundUpToEx(T x){
+			if(x >= 0)
+				return (T)((x + (n - 1)) & (T)-n);
+			return (T)-(bt_signed)((-(bt_signed)x + (n - 1)) & (T)-n);
+		}
+	};
+
+	template <>
+	struct RoundUpToExStruct<false>
+	{
+		template <typename T, int n>
+		static T RoundUpToEx(T x){
+			return (T)((x + (n - 1)) & (T)-n);
+		}
+	};
+
+	template <typename T, int n>
+	inline T RoundUpToEx(T x){
+		return RoundUpToExStruct<bt_is_signed>::template RoundUpToEx<T, n>(x);
+	}
+	
+
+	/// RoundDownTo
+	/// Template constant n is required to be a power of 2.
+	/// Returns 0 for an input of 0.
+	/// Rounds values towards negative infinity.
+	/// Example:
+	///    RoundDownTo<int, 4>(5)  ->  4
+	///    RoundDownTo<int, 4>(4)  ->  4
+	///    RoundDownTo<int, 4>(0)  ->  0
+	///    RoundDownTo<int, 4>(-7) -> -8
+	template <typename T, int n>
+	inline T RoundDownTo(T x){
+		return (T)(x & ~(n - 1));
+	}
+
+	/// RoundDownToEx
+	/// Template constant n is required to be a power of 2.
+	/// Returns 0 for an input of 0.
+	/// Rounds values towards zero.
+	/// Example:
+	///    RoundDownTo<int, 4>(5)  ->  4
+	///    RoundDownTo<int, 4>(4)  ->  4
+	///    RoundDownTo<int, 4>(0)  ->  0
+	///    RoundDownTo<int, 4>(-7) -> -4
+	///
+	template <bool is_signed>
+	struct RoundDownToExStruct
+	{
+		template <typename T, int n>
+		static T RoundDownToEx(T x){
+			if(x >= 0)
+				return (T)(x & ~(n - 1));
+			return (T)-(bt_signed)(-(bt_signed)x & ~(n - 1));
+		}
+	};
+
+	template <>
+	struct RoundDownToExStruct<false>
+	{
+		template <typename T, int n>
+		static T RoundDownToEx(T x){
+			return (T)(x & ~(n - 1));
+		}
+	};
+
+	template <typename T, int n>
+	inline T RoundDownToEx(T x){
+		return RoundDownToExStruct<bt_is_signed>::template RoundDownToEx<T, n>(x);
+	}
+
+
+
+	/// RoundUpToMultiple
+	/// This is a non-power of 2 function; n may be any value > zero.
+	/// Rounds values towards infinity.
+	/// Example:
+	///    RoundUpToMultiple<int, 6>(37)  ->  42
+	///    RoundUpToMultiple<int, 6>(42)  ->  42
+	template <typename T, int n>
+	inline T RoundUpToMultiple(T x){
+		return (T)(((x + (n - 1)) / n) * n);
+	}
+
+	/// RoundDownToMultiple
+	/// This is a non-power of 2 function; n may be any value > zero.
+	/// Rounds values towards zero.
+	/// Example:
+	///    RoundDownToMultiple<int, 6>(37)  ->  36
+	///    RoundDownToMultiple<int, 6>(36)  ->  36
+	template <typename T, int n>
+	inline T RoundDownToMultiple(T x){
+		return (T)((x / n) * n);
+	}
+
+
+	/// ZeroPresent8
+	/// Returns true if a 0 byte is present in x.
+	/// Example:
+	///    ZeroPresent8(0xffffffff)  ->  false
+	///    ZeroPresent8(0xffff00ff)  ->  true
+	inline bool ZeroPresent8(uint32_t x)
+	{
+		return ((((x - 0x01010101) & ~x) & 0x80808080) != 0); // Works because 0 is the only value for which subtracting 1 results in its high bit going from 0 to 1.
+	}
+
+	inline bool ZeroPresent8(uint64_t x)
+	{
+		return ((((x - UINT64_C(0x0101010101010101)) & ~x) & UINT64_C(0x8080808080808080)) != 0);
+	}
+
+
+	/// ZeroPresent16
+	/// Returns true if an aligned uint16_t of 0 is present in x.
+	/// Example:
+	///    ZeroPresent16(0xffffffff)  ->  false
+	///    ZeroPresent16(0xff0000ff)  ->  false   (There are 16 contiguous 0 bits, but they are not uint16_t aligned)
+	///    ZeroPresent16(0x0000ffff)  ->  true
+	inline bool ZeroPresent16(uint32_t x)
+	{
+		return ((((x - 0x00010001) & ~x) & 0x80008000) != 0);
+	}
+
+	inline bool ZeroPresent16(uint64_t x)
+	{
+		return ((((x - UINT64_C(0x0001000100010001)) & ~x) & UINT64_C(0x8000800080008000)) != 0);
+	}
+
+
+	/// ZeroPresent32
+	/// Returns true if an aligned uint32_t of 0 is present in x.
+	/// Example:
+	///    ZeroPresent16(0xffffffffffffffff)  ->  false
+	///    ZeroPresent16(0xffffff00000000ff)  ->  false   (There are 32 contiguous 0 bits, but they are not uint32_t aligned)
+	///    ZeroPresent16(0xffffffff00000000)  ->  true
+	inline bool ZeroPresent32(uint64_t x)
+	{
+		return ((((x - UINT64_C(0x0000000100000001)) & ~x) & UINT64_C(0x8000000080000000)) != 0);
+	  //return (((x & 0xffffffff) == 0) || (x >> 32) == 0); // Alternative implementation which is slower.
+	}
+
+
+	/// Log2
+	/// How to find the integer base 2 log of an integer.
+	/// It is frequently useful to tell what power of 2 a value corresponds to.
+	/// This function rounds the input value down to the nearest power of 
+	/// 2 before testing. Thus an input of 17 returns 4. The function can be 
+	/// made to round up by doing a standard power of 2 round-up at the 
+	/// beginning of the function.
+	/// This function doesn't work for an input of 0.
+	/// This function only works for unsigned integers.
+	/// For example, given "2^y = x" and you want to solve for y, you say:
+	///     y = Log2(x);
+	/// Assumes 32 bit IEEE float.
+	/// Note: This function yields incorrect answers for very large integers.
+	/// Example:
+	///     4 -> 2
+	///     8 -> 3
+	///    11 -> 3
+	///    16 -> 4
+	///     0 -> error
+	///     
+	inline uint32_t Log2(uint32_t x)
+	{
+		const union { float f; uint32_t i; } converter = { (float)x };
+		return (converter.i >> 23) - 127;
+	}
+
+	inline uint64_t Log2(uint64_t x)
+	{
+		const union { double f; uint64_t i; } converter = { (double)x };
+		return ((converter.i >> 52) & 0x7ff) - 1023;
+	}
+
+
+	/// CeilLog2
+	/// Generates the ceiling of Log2.
+	/// This function exists to deal with errors generated by Log2 under
+	/// the case of very large numbers.
+	inline uint32_t CeilLog2(uint32_t x){
+		const union { float f; uint32_t i; } converter = { (float)x };
+		return (converter.i - 0x3F000001) >> 23;
+	}
+
+	//inline uint64_t CeilLog2(uint64_t x){
+	//    const union { double f; uint64_t i; } converter = { (double)x };
+	//    return (converter.i - UINT64_C(0x________________)) >> 52;
+	//}
+
+
+	/// Log2Uint32 / Log2Int32 / Log2Uint64 / Log2Int64
+	///
+	/// Evaluates to floor(log2(N)) for a compile-time constant value of N, 
+	/// with no runtime cost, for any N > 0. This is the equivalent of a 
+	/// compile-time version of GetHighestBitPowerOf2, which gets the 
+	/// highest bit set in a value.
+	///
+	/// Example usage of Log2Uint32:
+	///   Log2Uint32<8>::value evaluates to 3
+	///   Log2Uint32<0x7fffffff>::value evaluates to 30
+	///   Log2Uint64<UINT64_C(0x1000000000000000)>::value evaluates to 60
+	///
+	template <uint32_t N> struct Log2Uint32    { static const uint32_t value = (1 + Log2Uint32<N/2>::value); };
+	template <>           struct Log2Uint32<1> { static const uint32_t value =  0; };
+
+	template <int32_t N>  struct Log2Int32     { static const int32_t value = (1 + Log2Int32<N/2>::value); };
+	template <>           struct Log2Int32<1>  { static const int32_t value =  0; };
+
+	template <uint64_t N> struct Log2Uint64    { static const uint64_t value = (1 + Log2Uint64<N/2>::value); };
+	template <>           struct Log2Uint64<1> { static const uint64_t value =  0; };
+
+	template <int64_t N>  struct Log2Int64     { static const int64_t value = (1 + Log2Int64<N/2>::value); };
+	template <>           struct Log2Int64<1>  { static const int64_t value =  0; };
+
+
+	/// DivideByPowerOf2Rounded
+	/// Divides a number x by 2^n, and rounds the result instead of 
+	/// chopping the result. This is useful for certain kinds of 
+	/// bit manipulation.
+	/// Example:
+	///     DivideByPowerOf2Rounded<uint32_t, 8>(  0)   -> 0
+	///     DivideByPowerOf2Rounded<uint32_t, 8>(127)   -> 0
+	///     DivideByPowerOf2Rounded<uint32_t, 8>(128)   -> 1
+	///     DivideByPowerOf2Rounded<uint32_t, 8>(256)   -> 1
+	///
+	// This is not yet tested.
+	//template<typename T, int n>
+	//inline T DivideByPowerOf2Rounded(T x)
+	//{
+	//    return ((x + (1 << (n - 1))) >> n);
+	//}
+
+
+	// EAGetAlignment
+	// This functionality and other alignment assistance functions have been
+	// moved to EAAlignment.h. The best way to portably and reliably detect
+	// alignment in both C and C++ is to use a compiler intrinsic or use 
+	// some fancy C++ templates. In either case, the solution is outside the
+	// realm of bit tricks.
+
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Overflow
+	////////////////////////////////////////////////////////////////////////////
+
+	EA_DISABLE_VC_WARNING(4310) // cast truncates constant value
+
+	/// SignedAdditionWouldOverflow
+	/// How to detect that an addition or subtraction of signed values will overflow.
+	/// This is a useful operation to test for when writing robust applications.
+	/// Signed integer overflow of addition or subtraction occurs if and only if 
+	/// the operands have the same sign and the result of the operation has the 
+	/// opposite of this sign.
+	/// Some platforms generate exceptions upon an overflow, and the functions 
+	/// presented here work by possibly causing overflow. Alternative versions of 
+	/// these functions exist which are slightly more complex but can work without 
+	/// generating overflows themselves. However, no platform that you 
+	/// are likely to be using generates such exceptions.
+	/// Example:
+	///    SignedAdditionWouldOverflow<int>(2, 3)                   -> false
+	///    SignedAdditionWouldOverflow<int>(0x7fffffff, 0x7fffffff) -> true
+	///
+	/// EA_NO_UBSAN is included because this function is designed to overflow 
+	/// and it UBSAN correctly warns here.
+	template <typename T>
+	EA_NO_UBSAN inline bool SignedAdditionWouldOverflow(T x, T y){
+		const T temp = (T)(x + y);
+		return (((temp ^ x) & (temp ^ y)) >> ((sizeof(T) * (T)8) - 1)) != 0;
+	}
+
+	template <typename T>
+	inline bool SignedSubtractionWouldOverflow(T x, T y){
+		const T tMin = (T)((T)1 << (T)((sizeof(T) * 8) - 1)); // This is not strictly portable.
+		return (x >= 0) ? (y < (T)(x - (T)-(tMin + 1))) : (y > (T)(x - tMin));
+	}
+
+	template <typename T>
+	inline bool UnsignedAdditionWouldOverflow(T x, T y){
+		const T temp = (T)(x + y);
+		return (temp < x) && (temp < y);
+	}
+
+	template <typename T>
+	inline bool UnsignedSubtractionWouldOverflow(T x, T y){
+		return y > x;
+	}
+
+	/// UnsignedMultiplyWouldOverflow
+	/// How to detect that a multiplication will overflow.
+	/// This is a useful operation to test for when writing robust applications.
+	/// Some platforms generate exceptions upon an overflow, and the functions 
+	/// presented here work by possibly causing overflow. Alternative versions of 
+	/// these functions exist which are slightly more complex but can work without 
+	/// generating overflows themselves. However, no platform that you 
+	/// are likely to be using generates such exceptions.
+	/// Example:
+	///    4 * 5 -> false
+	///    0xffffffff * 0xffffffff -> true
+	template <typename T>
+	inline bool UnsignedMultiplyWouldOverflow(T x, T y){
+		if(y)
+			return (((x * y) / y) != x);
+		return false;
+	}
+
+	inline bool SignedMultiplyWouldOverflow(int32_t x, int32_t y){
+		if((y < 0) && (x == (int32_t)INT32_C(0x80000000)))
+			return true;
+		if(y)
+			return (((x * y) / y) != x);
+		return false;
+	}
+
+	inline bool SignedMultiplyWouldOverflow(int64_t x, int64_t y){
+		if((y < 0) && (x == (int64_t)INT64_C(0x8000000000000000)))
+			return true;
+		if(y)
+			return (((x * y) / y) != x);
+		return false;
+	}
+
+	/// UnsignedDivisionWouldOverflow
+	/// How to detect that a division will overflow.
+	/// This is a useful operation to test for when writing robust applications.
+	/// Some platforms generate exceptions upon an overflow, and the functions 
+	/// presented here work by possibly causing overflow. Alternative versions 
+	/// of these functions exist which are slightly more complex but can work 
+	/// without generating overflows themselves. However, no platform that you 
+	/// are likely to be using generates such exceptions.
+	/// Example:
+	///    5 / 4 -> false
+	///    3 / 0 -> true
+	template <typename T>
+	inline bool UnsignedDivisionWouldOverflow(T /*x*/, T y){
+		return y == 0;
+	}
+
+	/// SignedDivisionWouldOverflow
+	/// How to detect that a division will overflow.
+	inline bool SignedDivisionWouldOverflow(int32_t x, int32_t y){
+		return (y == 0) || ((x == (int32_t)INT32_C(0x80000000)) && (y == -1));
+	}
+
+	inline bool SignedDivisionWouldOverflow(int64_t x, int64_t y){
+		return (y == 0) || ((x == (int64_t)INT64_C(0x8000000000000000)) && (y == -1));
+	}
+
+
+	/// GetAverage
+	/// How to compute average of two integers without possible overflow errors.
+	/// Returns floor of the average; Rounds to negative infinity.
+	/// This is useful for averaging two integers whereby the standard mechanism 
+	/// of returning (x + y) / 2  may overflow and yield an incorrect answer.
+	/// Normally, you want the floor (as opposed to ceiling) of the average, 
+	/// at least for positive values.
+	/// Example:
+	///     3,  4 ->  3
+	///     3,  3 ->  3
+	///    -3, -4 -> -4
+	///    -4,  5 ->  0
+	template <typename T>
+	inline int GetAverage(T x, T y){
+		return (x & y) + ((x ^ y) >> 1); // Need to use '>> 1' instead of '/ 2'
+	}
+
+	/// GetAverage_Ceiling
+	/// Returns ceiling of the average; Rounds to positive infinity.
+	/// Example:
+	///     3,  4 ->  4
+	///     3,  3 ->  3
+	///    -3, -4 -> -3
+	///    -4,  5 ->  1
+	template <typename T>
+	inline int GetAverage_Ceiling(T x, T y){
+		return (x | y) - ((x ^ y) >> 1); // Need to use '>> 1' instead of '/ 2'
+	}
+
+	EA_RESTORE_VC_WARNING()  // 4310
+
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Miscellaneous
+	////////////////////////////////////////////////////////////////////////////
+
+	/// GetParity
+	/// How to calculate parity of an integer.
+	/// Parity is defined as the oddness or evenness of the count of 1 bits.
+	/// There are other mechansims for accomplishing this. The version 
+	/// presented here has no branches, looping, nor table lookups.
+	/// Example:
+	///    01100011 -> 0 (even)
+	///    00101010 -> 1 (odd)
+	inline int GetParity(uint32_t x){
+		x ^= x >> 1;
+		x ^= x >> 2;
+		x = (x & UINT32_C(0x11111111)) * UINT32_C(0x11111111);
+		return (int)((x >> 28) & 1);
+	}
+
+	inline int GetParity(uint64_t x){
+		x ^= x >> 1;
+		x ^= x >> 2;
+		x = (x & UINT64_C(0x1111111111111111)) * UINT64_C(0x1111111111111111);
+		return (int)((x >> 60) & 1);
+	}
+
+
+	/// GetIsBigEndian
+	/// How to test (or verify) that the current machine is big-endian.
+	/// It is not normally possible with C/C++ to tell at compile-time what 
+	/// the machine endian-ness is. However, you might be able to check a 
+	/// predefined value, such as EABase's EA_SYSTEM_BIG_ENDIAN.
+	inline bool GetIsBigEndian(){
+		const int temp = 1;
+		return ((char*)&temp)[0] == 0;
+	}
+
+	/// ToggleBetween0And1
+	/// How to toggle an integer between 0 and 1.
+	/// Example:
+	///    1 -> 0
+	///    0 -> 1
+	inline int ToggleBetween0And1(int x){
+		return x ^ 1;
+	}
+
+	/// ToggleBetweenIntegers
+	/// How to toggle between any two integers.     
+	/// Toggling between three or more values is possible but requires 
+	/// code that is a bit more complicated.
+	/// You can do this in place with:
+	///    x ^= a ^ b;
+	/// Example:
+	///    5, 4, 5 -> 4
+	template <typename T>
+	inline T ToggleBetweenIntegers(T x, T a, T b){
+		return (T)(x ^ a ^ b);
+	}
+
+	/// IsBetween0AndValue
+	/// How to quickly see if an int is >= 0 and < some value.
+	/// The purpose of this is to save a comparison to zero and thus 
+	/// execute faster because you get two comparisons for the price of one. 
+	/// This method can be extended to any integer range by adding a 
+	/// value to x before the comparison.
+	/// Normally you wouldn't call a function as with this example, 
+	/// but instead you would just say something like:
+	///    if((unsigned)x < 4) ...
+	/// Example:
+	///    3, 4 -> true 
+	///   -3, 4 -> false
+	/// Example function:
+	///    inline bool IsBetween8And12(int x){
+	///        return ((unsigned)x + 8) < 12;
+	///    }
+	inline bool IsBetween0AndValue(int32_t x, int32_t a){
+		return (uint32_t)x < (uint32_t)a;
+	}
+
+	inline bool IsBetween0AndValue(int64_t x, int64_t a){
+		return (uint64_t)x < (uint64_t)a;
+	}
+
+	/// ExchangeValues
+	/// How to exchange two values in place.
+	/// It is well-known that xor-ing two values like this exchanges them in place.
+	/// This trick is perhaps of little practical value and is instead more of a curiosity.
+	/// Example:
+	///    2, 3 -> 3, 2
+	template <typename T>
+	inline void ExchangeValues(T& x, T& y){
+		x = (T)(x ^ y);
+		y = (T)(x ^ y);
+		x = (T)(x ^ y);
+	}
+
+	/// FloorMod
+	/// The normal C modulo operator (%) operates on the assumption that
+	/// division rounds towards zero, which means that the result of a
+	/// modulus operation on a negative number is also negative. (The
+	/// modulus operator in C is defined as the remainder after a division,
+	/// so in the case where a negative number is rounded up, the remainder
+	/// will be negative.) FloorMod instead returns the remainder of
+	/// a division rounded towards negative infinity; the modulus will always 
+	/// be a positive number.
+	template<class T>
+	T FloorMod(T n, T mod){
+		const T v = n % mod;
+		return v + ((v >> ((sizeof(v) * 8) - 1)) & mod);
+	}
+
+	/// GetSign
+	/// How to get the sign of an integer.
+	/// Negative inputs return -1, positive inputs return +1, a zero input returns 0.
+	/// These functions are significantly faster than brute force if/else statements.
+	/// Requires signed right shift support. Note that most compilers/platforms 
+	/// support signed numerical right shifts.
+	/// Example:
+	///     -5  -> -1
+	///      0  ->  0
+	///     250 ->  1
+	inline int GetSign(int32_t x){
+		return (int)((x >> 31) | (-(uint32_t)x >> 31)); // negation of signed int is undefined behaviour
+	}
+
+	inline int GetSign(int64_t x){
+		return (int)((x >> 63) | (-(uint64_t)x >> 63)); // negation of signed int is undefined behaviour
+	}
+
+	/// GetSignEx
+	/// How to get the sign of an integer.
+	/// Returns 1 for > 0, 0 for 0, and -1 for < 0.
+	/// This version doesn't work for input of 0x80000000 / 0x8000000000000000. 
+	inline int GetSignEx(int32_t x){
+		return (x >> 31) - (-x >> 31);
+	}
+
+	inline int GetSignEx(int64_t x){
+		return (x >> 63) - (-x >> 63);
+	}
+
+	/// SignExtend12
+	/// How to do fast sign extension of a 12 bit value to 32 bits.
+	/// Say you have a signed 12 bit value stored in an integer and you want to 
+	/// make it be a proper 32 bit int. To do this, you want to take the high bit 
+	/// of the 12 bit value and replicate it leftward.
+	/// To do this in a generic way, create a mask of the high bits in the 
+	/// larger operand plus the sign bit of the smaller, add, and xor.
+	/// Example:
+	///    0x00000fff -> 12 -> 0xffffffff
+	///    0x00000aaa -> 12 -> 0x00000aaa
+	///
+	///    0x00ffffff -> 24 -> 0xffffffff
+	///    0x00aaaaaa -> 24 -> 0x00aaaaaa
+	inline int32_t SignExtend12(int32_t x){
+		return (int32_t)((x + 0xfffff800) ^ 0xfffff800);
+	}
+
+	inline int32_t SignExtend24(int32_t x){
+		return (int32_t)((x + 0xff800000) ^ 0xff800000);
+	}
+
+	/// IsUnsigned
+	/// This version requires signed right shift support. It should work for most 
+	/// modern computers and compilers, as they have a signed right shift.
+	template <class T>
+	bool IsUnsigned(T /*x*/){
+		return (((T)(-1) >> 1) != (T)(-1));
+	}
+
+	/// EAIsUnsigned
+	/// How to tell if an integer variable is an unsigned type at runtime.
+	/// 
+	/// Note that this isn't a test to see of a given variable is negative; 
+	/// this is a test to see if a data type is signed or unsigned. 
+	/// With some data types, such as wchar_t and size_t, you can't know 
+	/// if the type is signed or unsigned at compile time.
+	///
+	/// This is a notoriously tricky problem, as commonly published solutions 
+	/// don't work for integer types other than int or make assumptions about 
+	/// how right shifting of signed values occur.
+	///
+	/// This could also be solved for C++ with templates. That would be a better
+	/// solution that this and would effectively be a compile-time check as opposed 
+	/// to a runtime check. See EASTL type traits for this.
+	///
+	/// Example usage:
+	///     uint8_t u8  = 0;
+	///     int64_t i64 = 0;
+	///     assert( EAIsUnsigned(u8)));
+	///     assert(!EAIsUnsigned(i64)));
+	#ifndef EAIsUnsigned
+		#define EAIsUnsigned(x) ((((x = ~x) >= 0) || (((x = ~x) != 0) && 0)) && ((x = ~x) >= 0))
+	#endif
+
+
+	// How to tell the integer representation of the computer.
+	// Just about every computer you are likely to work with will be twos-complement.
+	// You can look these terms up on the Internet to get detailed descriptions of them.
+	inline bool IsTwosComplement(){
+		return ((-2 | -3) == -1);
+	}
+
+	inline bool IsOnesComplement(){
+		return ((-1 & -2) == -3);
+	}
+
+	inline bool IsSignMagnitude(){
+		return ((-1 | -2) == -3);
+	}
+
+	inline bool IsOffsetBinary(){
+		return ((1u << (sizeof(int) * 8 - 1)) == 0);
+	}
+
+
+	/// EAOffsetOf
+	///
+	/// The offsetof macro is guaranteed to only work with POD types. However we wish to use
+	/// it for non-POD types but where we know that offsetof will still work for the cases 
+	/// in which we use it. 
+	#ifndef EAOffsetOf
+		#define EAOffsetOf EA_OFFSETOF
+	#endif
+
+
+	/// EAOffsetOfBase
+	///
+	/// Returns the offset of the class Base within the subclass Class.
+	/// Fails to compile for cases where Base is multiply inherited by Class.
+	/// Example usage:
+	///     struct A{ int a; };
+	///     struct B{ int b; };
+	///     struct C : public A, public B{ int c; };
+	///     EAOffsetOfBase<C, B>() => 4
+	/// 
+	/// EAOffsetOfBase is not guaranteed to be a compile-time constant expression, 
+	/// due to the use of reinterpret_cast of a pointer. 
+	///
+	#if !defined(EAOffsetOfBaseDefined)
+		#define EAOffsetOfBaseDefined 1
+
+		template <typename Class, typename Base>
+		EA_NO_UBSAN size_t EAOffsetOfBase() // Disable UBSAN because we are using an address which doesn't respect type alignment.
+		{    
+			return (size_t)((Base*)reinterpret_cast<Class*>(1)) - 1; // Use 1 instead of 0 because compilers often treat 0 specially and warn.
+		}
+	#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+EA_RESTORE_VC_WARNING()
+
+
+#endif // Header include guard
+
+
+
+ 
+
+
+
+

+ 99 - 0
include/EAStdC/EAByteCrackers.h

@@ -0,0 +1,99 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements safe "byte crackers" and builders for all primary 
+// built-in integral data types. These are particularly useful because working 
+// with signed values creates opportunities for mistakes, and the macros present 
+// in EAByteCrackers remove such possibilities for error.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#if !defined(EASTDC_EABYTECRACKERS_H) && !defined(FOUNDATION_EABYTECRACKERS_H)
+#define EASTDC_EABYTECRACKERS_H
+#define FOUNDATION_EABYTECRACKERS_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Conventions:
+//    0 (zero) refers to the lowest byte, 1 refers to the second lowest byte, etc. 
+//    UINT8_1_FROM_UINT32(0x12345678) for example, returns 0x56.
+//
+//    b means  8 bit byte
+//    w means 16 bit word
+//    d means 32 bit dword
+//    q means 64 bit quadword
+//
+// Example usage:
+//     uint8_t  u8  = UINT8_0_FROM_UINT16(0x1100);                // Returns 0x00
+//     uint8_t  u8  = UINT8_2_FROM_UINT64(0x7766554433221100);    // Returns 0x22
+//     uint16_t u16 = UINT16_3_FROM_UINT64(0x7766554433221100);   // Returns 0x7766
+//
+//     uint16_t u16 = UINT16_FROM_UINT8(0x11, 0x00);              // Returns 0x1100
+//     uint32_t u32 = UINT32_FROM_UINT8(0x33, 0x22, 0x11, 0x00);  // Returns 0x33221100
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef UINT8_0_FROM_UINT16
+
+	// uint8_t byte manipulators
+	#define UINT8_0_FROM_UINT16(w)                             ((uint8_t)(w))                         // Get the right-most byte from a uint16_t. (e.g. 0x1234 returns 0x34)
+	#define UINT8_1_FROM_UINT16(w)                             ((uint8_t)((uint16_t)(w) >>  8))       // Get the left-most byte from a uint16_t. (e.g. 0x1234 returns 0x12)
+
+	#define UINT8_0_FROM_UINT32(d)                             ((uint8_t)(d))                         // Get byte 0 from a uint32_t. (e.g. 0x12345678 returns 0x78)
+	#define UINT8_1_FROM_UINT32(d)                             ((uint8_t)((uint32_t)(d) >>  8))       // Get byte 1 from a uint32_t. (e.g. 0x12345678 returns 0x56)
+	#define UINT8_2_FROM_UINT32(d)                             ((uint8_t)((uint32_t)(d) >> 16))       // Get byte 2 from a uint32_t. (e.g. 0x12345678 returns 0x34)
+	#define UINT8_3_FROM_UINT32(d)                             ((uint8_t)((uint32_t)(d) >> 24))       // Get byte 3 from a uint32_t. (e.g. 0x12345678 returns 0x12)
+
+	#define UINT8_0_FROM_UINT64(q)                             ((uint8_t)(q))
+	#define UINT8_1_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >>  8))
+	#define UINT8_2_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 16))
+	#define UINT8_3_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 24))
+	#define UINT8_4_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 32))
+	#define UINT8_5_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 40))
+	#define UINT8_6_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 48))
+	#define UINT8_7_FROM_UINT64(q)                             ((uint8_t)((uint64_t)(q) >> 56))
+
+
+	// uint16_t byte manipulators
+	#define UINT16_0_FROM_UINT32(d)                            ((uint16_t)(d))                        // Get the right-most word from a uint32_t. (e.g. 0x12345678 returns 0x5678)
+	#define UINT16_1_FROM_UINT32(d)                            ((uint16_t)((uint32_t)(d) >> 16))      // Get the left-most  word from a uint32_t. (e.g. 0x12345678 returns 0x1234)
+
+	#define UINT16_0_FROM_UINT64(q)                            ((uint16_t)(q))
+	#define UINT16_1_FROM_UINT64(q)                            ((uint16_t)((uint64_t)(q) >> 16))
+	#define UINT16_2_FROM_UINT64(q)                            ((uint16_t)((uint64_t)(q) >> 32))
+	#define UINT16_3_FROM_UINT64(q)                            ((uint16_t)((uint64_t)(q) >> 48))
+
+	#define UINT16_FROM_UINT8(b1, b0)                          ((uint16_t)(((uint8_t)(b1) << 8) | (uint8_t)(b0)))
+
+
+	// uint32_t byte manipulators
+	#define UINT32_0_FROM_UINT64(q)                            ((uint32_t)(q))                        // Get the right-most dword from a uint64_t. (e.g. 0x0000000012345678 returns 0x12345678)
+	#define UINT32_1_FROM_UINT64(q)                            ((uint32_t)((uint64_t)(q) >> 32))      // Get the left-most  dword from a uint64_t. (e.g. 0x1234567800000000 returns 0x12345678)
+
+	#define UINT32_FROM_UINT8(b3, b2, b1, b0)                  ((uint32_t)(((uint8_t)(b3) << 24) | ((uint8_t)(b2) << 16) | ((uint8_t)(b1) << 8) | (uint8_t)(b0))) //Build a uint32_t from four Uint8s
+	#define UINT32_FROM_UINT16(w1, w0)                         ((uint32_t)(((uint16_t)(w1) << 16) | (uint16_t)(w0))) // Build a uint32_t from two Uint16s
+
+
+	// uint64_t byte manipulators
+	#define UINT64_FROM_UINT8(b7, b6, b5, b4, b3, b2, b1, b0)  ((uint64_t)(((uint64_t)((uint8_t)(b7)) << 56) | ((uint64_t)((uint8_t)(b6)) << 48) | ((uint64_t)((uint8_t)(b5)) << 40) | ((uint64_t)((uint8_t)(b4)) << 32) | ((uint64_t)((uint8_t)(b3)) << 24) | ((uint64_t)((uint8_t)(b2)) << 16) | ((uint64_t)((uint8_t)(b1)) << 8) | (uint64_t)((uint8_t)(b0))))
+	#define UINT64_FROM_UINT16(w3, w2, w1, w0)                 ((uint64_t)(((uint64_t)((uint16_t)(w3)) << 48) | ((uint64_t)((uint16_t)(w2)) << 32) | ((uint64_t)((uint16_t)(w1)) << 16) | (uint64_t)((uint16_t)(w0))))
+	#define UINT64_FROM_UINT32(d1, d0)                         ((uint64_t)(((uint64_t)((uint32_t)(d1)) << 32) | (uint64_t)((uint32_t)(d0))))
+
+#endif
+
+
+#endif // Header include guard
+
+
+
+
+
+
+

+ 536 - 0
include/EAStdC/EACType.h

@@ -0,0 +1,536 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This module implements the following functions.
+//
+//  int     Isalnum(char_t c);
+//  int     Isalpha(char_t c);
+//  int     Isdigit(char_t c);
+//  int     Isxdigit(char_t c);
+//  int     Isgraph(char_t c);
+//  int     Islower(char_t c);
+//  char_t  Tolower(char_t c);
+//  int     Isupper(char_t c);
+//  char_t  Toupper(char_t c);
+//  int     Isprint(char_t c);
+//  int     Ispunct(char_t c);
+//  int     Isspace(char_t c);
+//  int     Iscntrl(char_t c);
+//  int     Isascii(char_t c);
+//
+// By design, the 16 bit versions of these functions work only for chars 
+// up to 255. Characters above that always yield a return value of zero.
+// If you want Unicode-correct character and string classification 
+// functionality for all Unicode characters, use the EATextUnicode module 
+// from the EAText package. The Unicode module may be split off into a  
+// unique package by the time you read this.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EACTYPE_H
+#define EASTDC_EACTYPE_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+
+
+#ifndef EA_WCHAR_UNIQUE
+#define EA_WCHAR_UNIQUE 0
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	EASTDC_API int      Isalnum(char8_t c);
+	EASTDC_API int      Isalnum(char16_t c);
+	EASTDC_API int      Isalnum(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isalnum(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isalpha(char8_t c);
+	EASTDC_API int      Isalpha(char16_t c);
+	EASTDC_API int      Isalpha(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isalpha(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isdigit(char8_t c);
+	EASTDC_API int      Isdigit(char16_t c);
+	EASTDC_API int      Isdigit(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isdigit(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isxdigit(char8_t c);
+	EASTDC_API int      Isxdigit(char16_t c);
+	EASTDC_API int      Isxdigit(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isxdigit(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isgraph(char8_t c);
+	EASTDC_API int      Isgraph(char16_t c);
+	EASTDC_API int      Isgraph(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isgraph(wchar_t c);
+	#endif
+
+	EASTDC_API int      Islower(char8_t c);
+	EASTDC_API int      Islower(char16_t c);
+	EASTDC_API int      Islower(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Islower(wchar_t c);
+	#endif
+
+	EASTDC_API char8_t  Tolower(char8_t c);
+	EASTDC_API char16_t Tolower(char16_t c);
+	EASTDC_API char32_t Tolower(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API wchar_t  Tolower(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isupper(char8_t c);
+	EASTDC_API int      Isupper(char16_t c);
+	EASTDC_API int      Isupper(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isupper(wchar_t c);
+	#endif
+
+	EASTDC_API char8_t  Toupper(char8_t c);
+	EASTDC_API char16_t Toupper(char16_t c);
+	EASTDC_API char32_t Toupper(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API wchar_t  Toupper(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isprint(char8_t c);
+	EASTDC_API int      Isprint(char16_t c);
+	EASTDC_API int      Isprint(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isprint(wchar_t c);
+	#endif
+
+	EASTDC_API int      Ispunct(char8_t c);
+	EASTDC_API int      Ispunct(char16_t c);
+	EASTDC_API int      Ispunct(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Ispunct(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isspace(char8_t c);
+	EASTDC_API int      Isspace(char16_t c);
+	EASTDC_API int      Isspace(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isspace(wchar_t c);
+	#endif
+
+	EASTDC_API int      Iscntrl(char8_t c);
+	EASTDC_API int      Iscntrl(char16_t c);
+	EASTDC_API int      Iscntrl(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Iscntrl(wchar_t c);
+	#endif
+
+	EASTDC_API int      Isascii(char8_t c);
+	EASTDC_API int      Isascii(char16_t c);
+	EASTDC_API int      Isascii(char32_t c);
+	#if EA_WCHAR_UNIQUE
+		EASTDC_API int  Isascii(wchar_t c);
+	#endif
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inlines
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA
+{
+namespace StdC
+{
+	const size_t EASTDC_WCMAP_SIZE = 256;
+
+	extern EASTDC_API uint8_t EASTDC_WCTYPE_MAP[EASTDC_WCMAP_SIZE];
+	extern EASTDC_API uint8_t EASTDC_WLOWER_MAP[EASTDC_WCMAP_SIZE];
+	extern EASTDC_API uint8_t EASTDC_WUPPER_MAP[EASTDC_WCMAP_SIZE];
+
+	#define EASTDC_WCTYPE_CONTROL_1    0x01
+	#define EASTDC_WCTYPE_MOTION       0x02
+	#define EASTDC_WCTYPE_SPACE_1      0x04
+	#define EASTDC_WCTYPE_PUNCT        0x08
+	#define EASTDC_WCTYPE_DIGIT        0x10
+	#define EASTDC_WCTYPE_XDIGIT       0x20
+	#define EASTDC_WCTYPE_LOWER        0x40
+	#define EASTDC_WCTYPE_UPPER        0x80
+	#define EASTDC_WCTYPE_ALPHA        (EASTDC_WCTYPE_LOWER     | EASTDC_WCTYPE_UPPER)
+	#define EASTDC_WCTYPE_ALNUM        (EASTDC_WCTYPE_ALPHA     | EASTDC_WCTYPE_DIGIT)
+	#define EASTDC_WCTYPE_GRAPH        (EASTDC_WCTYPE_ALNUM     | EASTDC_WCTYPE_PUNCT)
+	#define EASTDC_WCTYPE_PRINT        (EASTDC_WCTYPE_GRAPH     | EASTDC_WCTYPE_SPACE)
+	#define EASTDC_WCTYPE_SPACE        (EASTDC_WCTYPE_SPACE_1   | EASTDC_WCTYPE_MOTION)
+	#define EASTDC_WCTYPE_CONTROL      (EASTDC_WCTYPE_CONTROL_1 | EASTDC_WCTYPE_MOTION)
+
+
+
+	inline int Isalnum(char8_t c) // char8_t is the same as char -- it is a signed or unsigned 8 bit value
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_ALNUM;
+	}
+
+	inline int Isalnum(char16_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit). 
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
+	}
+
+	inline int Isalnum(char32_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit). 
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isalnum(wchar_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit). 
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
+		}
+	#endif
+
+
+
+	inline int Isalpha(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_ALPHA;
+	}
+
+	inline int Isalpha(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
+	}
+
+	inline int Isalpha(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isalpha(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
+		}
+	#endif
+
+
+	inline int Isdigit(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_DIGIT;
+
+		// Alternative which may be faster due to avoiding a memory hit:
+		// return (((unsigned)(int)c - '0') < 10) ? 1 : 0;
+	}
+
+	inline int Isdigit(char16_t c)
+	{
+		// return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
+		// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
+		return (((unsigned)c - '0') < 10) ? 1 : 0;
+	}
+
+	inline int Isdigit(char32_t c)
+	{
+		// return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
+		// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
+		return (((uint32_t)c - '0') < 10) ? 1 : 0;
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isdigit(wchar_t c)
+		{
+			// return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
+			// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
+			return (((uint32_t)c - '0') < 10) ? 1 : 0;
+		}
+	#endif
+
+
+
+	inline int Isxdigit(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_XDIGIT;
+	}
+
+	inline int Isxdigit(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
+	}
+
+	inline int Isxdigit(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isxdigit(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
+		}
+	#endif
+
+
+
+	inline int Isgraph(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_GRAPH;
+	}
+
+	inline int Isgraph(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
+	}
+
+	inline int Isgraph(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isgraph(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
+		}
+	#endif
+
+
+	inline int Islower(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_LOWER;
+	}
+
+	inline int Islower(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
+	}
+
+	inline int Islower(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Islower(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
+		}
+	#endif
+
+
+
+	inline char8_t Tolower(char8_t c)
+	{
+		return (char8_t)EASTDC_WLOWER_MAP[(uint8_t)c];
+	}
+
+	inline char16_t Tolower(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (char16_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
+	}
+
+	inline char32_t Tolower(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (char32_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline wchar_t Tolower(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (wchar_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
+		}
+	#endif
+
+
+	inline int Isupper(char8_t c)
+	{
+		return (char8_t)EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_UPPER;
+	}
+
+	inline int Isupper(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
+	}
+
+	inline int Isupper(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isupper(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
+		}
+	#endif
+
+
+	inline char8_t Toupper(char8_t c)
+	{
+		return (char8_t)EASTDC_WUPPER_MAP[(uint8_t)c];
+	}
+
+	inline char16_t Toupper(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (char16_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
+	}
+
+	inline char32_t Toupper(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (char32_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline wchar_t Toupper(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (wchar_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
+		}
+	#endif
+
+
+	inline int Isprint(char8_t c) 
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_PRINT;
+	}
+
+	inline int Isprint(char16_t c) 
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
+	}
+
+	inline int Isprint(char32_t c) 
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isprint(wchar_t c) 
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
+		}
+	#endif
+
+
+	inline int Ispunct(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_PUNCT;
+	}
+
+	inline int Ispunct(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
+	}
+
+	inline int Ispunct(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Ispunct(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
+		}
+	#endif
+
+
+	inline int Isspace(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_SPACE;
+	}
+
+	inline int Isspace(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
+	}
+
+	inline int Isspace(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isspace(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
+		}
+	#endif
+
+
+	inline int Iscntrl(char8_t c)
+	{
+		return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_CONTROL;
+	}
+
+	inline int Iscntrl(char16_t c)
+	{
+		return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
+	}
+
+	inline int Iscntrl(char32_t c)
+	{
+		return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Iscntrl(wchar_t c)
+		{
+			return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
+		}
+	#endif
+
+
+	inline int Isascii(char8_t c)
+	{
+		return (uint8_t)c < 0x80;
+	}
+
+	inline int Isascii(char16_t c)
+	{
+		return (uint8_t)c < 0x80;
+	}
+
+	inline int Isascii(char32_t c)
+	{
+		return (uint8_t)c < 0x80;
+	}
+
+	#if EA_WCHAR_UNIQUE
+		inline int Isascii(wchar_t c)
+		{
+			return (uint8_t)c < 0x80;
+		}
+	#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+

+ 556 - 0
include/EAStdC/EACallback.h

@@ -0,0 +1,556 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// EACallback is a callback timer, also known as an asynchronous timer or alarm. 
+// You provide a callback function and your function will be called at a fixed 
+// periodic rate or will be called just once ("one-shot"). 
+//
+// Callback timers are useful for implementing systems that must respond to 
+// something periodically or after some amount of time. Examples include:
+//    - Streaming buffer periodic refilling
+//    - Text editor flashing carets
+//    - Alarm clocks
+//    - Starting and stopping of background tasks 
+//
+// Notes:
+//    - Time-based callbacks are specified in nanoseconds.
+//    - The callback system can work synchronously or asynchronously (user's choice). 
+//      In the former case, callbacks are serviced via a manually-called Tick function. 
+//      In the latter case, multithreading is used.
+//    - In the case of asynchronous callbacks, your callback function will likely be 
+//      called from a different thread than the thread used to set up the callback. 
+//    - The precision specification of a callback is merely a hint and not a guarantee, 
+//      due to the fact that the platforms we are running on are not strictly 
+//      real-time systems. This is particularly true with desktop systems like 
+//      Windows which implement pre-emptive thread time-slicing. 
+//    - The period of a callback will be respected to the extent possible. 
+//      However, if another callback is being serviced ahead of your callback, 
+//      it might cause delays in the calling of your callback. 
+//    - Callbacks may operate in synchronous and asynchronous modes with respect to 
+//      the rest of the application. Asynchronous mode is implemented via a separate thread. 
+//    - Periodicity may be time or tick based, depending on what you request. 
+//    - The callback parameters (e.g. period, function, mode) can be changed at any 
+//      time and from any place, including within the callback function itself. 
+//    - You can stop a periodic callback at any time and from any place, including 
+//      within the callback function itself.
+//    - As of this writing, EACallback doesn't guarantee ordering of event notifications.
+//      Thus if you have two callbacks, one to call back in 15 ms and another in 20 ms,
+//      and the callback system doesn't get polled until 30 ms later, the callback for 
+//      20 ms might be notified before the callback for 15 ms. This may be changed in the future.
+//
+// Example usage:
+//     
+//     void MyCallback(Callback* pCallback, void* pContext, uint64_t absoluteValue, uint64_t deltaValue)
+//     {
+//         printf("%llu nanoseconds have passed since the last callback.\n");
+//     }
+//
+//     int main(int, char**)
+//     {
+//         // Manager setup
+//         CallbackManager callbackManager;
+//         SetCallbackManager(&callbackManager);
+//         callbackManager.Init(false, false);    // Run in synchronous mode.
+//
+//         // Create and register a couple callbacks.
+//         Callback callbackA(MyCallback, NULL, 100000, 0);
+//         Callback callbackB(MyCallback, NULL, 300000, 0);
+//
+//         callbackManager.Add(&callbackA, false);  // periodic callback.
+//         callbackManager.Add(&callbackB, true);   // one-shot callback.
+//
+//         // Run the app.
+//         while(!ShouldQuit())
+//             callbackManager.Update();  // If we ran in asunchronous (threaded) mode, we wouldn't need to call this.
+//
+//         return 0;
+//     }
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_EACALLBACK_H
+#define EASTDC_EACALLBACK_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EARandom.h>
+#include <eathread/eathread_atomic.h>   // Used even if EASTDC_THREADING_SUPPORTED is zero, as it's still present. Just no actual threads.
+#if EASTDC_THREADING_SUPPORTED
+	#include <eathread/eathread_thread.h>
+	#include <eathread/eathread_mutex.h>
+#endif
+
+
+#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4371) // Layout of class may have changed from a previous version of the compiler due to better packing of member.
+	#pragma warning(disable: 4251) // class (some template) needs to have dll-interface to be used by clients.
+	#pragma warning(disable: 4121) // 'EA::StdC::CallbackT<T>' : alignment of a member was sensitive to packing.
+#endif
+
+
+namespace EA
+{
+	namespace StdC
+	{
+		class ICallbackManager;
+		class CallbackManager;
+
+
+		///////////////////////////////////////////////////////////////////////////
+		/// Callback
+		///
+		/// This is the user class for a callback.
+		///
+		class EASTDC_API Callback
+		{
+		public:
+			/// Defines the prototype of a function that can be used with a callback. 
+			/// absoluteValue is a value of total of time/ticks passed since the system have been started.
+			/// deltaValue is the value of time/ticks passed since last callback.
+			/// If the user enables RefCounting then this function can be called with
+			/// absoluteValue = kMessageAddRef or kMessageRelease instead of a time value.
+			/// Your callback function may safely call back into the CallbackManager.
+			typedef void (*CallbackFunctionType)(Callback* pCallback, void* pContext, uint64_t absoluteValue, uint64_t deltaValue);
+			
+			/// The running mode 
+			enum Mode
+			{
+				kModeAsync,    /// Asynchronous/threaded callbacks.
+				kModeSync      /// Synchronous/polled callbacks.
+			};
+			
+			/// The type of event to trigger a callback
+			enum Type 
+			{
+				kTypeTime,      /// For callbacks based on time in nanoseconds.
+				kTypeTick,      /// For callbacks based on a number of passed ticks. Every time the CallbackSystem::Update function is called (usually once per application loop), the tick count is incremented.
+				kTypeUserEvent  /// For callbacks based on some external user event (e.g. screen vblank (vertical retrace)). CallbackSystem::OnUserEvent needs to be called whenever such an event occurs.
+			};
+
+			enum Message
+			{
+				kMessageAddRef  = 0,    // If your callback is called with timeNS == kMessageAddRef, then this means you are being notified of first usage. See bEnableRefCount.
+				kMessageRelease = 1     // If your callback is called with timeNS == kMessageRelease, then this means you are being notified of last usage. See bEnableRefCount.
+			};
+
+			// static const bool kPeriodic = false; /// kPeriodic is a synonym for 'false', which is how it's used in the API.
+			// static const bool kOneShot  = true;  /// kOneShot is a synonym for 'true', which is how it's used in the API.
+
+		public:
+			/// Constructor
+			Callback();
+
+			/// Constructor
+			/// See SetFunctionInfo for relevant documentation.
+			Callback(CallbackFunctionType pCallbackFunction, void* pCallbackArgument, 
+					 uint64_t periodNs, uint64_t precisionNs = 500000, 
+					 Type type = kTypeTime, bool bEnableRefCount = false);
+
+			virtual ~Callback();
+
+			/// Sets the function which is called when the time/tick/event count expire. 
+			/// Note that if the in asynch mode, the callback could occur in a different 
+			/// thread from the thread that started resumed it.
+			///
+			/// If bEnableRefCount is true, kMessageAddRef will be sent immediately
+			/// and kMessageRelease will be sent immediately after callback is stopped.
+			/// For a one-shot timer, the kMessageRelease is sent immediately after the callback 
+			/// completes. For timers that are never stopped, the kMessageRelease is called when 
+			/// the associated CallbackManager is shut down or when you call Stop. You should use 
+			/// the reference counting system if there is any chance there is an opportunity for 
+			/// multithreaded race conditions regarding the lifetime of called code and its data.
+			bool SetFunctionInfo(CallbackFunctionType pCallbackFunction, void* pCallbackArgument, bool bEnableRefCount);
+
+			/// Sets new function information.
+			/// This can be called at any time, including after the callback is already started.
+			void GetFunctionInfo(CallbackFunctionType& pCallbackFunction, void*& pCallbackArgument) const;
+
+			/// Calls the callback function directly.
+			/// You don't need to call this function unless you want to manually trigger a callback ad-hoc. 
+			void Call(uint64_t absoluteValue, uint64_t deltaValue);
+			
+			/// Gets the period value in nanoseconds/ticks/events.
+			uint64_t GetPeriod() const;
+
+			/// Sets the period of the callback in nanoseconds/ticks/events. For a periodic callback, 
+			/// this is the time between repetitive callbacks. For a one-shot callback, this is the time
+			/// before its call. Changing the period has effect only after the next callback or if 
+			/// the user calls Stop and then Start to restart the callback.
+			///
+			/// As of this writing, Callback periods times refer to the time from one
+			/// callback to the next callback, and not in absolute time. For example,
+			/// If you request a callback with a period of 100 ms and the first one comes
+			/// at 105 ms, the second one is targeted to arrive 100 ms later than 105 ms
+			/// (i.e. 205 ms) and not at 200 ms. Thus the system doesn't try to catch up 
+			/// if it gets behind, nor does it slow down if it gets ahead.
+			bool SetPeriod(uint64_t nPeriodNs);
+
+			/// Gets the user-specified precision of the callback in nanoseconds/ticks/events.
+			/// Lower (i.e. more accurate) precision values may incur increased execution overhead
+			/// on some systems, though usually it doesn't.
+			uint64_t GetPrecision() const;
+
+			/// The callback will occur every GetPeriod() +/- GetPrecisison() units of nanoseconds/ticks/events.
+			/// The default precision is 0, which means to be as accurate as possible. This may or may
+			/// not incur any extra overhead, depending on the circumstances, though in most cases it
+			/// doesn't. One benefit of setting a non-zero precision is that in the case of there
+			/// being multiple callbacks of equal periods setting a non-zero precision results in some
+			/// slight randomization of the callback times and avoids all the calls happening at once
+			/// and causing an execution pause (lack of smoothness) in the app.
+			bool SetPrecision(uint64_t nPrecisionNs);
+
+			/// Activates the callback.
+			/// If pCallbackManager is NULL, the default CallbackManager is used.
+			/// 'bOneShot' controls whether the callback is a call-once or a periodic kind.
+			/// The return value indicates whether or not the callback is started. 
+			/// If the function is already Started then calling it a second time will
+			/// result in a true return value. Calling Start with bOneShot=true twice in 
+			/// a row will result in a true return value but only one callback and not two.
+			/// It's possible to start in periodic (not one-shot) mode and Stop the callback at any point.
+			bool Start(ICallbackManager* pCallbackManager, bool bOneShot);
+
+			/// Stops callback function being called. In asynch mode it is theoretically
+			/// possible to receive a callback very shortly after calling Stop, as a call to 
+			/// your callback might already be 'in flight' as you are calling Stop. If you 
+			/// need to make sure this doesn't happen, you can check the IsStarted 
+			void Stop();
+
+			/// Returns true if the callback has been started (is running).
+			/// If the callback is a one-shot, this returns true if the callback has been 
+			/// started but its one timeup hasn't occurred yet.
+			bool IsStarted() const;
+			bool IsRunning() const { return IsStarted(); }
+
+			/// Sets running mode (kModeAsync or kModeSync).
+			/// Asynch mode will result in being called back from a different thread.
+			/// Changes won't take effect until the next time the callback is Started. 
+			//bool SetMode(Mode mode);
+
+			/// Gets the running mode (kModeAsync or kModeSync).
+			//Mode GetMode() const;
+
+			/// Sets the callback type (kTypeTime, kTypeTick, or kTypeUserEvent).
+			/// kTypeTime means that period/precision functions refer time in nanoseconds, 
+			/// while ticks are just a snapshot of the count of times called.
+			bool SetType(Type type);
+
+			/// Gets the callback type.
+			/// Changes won't take effect until the next time the callback is Started. 
+			Type GetType() const;
+
+			/// Manually sends the callback function kMessageAddRef.
+			/// You normally don't need to manually call this function but should rather
+			/// let the Callback instance call it as needed.
+			void AddRefCallback();
+
+			/// Manually sends the callback function kMessageRelease.
+			/// You normally don't need to manually call this function but should rather
+			/// let the Callback instance call it as needed.
+			void ReleaseCallback();
+
+		protected:
+			friend class CallbackManager;
+
+			uint64_t                mPeriod;                /// Period in units that match Type (nanoseconds if Type is kTypeTime).
+			uint64_t                mPrecision;             /// Precision in units that match Type (nanoseconds if Type is kTypeTime).
+			ICallbackManager*       mpCallbackManager;      /// The manager we use for callbacks.
+			CallbackFunctionType    mpFunction;             /// The user function we call.
+			void*                   mpFunctionArg;          /// The context we pass to the user function.
+			Type                   mType;                  /// One of enum Type.
+			#if EASTDC_THREADING_SUPPORTED
+			EA::Thread::AtomicInt32 mbStarted;              /// True if there is an active callback.
+			#else
+			int32_t                 mbStarted;
+			#endif
+			bool                    mbOneShot;              /// True if this is a one-shot event.
+			bool                    mbEnableRefCount;       /// If true, the callback is notified before first usage and after last usage. Allows for safe lifetime maintentance of callback in the case that it has a dynamic lifetime.
+
+		private: // Internal data owned by CallbackManager.
+			int64_t                 mNextCallbackEvent;     /// Time, tick count, or user event count of next callback we should do.
+			int64_t                 mLastCallbackEvent;     /// Time, tick count, or user event count of the previous callback we did.
+
+		}; // class Callback
+
+
+
+		///////////////////////////////////////////////////////////////////////////
+		/// CallbackT
+		///
+		/// This is a template class based on Callback. In addition to callbacks 
+		/// based on static/global functions, this class allows one to use class 
+		/// member functions as callbacks.
+		///
+		/// Example usage:
+		///     // In this example, MyFunction will be called very 1000 nanoseconds.
+		/// 
+		///     class SomeClass : public EA::CallbackT<SomeClass>
+		///     {
+		///         SomeClass() 
+		///           : EA::StdC::CallbackT<SomeClass>(MyFunction, this, 1000)
+		///         {
+		///             Start(NULL, false);
+		///         }
+		///
+		///         void MyFunction(uint64_t absoluteValue, uint64_t deltaValue)
+		///         { 
+		///             // Do something. If mType is kTypeTime, values are in nanoseconds. Else they are event counts.
+		///         }
+		///     };
+		///
+		template <typename T>
+		class CallbackT : public Callback
+		{
+		public:
+			typedef CallbackT<T> this_type;
+
+			typedef void (T::*CallbackFunctionTypeT)(Callback* pCallback, uint64_t absoluteValue, uint64_t deltaValue);
+			
+			CallbackT()
+				: mpMemberFunctionObject(NULL), mpMemberFunction(NULL) { }
+
+			CallbackT(CallbackFunctionTypeT pMemberFunc, T* pMemberFuncObject, uint64_t periodNs, 
+					  uint64_t precisionNs = 500000, Type type = kTypeTime, bool bEnableRefCount = false)
+				: Callback(NULL, NULL, periodNs, precisionNs, type)
+			{
+				SetFunctionInfo(pMemberFunc, pMemberFuncObject, bEnableRefCount);
+			}
+
+			bool SetFunctionInfo(CallbackFunctionTypeT function, T* pT, bool bEnableRefCount)
+			{
+				mpMemberFunction       = function;
+				mpMemberFunctionObject = pT;
+
+				return Callback::SetFunctionInfo(generic_callback, this, bEnableRefCount);
+			}
+
+			void GetFunctionInfo(CallbackFunctionTypeT& function, T*& pT) const
+			{
+				function = mpMemberFunction;
+				pT       = mpMemberFunctionObject;
+			}
+
+		protected:
+			static void generic_callback(Callback* pCallback, void* /*arg*/, uint64_t absoluteValue, uint64_t deltaValue)
+			{
+				this_type* pThis = static_cast<this_type*>(pCallback);
+
+				if(pThis && pThis->mpMemberFunctionObject && pThis->mpMemberFunction)                    
+				   (pThis->mpMemberFunctionObject->*pThis->mpMemberFunction)(pCallback, absoluteValue, deltaValue);
+			}
+
+		protected:
+			T*                    mpMemberFunctionObject;      
+			CallbackFunctionTypeT mpMemberFunction;      
+		};
+
+
+
+		///////////////////////////////////////////////////////////////////////////
+		/// ICallbackManager
+		///
+		class EASTDC_API ICallbackManager
+		{
+		public:
+			/// Virtual destructor.
+			virtual ~ICallbackManager() { }
+
+			/// This function needs to be called regularly (every frame) from the main application loop.
+			virtual void Update() = 0;
+
+			/// This function needs to be called by the application whenever a callback driving event occur.
+			virtual void OnUserEvent() = 0;
+
+			/// Returns time in nanoseconds as the Callback system sees it
+			virtual uint64_t GetTime() = 0;
+
+			/// Add a new callback
+			virtual bool Add(EA::StdC::Callback* pCallback, bool bOneShot) = 0;
+
+			/// Remove a callback
+			virtual bool Remove(EA::StdC::Callback* pCallback) = 0;
+		};
+
+		// Global default CallbackManager.
+		// This is a global default CallbackManager.
+		// Your application doesn't need to call SetCallbackManager unless the you want to 
+		// call the Callback::Start function with a NULL (default) CallbackManager.
+		EASTDC_API ICallbackManager* GetCallbackManager();
+		EASTDC_API void              SetCallbackManager(ICallbackManager* pCallbackManager);
+
+
+
+		///////////////////////////////////////////////////////////////////////////
+		/// CallbackManager
+		///
+		/// Maintains a set of Callback instances.
+		///
+		/// As of this writing, Callback periods times refer to the time from one
+		/// callback to the next callback, and not in absolute time. For example,
+		/// If you request a callback with a period of 100 ms and the first one comes
+		/// at 105 ms, the second one is targeted to arrive 100 ms later than 105 ms
+		/// (i.e. 205 ms) and not at 200 ms. Thus the system doesn't try to catch up 
+		/// if it gets behind, nor does it slow down if it gets ahead.
+		/// To consider: Make a flag that enables the behaviour of going by absolute
+		/// time instead of inter-callback time. Note that such a feature would have
+		/// to watch out for getting too far behind that callbacks get backlogged.
+		/// 
+		/// CallbackManager uses 64 bit integers to measure nanoseconds of passed   
+		/// time. The CallbackManager can theoretically run for 290 years without 
+		/// the integers overflowing.
+		///
+		class EASTDC_API CallbackManager : public ICallbackManager
+		{
+		public:
+			/// CallbackManager
+			/// No siginficant processing is done in this constructor. All initialization
+			/// code is done in the Init function.
+			CallbackManager();
+		   ~CallbackManager();
+
+			/// This needs to be called in order to use CallbackManager.
+			/// If bAsync is true then the CallbackManager is run in a separate thread
+			/// and callbacks are made to user code from that thread automatically.
+			/// If bAsync is false then the user must periodically call Update to 
+			/// do processing and callbacks are made to user code from the current thread.
+			/// You can do manual callbacks even if Async (threaded) mode is enabled.
+            bool Init(bool bAsync, 
+                      bool bAsyncStart = false
+			#if EASTDC_THREADING_SUPPORTED
+                      , EA::Thread::ThreadParameters threadParam = EA::Thread::ThreadParameters()
+			#endif
+	        );
+
+			/// This needs to be called by application on shutdown to deinitialize 
+			/// the CallbackManager. It cancels and unregisters any callbacks and returns 
+			/// CallbackManager to the newly constructed state (i.e. before Init).
+			void Shutdown();
+
+			/// This must be called by the application on every iteration of the main loop if
+			/// async (threaded) mode is not used. However, this function can also be called 
+			/// if threaded mode is used.
+			/// This function calls user-registered timers which have expired.
+			void Update(); 
+
+			/// Returns the number of ticks (calls to Update) that have occurred.
+			uint64_t GetTickCounter() { return (uint64_t)mTickCounter; }
+
+			/// This must be called by the application on every user event whatever that might be.
+			/// An alternative way would be to use the CallbackManager class below.
+			void OnUserEvent();
+
+			// Returns the number of times OnUserEvent has been called.
+			uint64_t GetUserEventCounter() { return (uint64_t)mUserEventCounter; }
+
+			/// Returns time in nanoseconds as the Callback system sees it
+			uint64_t GetTime();
+
+			/// Add a new callback
+			bool Add(EA::StdC::Callback* pCallback, bool bOneShot);
+
+			/// Remove a callback. 
+			/// Stops the Callback if it is started.
+			bool Remove(EA::StdC::Callback* pCallback);
+
+			/// GetStopwatch
+			/// This is a debug function. Don't call mutating functions on it (e.g. Stop).
+			/// To do: Remove this function once this class is debugged.
+			EA::StdC::Stopwatch& GetStopwatch() { return mStopwatch; }
+
+			#if EASTDC_THREADING_SUPPORTED
+				// Manipulate the thread used for async (i.e. threaded) execution.
+				EA::Thread::Thread& GetThread(); 
+				void Lock();
+				void Unlock();
+			#endif
+
+		protected:
+			//struct CallbackInfo
+			//{
+			//    Callback* mpCallback;           // The user Callback object associated with this info.
+			//    int64_t   mNextCallbackEvent;   // Time, tick count, or user event count of next callback we should do.
+			//    int64_t   mLastCallbackEvent;   // Time, tick count, or user event count of the previous callback we did.
+			//    bool      mbOneShot;            // If true then we Stop the callback after it's called once.
+			//
+			//    CallbackInfo() : mpCallback(NULL), mNextCallbackEvent(0), mLastCallbackEvent(0), mbOneShot(false){}
+			//};
+
+			// We implement a tiny vector class.
+			class EASTDC_API CallbackVector
+			{
+			public:
+				typedef Callback*   value_type;
+				typedef value_type* iterator;
+
+				CallbackVector();
+			   ~CallbackVector();
+			  
+				void        reserve(size_t)                 { }
+				void        clear()                         { mpEnd = mpBegin; }
+				bool        empty() const                   { return mpBegin == mpEnd; }
+				iterator    begin()                         { return mpBegin; }
+				iterator    end()                           { return mpEnd; }
+				value_type& back()                          { return *(mpEnd - 1); }
+				size_t      size() const                    { return (size_t)(mpEnd - mpBegin); }
+				value_type& operator[](size_t i)            { return mpBegin[i]; }
+				iterator    erase(iterator it);
+				iterator    push_back(value_type value);
+
+			protected:
+				value_type* mpBegin;
+				value_type* mpEnd;
+				value_type* mpCapacity;
+				value_type  mLocalBuffer[8]; // Initial local buffer capable of holding some pointers without us needing to hit the heap.
+			};
+
+		protected:
+			static intptr_t RunStatic(void* pContext) { return static_cast<CallbackManager*>(pContext)->Run(); }
+			intptr_t        Run();
+			void            UpdateInternal(int64_t& curTick, int64_t& curTime, int64_t& curUserEvent);
+			bool            StartThread();
+			void            StopThread();
+
+			CallbackManager(const CallbackManager&); // Declared but not defined.
+			void operator=(const CallbackManager&);  // Declared but not defined.
+
+		protected:
+			CallbackVector           mCallbackArray;                // Our current callbacks.
+			EA::StdC::Stopwatch      mStopwatch;                    // 
+			EA::Thread::AtomicInt64  mTickCounter;                  // 
+			EA::Thread::AtomicInt64  mUserEventCounter;             // 
+			bool                     mbInitialized;                 // 
+			volatile bool            mbRunning;                     // 
+			bool                     mbAsync;                       // This is whether the processor is in a separate thread.
+			EA::StdC::RandomFast     mRandom;                       // 
+			double                   mNSecPerTick;                  // Used for guessing for how long to sleep our thread until the next callback time arrives. We need to know this because some events are based on the number of ticks (number of times Update is called), while others are based on time.
+			int64_t                  mNSecPerTickLastTimeMeasured;  // Last time when average nsec/tick was measured.
+			int64_t                  mNSecPerTickLastTickMeasured;  // Last time when average nsec/tick was measured.
+			int64_t                  mNextCallbackEventTime;        // The next earliest time callback event. If we have five registered user callbacks, we want to wake up whenever the soonest one is due.
+			int64_t                  mNextCallbackEventTick;        // The next earliest tick callback event.
+
+			#if EASTDC_THREADING_SUPPORTED
+				EA::Thread::Mutex       mMutex;
+				EA::Thread::Thread      mThread;
+				EA::Thread::AtomicInt32 mbThreadStarted;
+                EA::Thread::ThreadParameters mThreadParam;
+			#endif
+		};
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+#endif // Header include guard
+
+
+

+ 720 - 0
include/EAStdC/EADateTime.h

@@ -0,0 +1,720 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Implements a suite of functionality to manage dates, times, and calendars.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EADATETIME_H
+#define EASTDC_EADATETIME_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/Int128_t.h>
+
+#if EASTDC_TIME_H_AVAILABLE
+	#include <time.h>
+#else
+	typedef int64_t time_t;
+	typedef int32_t suseconds_t;
+	typedef int64_t clock_t;
+
+	struct tm
+	{
+		int tm_sec;
+		int tm_min;
+		int tm_hour;
+		int tm_mday;
+		int tm_mon;
+		int tm_year;
+		int tm_wday;
+		int tm_yday;
+		int tm_isdst;
+	};
+
+	time_t time(time_t*);
+	tm*    gmtime(const time_t*);
+	tm*    localtime(const time_t*);
+#endif
+
+#if   defined(EA_PLATFORM_MICROSOFT)
+	#if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+		typedef int32_t suseconds_t;
+		#define EASTDC_TIMEVAL_NEEDED
+		#define EASTDC_TIMEZONE_NEEDED
+	#else
+		struct timeval;                 // We have a problem: Microsoft defines timeval, but only within the Windows header system.
+		typedef time_t suseconds_t;     // And we have a rule against #including windows headers within header files. So we forward
+		#define EASTDC_TIMEZONE_NEEDED  // declare timeval and expect the user to #include <WinSock2.h> in order to use it.
+	#endif
+
+#elif EASTDC_SYS_TIME_H_AVAILABLE
+	#include <sys/time.h>           // Defines timeval and timezone
+	struct timezone_ : public timezone{};
+
+#elif EASTDC_SYS__TIMEVAL_H_AVAILABLE
+	#include <sys/_timeval.h>       // Defines timeval
+	#define EASTDC_TIMEZONE_NEEDED
+
+#else
+	#define EASTDC_TIMEVAL_NEEDED
+	#define EASTDC_TIMEZONE_NEEDED
+#endif
+
+
+#ifdef EASTDC_TIMEVAL_NEEDED
+	struct timeval {
+		time_t      tv_sec;     // seconds
+		suseconds_t tv_usec;    // microseconds
+	};
+#endif
+
+#ifdef EASTDC_TIMEZONE_NEEDED
+	struct timezone_ {          // Defined as timezone_ instead of timezone because otherwise it conflicts with some platforms' existing identifiers.
+		int tz_minuteswest;     // This is the number of minutes west of GMT.
+		int tz_dsttime;         // If nonzero, daylight savings time applies during some part of the year. 
+	};
+#endif
+
+
+
+// Forward declare the Microsoft time structs. We do this for all platforms, 
+// including non-Microsoft platforms in order to simply the code. This won't
+// result in excess code as these functions go away when unused.
+struct _FILETIME;
+struct _SYSTEMTIME;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_UTC_TIME_AVAILABLE
+//
+// Defined as 0 or 1. It's value is fixed based on the platform.
+// This is a read-only value which is set by this module.
+// If you become aware of a way to get UTC time for a platform which 
+// currently is flagged as unavailable here, notify the package owners.
+// For platforms where UTC time is not available, UTC time is reported to 
+// be the same as local time. If UTC time is not available, then time 
+// zone information is by definition not available either.
+//
+#if !defined(EASTDC_UTC_TIME_AVAILABLE)
+		#define EASTDC_UTC_TIME_AVAILABLE 1
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+	// Used for Parameter values (see below)
+	static const uint32_t kDateTimeDefault = 0xffffffff;
+	static const uint32_t kDateTimeIgnored = 0xffffffff;
+
+	/// Month
+	enum Month
+	{
+		kMonthUnknown        =  0,
+		kMonthJanuary        =  1,
+		kMonthFebruary       =  2,
+		kMonthMarch          =  3,
+		kMonthApril          =  4,
+		kMonthMay            =  5,
+		kMonthJune           =  6,
+		kMonthJuly           =  7,
+		kMonthAugust         =  8,
+		kMonthSeptember      =  9,
+		kMonthOctober        = 10,
+		kMonthNovember       = 11,
+		kMonthDecember       = 12
+	};
+
+	enum DayOfMonth
+	{
+		kDayOfMonthUnknown   =  0,
+		kDayOfMonthMin       =  1,
+		kDayOfMonthMax       = 31   /// The actual max month is dependent on which month is being referred to.
+	};
+
+	enum DayOfWeek
+	{
+		kDayOfWeekUnknown    =  0,
+		kDayOfWeekSunday     =  1,
+		kDayOfWeekMonday     =  2,
+		kDayOfWeekTuesday    =  3,
+		kDayOfWeekWednesday  =  4,
+		kDayOfWeekThursday   =  5,
+		kDayOfWeekFriday     =  6,
+		kDayOfWeekSaturday   =  7
+	};
+
+	/// TimeFrame
+	enum TimeFrame
+	{
+		kTimeFrameUnknown = 0,  /// Unspecified time frame.
+		kTimeFrameUTC     = 1,  /// Universal Coordinated Time. This is the time based on the time zone at Greenwich, England.
+		kTimeFrameLocal   = 2   /// Same time as current machine.
+	};
+
+	/// TimeZone
+	/// Standard time zone definitions, with their values corresponding to the nmuber of hours they are
+	/// off relative to UTC (Universal Coordinated Time, which is the time at Greenwich England). 
+	/// Note, for example, that kTimeZonePacific is -8 hours relative to Greenwich, England.
+	/// It should be noted that time zone biases (e.g. central australia) are properly represented in 
+	/// seconds and not hours, as some time zone biases relative to UTC are on half-hour increments. 
+	/// Such time zones are not listed below, as the list below is merely the UTC standard 24 hour list.
+	enum TimeZone
+	{
+		kTimeZoneEniwetok    = -12,
+		kTimeZoneSamoa       = -11,
+		kTimeZoneHawaii      = -10,
+		kTimeZoneAlaska      =  -9,
+		kTimeZonePacific     =  -8,
+		kTimeZoneMountain    =  -7,
+		kTimeZoneCentral     =  -6,
+		kTimeZoneEastern     =  -5,
+		kTimeZoneAltantic    =  -4,
+		kTimeZoneBrazilia    =  -3,
+		kTimeZoneMidAtlantic =  -2,
+		kTimeZoneAzores      =  -1,
+		kTimeZoneGreenwich   =   0,
+		kTimeZoneRome        =  +1,
+		kTimeZoneIsrael      =  +2,
+		kTimeZoneMoscow      =  +3,
+		kTimeZoneBaku        =  +4,
+		kTimeZoneNewDelhi    =  +5,
+		kTimeZoneDhakar      =  +6,
+		kTimeZoneBangkok     =  +7,
+		kTimeZoneHongKong    =  +8,
+		kTimeZoneTokyo       =  +9,
+		kTimeZoneSydney      = +10,
+		kTimeZoneMagadan     = +11,
+		kTimeZoneWellington  = +12
+	};
+
+	/// Epoch
+	/// The use of an epoch is to provide a timeframe with which to work.
+	/// Most of the time you don't need to know about this.
+	/// https://en.wikipedia.org/wiki/Epoch_%28reference_date%29
+	enum Epoch
+	{
+		kEpochUnknown         =  0,
+		kEpochJulian          =  1,      /// Began at -4712/01/01/12:00:00 (Year 1858, January 1, noon).
+		kEpochGregorian       =  2,      /// Began at  1752/09/14/00:00:00 (Year 1752, January 1, midnight). Beginning of the Gregorian calendar.
+		kEpochModifiedJulian  =  3,      /// Began at  1858/11/17/00:00:00 (Year 1858, January 1, midnight). 2,400,000.5 days after Julian epoch began.
+		kEpoch1900            =  4,      /// Began at  1900/01/01/00:00:00 (Year 1900, January 1, midnight). Same epoch used by the Network Time Protocol.
+		kEpoch1950            =  5,      /// Began at  1950/01/01/00:00:00 (Year 1950, January 1, midnight). Used by some gaming systems.
+		kEpoch1970            =  6,      /// Began at  1970/01/01/00:00:00 (Year 1970, January 1, midnight). Same epoch used by C runtime library and Unix OS.
+		kEpoch2000            =  7,      /// Began at  2000/01/01/00:00:00 (Year 2000, January 1, midnight). Same epoch used by Apple file systems.
+		kEpochJ2000           =  8,      /// Began at  2000/01/01/11:58:55 (Year 2000, January 1, ~noon).    Coordinated Universal Time, also includes 816 milliseconds.
+		kEpochDateTime        =  9,      /// Began at  0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+		kEpochCount
+	};
+
+	/// Era
+	enum Era
+	{
+		kEraUnknown = 0,
+		kEraBC      = 1,
+		kEraAD      = 2
+	};
+
+	/// Parameter
+	/// Defines a date or time parameter.
+	enum Parameter
+	{
+		kParameterUnknown     =  0,
+		kParameterYear        =  1,     /// Refers to full year value, such as 1994, 2006, etc. but not a two digit value such as 94 or 04. The valid range is 0-INT_MAX.
+		kParameterMonth       =  2,     /// Refers to month of year, starting with 1 for January. The valid range is 1-12.
+		kParameterWeekOfYear  =  3,     /// Refers to the week of the year, starting with 1 for the week of January 1. The valid range is 1-52.
+		kParameterWeekOfMonth =  4,     /// Refers to the week of the month, starting with 1 for the first week. The valid range is 1-5.
+		kParameterDayOfYear   =  5,     /// Refers to a day of the year, starting with 1 for January 1st. The valid range is 1-366.
+		kParameterDayOfMonth  =  6,     /// Refers to the day of the month, starting with 1 for the first of the month. The valid range is 1-31.
+		kParameterDayOfWeek   =  7,     /// Refers to the day of the week, starting with 1 for Sunday. The valid range is 1-7.
+		kParameterHour        =  8,     /// Refers to the hour of a day in 24 hour format, starting with 0 for midnight. The valid range is 0-23.
+		kParameterMinute      =  9,     /// Refers to the minute of the hour, starting with 0 for the first minute. The valid range is 0-59.
+		kParameterSecond      = 10,     /// Refers to the second of the minute, starting with 0 for the first second. The valid range is 0-60, with the range usually being 0-59, but the occasional leap second could cause it to be 60.
+		kParameterNanosecond  = 11      /// Refers to the nanosecond of the second. The valid range is 0-999999999. 
+	};
+
+	/// Conversions
+	/// Defines useful conversion multipliers between seconds, minutes, hours, and days.
+	/// Conversions are not defined for some entities (e.g. days per year) because the 
+	/// value changes based on the particular year.
+	enum Conversions
+	{
+		kSecondsPerMinute =    60,  // We ignore than on rare occasions in real time there are 61 seconds in a minute when there is a leap second.
+		kSecondsPerHour   =  3600,
+		kSecondsPerDay    = 86400,
+		kMinutesPerHour   =    60,
+		kMinutesPerDay    =  1440,
+		kHoursPerDay      =    24,
+		kDaysPerWeek      =     7,
+		kWeeksPerYear     =    52,
+		kMonthsPerYear    =    12
+	};
+
+
+	/// DateTimeParameters
+	/// Specifies a struct that holds one of each of the date/time Parameter type.
+	/// This struct is a bit like the C tm struct, though with more flexibility and precision.
+	/// Any value can be kDateTimeIgnored to indicate that it isn't used. 
+	/// Some parameters are potentially mutually exclusive with others (e.g. mMonth and mWeekOfYear).
+	/// The user is expected to avoid such conflicts, or else the code specifies how it handles
+	/// such conflicts.
+	/// All values default to kDateTimeIgnored.
+	/// Values can be negative in order to identify date/time deltas as opposed to absolute date/time values.
+	/// Values can be outside their normal range in order to indicate date/time deltas greater 
+	/// than normal. For example, mMonth can be -16 to indicate subtraction of 16 months. Negative or 
+	/// out-of-range values may have no meaning when DateTimeParameters is used to indicate absolute date/time.
+	struct DateTimeParameters
+	{
+		DateTimeParameters();
+
+		int32_t mYear;
+		int32_t mMonth;
+		int32_t mWeekOfYear;
+		int32_t mWeekOfMonth;
+		int32_t mDayOfYear;
+		int32_t mDayOfMonth;
+		int32_t mDayOfWeek;
+		int32_t mHour;
+		int32_t mMinute;
+		int32_t mSecond;
+		int32_t mNanosecond;
+	};
+
+
+	/// DateTime
+	/// Represents date and time in a single class. Unlike other date/time systems, 
+	/// this class doesn't have a strictly limited resolution, due to its use of
+	/// 64 bits (instead of 32 bits) of storage for the date/time value.
+	/// 
+	/// DateTime is internally represented as nanoseconds since 0000/01/01/00:00:00 
+	/// (Year 0000, January 1, midnight). Thus the beginning of time is considered
+	/// to be this date. This is differnet from the old Unix concept of time starting
+	/// at 1970 and is so because it provides more flexibility and portably allows 
+	/// for date representation prior to 1970. You can convert between time_t and DateTime 
+	/// seconds with the DateTimeSecondsToTimeTSeconds and TimeTSecondsSecondsToDateTime functions.
+	///
+	/// This class does not support the formatting of date and time strings for 
+	/// display to a users. Such functionality is outside the scope of this class
+	/// primarily due to the issue of multiple string encodings (e.g. ASCII and Unicode)
+	/// and due to the issue of multiple localization (e.g. English and French).
+	/// As separate module exists which implements string formatting.
+	///
+	/// For best practices regarding usage of DateTime, see the following discussion
+	///     http://stackoverflow.com/questions/2532729/daylight-saving-time-and-timezone-best-practices
+	/// 
+	class EASTDC_API DateTime
+	{
+	public:
+		static const uint32_t kValueDefault = kDateTimeDefault;
+		static const uint32_t kValueIgnored = kDateTimeIgnored;
+
+	public:
+		DateTime(TimeFrame timeFrame = kTimeFrameLocal)
+		  : mnSeconds(0), mnNanosecond(0){ Set(timeFrame); }
+
+		/// nSeconds/nNanosecond refers to elapsed time since 0000/01/01/00:00:00 
+		/// (Year 0000, January 1, midnight). This function is thus different from
+		/// the old Unix concept of time starting at 1970 and is so because it provides
+		/// more flexibility and portably allows for date representation prior to 1970.
+		/// You can convert between time_t and DateTime seconds with the DateTimeSecondsToTimeTSeconds
+		/// and TimeTSecondsSecondsToDateTime functions.
+		/// Nanosecond refers to a fraction of seconds.
+		DateTime(int64_t nSeconds, uint32_t nNanosecond = 0)
+		  : mnSeconds(nSeconds), mnNanosecond(nNanosecond) { }
+
+		// See the SetNanoseconds function for the specification of nanoseconds.
+		DateTime(const int128_t& nanoseconds)
+		  : mnSeconds(0), mnNanosecond(0) { SetNanoseconds(nanoseconds); }
+
+		DateTime(const DateTime& dateTime) 
+		  : mnSeconds(dateTime.mnSeconds), mnNanosecond(dateTime.mnNanosecond) { }
+
+		/// DateTime
+		/// Constructs a DateTime class object from some standard parameters.
+		/// To construct a DateTime class with a different set of parameter types,
+		/// you'll need to use the Set function or in odd cases do manual calculations. 
+		DateTime(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour = 0, 
+				 uint32_t nMinute = 0, uint32_t nSecond = 0, uint32_t nNanosecond = 0)
+		   : mnSeconds(0), mnNanosecond(0)
+			{ Set(nYear, nMonth, nDayOfMonth, nHour, nMinute, nSecond, nNanosecond); }
+
+		DateTime& operator=(const DateTime& dateTime)
+			{ mnSeconds = dateTime.mnSeconds; mnNanosecond= dateTime.mnNanosecond; return *this; }
+
+		/// Compare
+		/// This function compares this object with another DateTime object and returns an integer result.
+		/// The return value is the same as with the strcmp string comparison function. If this object is
+		/// less than the argument dateTime, the return value is < 0.
+		/// Both involved DateTime objects are assumed to be in the same time zone, and if you want to compare times
+		/// in different time zones then you will have to translate one of them to the other's time zone, at least 
+		/// until some day that explicit support for that is added to this package.
+		///
+		/// If bCompareDate is true  and bCompareTime is false, we compare the absolute day and nothing more precise.
+		/// If bCompareDate is false and bCompareTime is true,  we compare the time (seconds+nanoseconds) within a day.
+		/// If bCompareDate is true  and bCompareTime is true,  we compare absolute time (seconds+nanoseconds).
+		/// If bCompareDate is false and bComareTime  is false, we compare absolute time (seconds+nanoseconds).
+		/// Comparison operators are defined outside this class, though they use the Compare function to do their work.
+		int Compare(const DateTime& dateTime, bool bCompareDate = true, bool bCompareTime = true) const;
+
+		/// GetParameter
+		/// Gets the given parameter. If you want to get the year, you would call Get(kParameterYear).
+		uint32_t GetParameter(Parameter parameter) const;
+
+		/// SetParameter
+		/// Sets the given parameter. If you want to set the year to 1999, you would call Set(kParameterYear, 1999).
+		/// You have to be careful with the following parameters, becuase they adjust time in a relative way as opposed
+		/// to an absolute way, and thus the current date/time value may affect the resulting absolute date/time.
+		/// As a result the effects of these parameters when called with other paramters can be order-dependent. 
+		/// For example, setting kParameterDayOfWeek before kParameterDayOfMonth could result in a different month
+		/// than if you called them in reverse order.
+		///     kParameterDayOfYear, kParameterDayOfWeek, kParameterWeekOfYear, kParameterWeekOfMonth.
+		void SetParameter(Parameter parameter, uint32_t nValue);
+
+		/// Set
+		/// Sets the time based on the current time. If the timeFrame is kTimeFrameUTC, the time is set
+		/// to what the current UTC time is, which will be a fixed number of hours off of what the current
+		/// local time is.
+		void Set(TimeFrame timeFrame = kTimeFrameLocal, bool bSetNanoseconds = true);
+
+		/// Set
+		/// Sets the time based on various inputs. If any input is kValueIgnored (uint32_t)-1, then the 
+		/// input is ignored and the current value is used. If any of the cyclic inputs is beyond its 
+		/// valid range, the modulo of the value is used and the division of the value is added to the 
+		/// next higher bracket. For example, if the input nMinute is 65, then the minute used is 5 and 
+		/// 1 is added to the current hour value.
+		void Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour, 
+				 uint32_t nMinute, uint32_t nSecond, uint32_t nNanosecond = kValueIgnored);
+
+		/// AddTime
+		/// Allows you to increment (or decrement) the given parameter by the given nValue amount.
+		/// The amount can be less than zero and can be more than the conventional type limit. 
+		/// For example, if parameter = kParameterMinute, nValue can be any int64_t value and not
+		/// just in the range of 0 - 60.
+		void AddTime(Parameter parameter, int64_t nValue);
+
+		/// GetSeconds
+		/// Returns the DateTime internal representation. The internal representation is such that 
+		/// a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+		/// This function thus gets the number of seconds "since the beginning of time".
+		int64_t GetSeconds() const;
+
+		/// SetSeconds
+		/// Sets the DateTime internal representation. 
+		/// This is not the same as SetParameter(kParameterSeconds), which sets the current day's 
+		/// seconds and not the absolute seconds since the beginning of time like this function does.
+		/// This function thus sets the number of seconds "since the beginning of time".
+		void SetSeconds(int64_t nSeconds);
+
+		/// GetMilliseconds
+		/// Returns the DateTime internal representation. The internal representation is such that 
+		/// a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+		/// This function thus gets the number of milliseconds "since the beginning of time".
+		uint64_t GetMilliseconds() const;
+
+		/// SetMilliseconds
+		/// Sets the DateTime internal representation. 
+		/// This is not the same as SetParameter, which sets the current day's 
+		/// seconds and not the absolute seconds since the beginning of time like this function does.
+		/// This function thus sets the number of milliseconds "since the beginning of time".
+		void SetMilliseconds(uint64_t milliseconds);
+
+		/// GetNanoseconds
+		/// Note that this is not the same thing as GetParameter(kParamaterNanosecond), as GetParameter retrieves
+		/// the fraction of the current second in nanoseconds.
+		EA::StdC::int128_t GetNanoseconds() const;
+
+		/// SetNanoseconds
+		/// Sets the time based in absolute nanoseconds since 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+		/// Note that this is not the same thing as SetParameter(kParameterNanosecond), as SetParameter set the 
+		/// number of nanoseconds that have elapsed since the current second, whereas this function sets the 
+		/// number of nanoseconds that have elapsed since Jan 1, year 0000).
+		void SetNanoseconds(const EA::StdC::int128_t& nanoseconds);
+
+	protected:
+		int64_t  mnSeconds;     /// Time as seconds. The internal representation is such that a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight). This function thus gets the number of seconds "since the beginning of time".
+		uint32_t mnNanosecond;  /// The nanosecond value within the current second. In the range of [0, 999999999]. This variable is not an analogue of the DateTime mnSeconds member variable, as this is a fraction whereas that is absolute.
+
+	}; // class DateTime
+
+
+
+
+	// Utility functions
+	// The following utilities refer to the Gregorian calendar, which is the current 
+	// standard calendar. These form the basis for the implementation of a basic calendar.
+
+	/// GetTime
+	/// Returns the number of nanoseconds elapsed since January 1, 1970 UTC.
+	/// This is like the C time() function, except it returns nanoseconds.
+	/// However, there is a key difference between this function and the C time
+	/// function: this function returns actual real time progression in nanoseconds
+	/// and will always return a value that's the actual time progressed since 
+	/// it was first called. Thus time will never appear to go backwards, slow down,
+	/// or speed up. Thus this function cannot be used for accurate calendaring
+	/// like an desktop productivity app might want. For that you should use the 
+	/// GetTimeOfDay function instead. This function's time consistency is what
+	/// Posix refers to as CLOCK_MONOTONIC as opposed to CLOCK_REALTIME.
+	/// This function is not guaranteed to give results that are precisely aligned
+	/// with the results of GetTimeOfDay, as this function has different behaviour
+	/// guarantees.
+	/// Not all platforms have nanosecond-level precision, and in that case
+	/// a nanosecond value will be returned but it may be with precision 
+	/// that is lower.
+	/// You can use the TimeTSecondsSecondsToDateTime function to help convert 
+	/// GetTime values to seconds/nanoseconds used by the DateTime class.
+	EASTDC_API uint64_t GetTime();
+
+	/// GetTimePrecision
+	/// Returns the precision, in nanoseconds, of GetTime().
+	/// A return value of 1 means GetTime indeed returns nanosecond values with nanosecond precision.
+	/// A value of 1000 means that GetTime returns nanoseconds values with microsecond precision.
+	EASTDC_API uint64_t GetTimePrecision();
+
+	/// IsLeapYear
+	/// Returns true if the given year is a leap year.
+	EASTDC_API bool IsLeapYear(uint32_t nYear);
+
+	/// GetDaysInYear
+	/// Returns the number of days in the given year. The value will vary based on whether 
+	/// the year is a leap year or not.
+	EASTDC_API uint32_t GetDaysInYear(uint32_t nYear);
+
+	/// GetDaysInMonth
+	/// Returns the number of days in the given month. The value will vary based on the 
+	/// month and based on whether the year is a leap year. The input nMonth takes one
+	/// of enum Month or an integer equivalent.
+	EASTDC_API uint32_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear);
+
+	/// GetDayOfYear
+	/// The input nMonth takes one of enum Month or an integer equivalent.
+	/// The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
+	EASTDC_API uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear);
+
+	/// Convert4DigitTo2DigitYear
+	/// Note that two-digit years bring a number of problems; they are best used for text
+	/// display only and not for any internal processing.
+	inline int Convert4DigitTo2DigitYear(int nYear4)
+		{ return (nYear4 % 100); } 
+
+	/// Convert2DigitTo4DigitYear
+	/// This code returns a year in the 1900s if the input year is > 30 but returns
+	/// a year in the 2000s if the year is <= 68. This is merely a guess and in fact there
+	/// really is no good way to reliably convert a two digit year to a four digit year.
+	inline int Convert2DigitTo4DigitYear(int nYear2)
+		{ return nYear2 > 68 ? (1900 + nYear2) : (2000 + nYear2); } 
+
+	/// DateTimeSecondsToTimeTSeconds
+	/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+	/// time_t seconds are based on 1970/01/01/00:00:00.
+	inline int64_t DateTimeSecondsToTimeTSeconds(int64_t dateTimeSeconds)
+		{ return dateTimeSeconds - INT64_C(62135683200);}
+
+	/// TimeTSecondsSecondsToDateTime
+	/// time_t seconds are based on 1970/01/01/00:00:00.
+	/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+	inline int64_t TimeTSecondsSecondsToDateTime(int64_t TimeTSeconds)
+		{ return TimeTSeconds + INT64_C(62135683200);}
+
+	/// ConvertEpochSeconds
+	/// Converts seconds in an epoch to seconds in another epoch. You don't need to convert
+	/// the nanoseconds portion of a DateTime (e.g. dateTime.GetNanoseconds()), as that value
+	/// is a fractional second which is unchanged regardless of epoch. Recall that an epoch is
+	/// a starting timepoint, such as how the Unix Epoch (time_t == 0) starts at Jan 1, 1970.
+	/// This function extends and generalizes the functionality in DateTimeSecondsToTimeTSeconds and
+	/// TimeTSecondsSecondsToDateTime.
+	/// Returns seconds in the destination Epoch. Returns 0 if srcEpoch or destEpoch are invalid.
+	/// Example usage:
+	///     // Convert myDateTime to Unix time.
+	///     int64_t unixSeconds = ConvertEpochSeconds(kEpochDateTime, myDateTime.GetSeconds(), kEpoch1970);
+	EASTDC_API int64_t ConvertEpochSeconds(Epoch srcEpoch, int64_t srcSeconds, Epoch destEpoch);
+
+	/// GetCurrent
+	/// Returns the current year, month, hour, etc.
+	EASTDC_API uint32_t GetCurrent(Parameter parameter, TimeFrame timeFrame = kTimeFrameLocal);
+
+	/// IsDST
+	/// Returns true if the current time is daylight savings time. This function assumes 
+	/// that DST is valid for the given current locale. Some locales within the 
+	/// United States don't observe DST. Daylight savings time is changed every few 
+	/// years by the government, so it's probably best not to rely on this function
+	/// being accurate. The computer this software is running on may not know the 
+	/// current rules for daylight savings time.
+	EASTDC_API bool IsDST();
+
+	/// IsDSTDateTime
+	/// Returns IsDST for a DateTime seconds value and the current location.
+	/// The dateTimeSeconds argument is expected to be in Universal time.
+	/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight) (not time_t).
+	EASTDC_API bool IsDSTDateTime(int64_t dateTimeSeconds);
+
+	/// GetDaylightSavingsBias
+	/// Returns the number of seconds that the current time is daylight savings adjusted from 
+	/// the conventional time. Adding this value to the conventional time yields the time when
+	/// adjusted for daylight savings.
+	EASTDC_API int64_t GetDaylightSavingsBias();
+
+	/// GetTimeZoneBias
+	/// Returns the number of seconds that the local time zone is off of UTC.
+	/// Adding this value to the current UTC yields the current local time.
+	/// The return value will be zero in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
+	EASTDC_API int64_t GetTimeZoneBias();
+
+	/// GetTimeZoneName
+	/// Retrieves the name of the time zone. This is not a localizable function.
+	/// The supplied pName must have a capacity of at least kTimeZoneNameCapacity bytes.
+	/// For example names, see http://www.worldtimezone.com/wtz-names/timezonenames.html.
+	/// The returned string isn't guaranteed to match equivalent results from other platforms.
+	/// The return value will be false in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
+	enum { kTimeZoneNameCapacity = 64 };
+	EASTDC_API bool GetTimeZoneName(char8_t* pName, bool bDaylightSavingsName);
+
+	/// DateTimeToTm
+	/// Converts a DateTime to a C tm struct.
+	EASTDC_API void DateTimeToTm(const DateTime& dateTime, tm& time);
+
+	/// TmToDateTime
+	/// Converts a C tm struct to a DateTime.
+	EASTDC_API void TmToDateTime(const tm& time, DateTime& dateTime);
+
+
+	/// DateTimeToFileTime
+	/// Converts a DateTime to a FILETIME struct.
+	/// A FILETIME contains a 64-bit value representing the number of 100-nanosecond 
+	/// intervals since January 1, 1601 (UTC).
+	EASTDC_API void DateTimeToFileTime(const DateTime& dateTime, _FILETIME& time);
+
+	/// FileTimeToDateTime
+	/// Converts a FILETIME struct to a DateTime.
+	EASTDC_API void FileTimeToDateTime(const _FILETIME& time, DateTime& dateTime);
+
+
+	/// DateTimeToSystemTime
+	/// Converts a DateTime to a SYSTEMTIME struct.
+	EASTDC_API void DateTimeToSystemTime(const DateTime& dateTime, _SYSTEMTIME& time);
+
+	/// SystemTimeToDateTime
+	/// Converts a SYSTEMTIME struct to a DateTime.
+	EASTDC_API void SystemTimeToDateTime(const _SYSTEMTIME& time, DateTime& dateTime);
+
+
+	/// GetTimeOfDay
+	/// This is nearly identical to the Posix gettimeofday function, except this function adds a bUTC parameter to allow returning as local time instead of UTC.
+	/// The gettimeofday() function shall obtain the current time, expressed as seconds and microseconds 
+	/// since the Epoch, and store it in the timeval structure pointed to by tp. The resolution of the 
+	/// system clock is unspecified. If tzp is not a null pointer, the behavior is unspecified.
+	/// In other words: obtains the current time, expressed as seconds and microseconds since 00:00 Coordinated Universal Time (UTC), January 1, 1970.
+	/// Returns 0 upon success or -1 upon error.
+	/// Note that a timeval has the same meaning as time_t except that it contains sub-second information.
+	/// The bUTC parameter defines if the time is returned in UTC time (a.k.a. Greenwich Mean Time) or local time.
+	/// Recall that UTC refers to the time zone in Greenwich, Switzerland, whereas local time refers to whatever 
+	/// time zone the computer is currently in.
+	/// This function is a calendar function as opposed to being a time progression measurement function. As such it's possible
+	/// that the time reported by it may go backwards occasionally, as would be the case when the user's clock is changed.
+	/// For a time function that's meant to measure real time progression in a non-adjusting, use the GetTime function.
+	/// The time zone parameter may be NULL, in which case the time zone is not returned.
+	/// Time zone information will not be available in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
+	EASTDC_API int GetTimeOfDay(timeval* pTV, timezone_* pTZ, bool bUTC = true);
+
+
+	/// TimevalDifference
+	/// Calculates the result of TVA - TVB.
+	/// Returns 1 if TVA >= TVB, 0 if TVA == TVB, -1 if TVA < TVB. Much like strcmp().
+	/// Note that a timeval has the same meaning as time_t except that it contains
+	/// sub-second information.
+	EASTDC_API int TimevalDifference(const timeval* pTVA, const timeval* pTVB, timeval* pTVResult);
+
+
+	/// TimeLocale 
+	/// Allows user to override strings used by time and date formatting.
+	struct TimeLocale
+	{
+		const char* mAbbrevDay[7];      // e.g. "Sun", etc.
+		const char* mDay[7];            // e.g. "Sunday", etc.
+		const char* mAbbrevMonth[12];   // e.g. "Jan", etc.
+		const char* mMonth[12];         // e.g. "January", etc.
+		const char* mAmPm[2];           // e.g. "AM" and "PM".
+		const char* mDateTimeFormat;    // e.g. "%a %b %d %H:%M:%S %Y"
+		const char* mDateFormat;        // e.g. "%m/%d/%y"
+		const char* mTimeFormat;        // e.g. "%H:%M:%S"
+		const char* mTimeFormatAmPm;    // e.g. "%I:%M:%S %p"
+	};
+
+	/// Strftime
+	/// Converts tm struct to time string.
+	/// Has equivalent behaviour to the C strftime function as defined by the Posix Standard.
+	/// If the total number of resulting bytes including the terminating null byte is not more 
+	/// than timeStringCapacity, returns the number of bytes placed into the array pointed to 
+	/// by pTimeString, not including the terminating null byte. Otherwise, 0 is returned and 
+	/// the contents of the array are indeterminate. 
+	/// See http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html
+	/// This function doesn't guarantee locale-correctness. For localized time and date 
+	/// formatting, consider the EALocale package or Localizer package.11
+	EASTDC_API size_t Strftime(char* EA_RESTRICT pTimeString, size_t timeStringCapacity, const char* EA_RESTRICT pFormat, 
+								const tm* EA_RESTRICT pTime, const TimeLocale* EA_RESTRICT pTimeLocale = NULL);
+
+	/// Strptime
+	/// Converts time string to tm struct.
+	/// Has equivalent behaviour to the C strftime function as defined by the Posix Standard.
+	/// Returns a pointer to the character following the last character parsed. Otherwise, a null pointer is returned. 
+	/// See http://www.opengroup.org/onlinepubs/009695399/functions/strptime.html
+	/// This function doesn't guarantee locale-correctness. For localized time and date 
+	/// formatting, consider the EALocale package or Localizer package.
+	EASTDC_API char* Strptime(const char* EA_RESTRICT pTimeString, const char* EA_RESTRICT pFormat, 
+								tm* EA_RESTRICT pTime, const TimeLocale* EA_RESTRICT pTimeLocale = NULL);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DateTime inline operators
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA {
+  namespace StdC {
+
+	inline bool operator==(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) == 0; }
+
+	inline bool operator>(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) > 0; }
+
+	inline bool operator>=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) >= 0; }
+
+	inline bool operator<(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) < 0; }
+
+	inline bool operator<=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) <= 0; }
+
+	inline bool operator!=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
+		{ return dateTime1.Compare(dateTime2) != 0; }
+
+  } // namespace StdC
+} // namespace EA
+
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+

+ 1124 - 0
include/EAStdC/EAEndian.h

@@ -0,0 +1,1124 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAENDIAN_H
+#define EASTDC_EAENDIAN_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/Int128_t.h>
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) // VC++ >= v7.1
+	#include <stdlib.h> // for _byteswap_ushort, _byteswap_ulong
+	#pragma intrinsic(_byteswap_ushort, _byteswap_ulong) // Unless you do this, you will get linker errors with the DLL CRT.
+	#if defined(EA_PROCESSOR_X86_64)
+		#pragma intrinsic(_byteswap_uint64)
+	#endif
+#endif
+
+
+//   ---------------------------------------------------------------------------------------------------------------------------------------------------------
+//   | Big-endian version      | Little-endian version      | Purpose                               | Example Usage
+//   |========================================================================================================================================================
+//   | Swizzle                 | Swizzle                    | Unilaterally converts a value to the  | uint16_t x = Swizzle(0x1234);
+//   |                         |                            | opposite endian.                      | (x becomes 0x3412)
+//   |                         |                            |                                       |
+//   | ---------------------------------------------------------------------------------------------|---------------------------------------------------------
+//   | ReadFromBigEndian       | ReadFromLittleEndian       | Reads a value from a buffer with      | void* pBuffer;
+//   |                         |                            | a source endian-ness to a variable    | uint16_t x = ReadFromLittleEndianUint16(pBuffer);
+//   |                         |                            | of local endian.                      |
+//   | ---------------------------------------------------------------------------------------------|---------------------------------------------------------
+//   | WriteToBigEndian        | WriteToLittleEndian        | Writes a value from local endian      | void* pBuffer;
+//   |                         |                            | to a buffer of destination endian.    | uint32_t x;
+//   |                         |                            |                                       | WriteToLittleEndian(x, pBuffer);
+//   |--------------------------------------------------------------------------------------------------------------------------------------------------------
+//   | ToBigEndian             | ToLittleEndian             | Converts a variable from local        | uint64_t x;
+//   |                         |                            | endian to destination endian.         | x = ToLittleEndian(x);
+//   |                         |                            |                                       |
+//   | ---------------------------------------------------------------------------------------------|---------------------------------------------------------
+//   | FromBigEndian           | FromLittleEndian           | Converts a variable from a given      | uint32 x;
+//   |                         |                            | endian-ness to local endian-ness.     | x = FromBigEndian(x);
+//   |                         |                            |                                       |
+//   | ---------------------------------------------------------------------------------------------|---------------------------------------------------------
+//   | ToBigEndianConst        | ToLittleEndianConst        | Converts a constant from local        | uint32_t x = ToBigEndianConst(0x12345678);
+//   |                         |                            | endian to destination endian. Faster  |
+//   |                         |                            | than non-const and good for globals.  |
+//   | -------------------------------------------------------------------------------------------------------------------------------------------------------
+//   | FromBigEndianConst      | FromLittleEndianConst      | Converts a constant from a given      | uint16_t x = FromBigEndianConst(0x1234);
+//   |                         |                            | endian-ness to local endian-ness.     |
+//   |                         |                            | Faster than non-const and for globals.|
+//   |----------------------------------------------------------------------------------------------|---------------------------------------------------------
+
+
+namespace EA
+{
+	namespace StdC
+	{
+
+		/// Enum Endian
+		///
+		/// Allows a system to specify endian-ness. We recognize only big endian and 
+		/// little endian, as these are by far the most common kind of endian-ness.
+		///
+		enum Endian
+		{
+			kEndianBig       = 0,            /// Big endian.
+			kEndianLittle    = 1,            /// Little endian.
+			#ifdef EA_SYSTEM_BIG_ENDIAN
+				kEndianLocal = kEndianBig    /// Whatever endian is native to the machine.
+			#else 
+				kEndianLocal = kEndianLittle /// Whatever endian is native to the machine.
+			#endif
+		};
+
+
+		namespace detail
+		{
+			#if defined(EA_PROCESSOR_X86_64) && defined(_MSC_VER) 
+				EA_FORCE_INLINE uint16_t bswap16(uint16_t x) { return static_cast<uint16_t>(_byteswap_ushort(x)); }
+				EA_FORCE_INLINE uint32_t bswap32(uint32_t x) { return static_cast<uint32_t>(_byteswap_ulong(x)); }
+				EA_FORCE_INLINE uint64_t bswap64(uint64_t x) { return static_cast<uint64_t>(_byteswap_uint64(x)); }
+				#define EASTDC_LITTLE_ENDIAN_WITH_BSWAP 1
+			#elif defined(EA_PROCESSOR_X86_64) && (defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) 
+				EA_FORCE_INLINE uint16_t bswap16(uint16_t x) { return static_cast<uint16_t>(__builtin_bswap16(x)); }
+				EA_FORCE_INLINE uint32_t bswap32(uint32_t x) { return static_cast<uint32_t>(__builtin_bswap32(x)); }
+				EA_FORCE_INLINE uint64_t bswap64(uint64_t x) { return static_cast<uint64_t>(__builtin_bswap64(x)); }
+				#define EASTDC_LITTLE_ENDIAN_WITH_BSWAP 1
+			#endif
+
+			#if defined(EASTDC_LITTLE_ENDIAN_WITH_BSWAP)
+				EA_FORCE_INLINE int16_t bswap16(int16_t x) { return static_cast<int16_t>(bswap16(static_cast<uint16_t>(x))); }
+				EA_FORCE_INLINE int32_t bswap32(int32_t x) { return static_cast<int32_t>(bswap32(static_cast<uint32_t>(x))); }
+				EA_FORCE_INLINE int64_t bswap64(int64_t x) { return static_cast<int64_t>(bswap64(static_cast<uint64_t>(x))); }
+			#endif 
+
+
+			inline float FloatFromBitRepr(uint32_t bits)
+			{
+				union { float f; uint32_t u; } pun;
+				pun.u = bits;
+				return pun.f;
+			}
+			inline double DoubleFromBitRepr(uint64_t bits)
+			{
+				union { double d; uint64_t u; } pun;
+				pun.u = bits;
+				return pun.d;
+			}
+			inline uint32_t BitReprOfFloat(float f)
+			{
+				union { float f; uint32_t u; } pun;
+				pun.f = f;
+				return pun.u;
+			}
+			inline uint64_t BitReprOfDouble(double d)
+			{
+				union { double d; uint64_t u; } pun;
+				pun.d = d;
+				return pun.u;
+			}
+		}
+
+		#ifdef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+			// Runtime reading data from an address.
+			// The address need not be aligned to the data size -- but unaligned accesses are acceptable on x64 processors.
+			EA_FORCE_INLINE uint16_t ReadFromBigEndianUint16(const void* pData) { return detail::bswap16(static_cast<const uint16_t*>(pData)[0]); }
+			EA_FORCE_INLINE uint32_t ReadFromBigEndianUint32(const void* pData) { return detail::bswap32(static_cast<const uint32_t*>(pData)[0]); }
+			EA_FORCE_INLINE uint64_t ReadFromBigEndianUint64(const void* pData) { return detail::bswap64(static_cast<const uint64_t*>(pData)[0]); }
+			EA_FORCE_INLINE int16_t  ReadFromBigEndianInt16(const void* pData)  { return detail::bswap16(static_cast<const int16_t*>( pData)[0]); }
+			EA_FORCE_INLINE int32_t  ReadFromBigEndianInt32(const void* pData)  { return detail::bswap32(static_cast<const int32_t*>( pData)[0]); }
+			EA_FORCE_INLINE int64_t  ReadFromBigEndianInt64(const void* pData)  { return detail::bswap64(static_cast<const int64_t*>( pData)[0]); }
+
+		#else
+			// Runtime reading data from an address.
+			// The address need not be aligned to the data size.
+			inline uint16_t ReadFromBigEndianUint16(const void* pData)
+			{
+				return (uint16_t)((uint32_t)(((const uint8_t*)pData)[0] << 8) | 
+								  (uint32_t)(((const uint8_t*)pData)[1] << 0));
+			}
+
+			inline int16_t ReadFromBigEndianInt16(const void* pData)
+				{ return (int16_t)ReadFromBigEndianUint16(pData); }
+
+			inline uint32_t ReadFromBigEndianUint32(const void* pData)
+			{
+				return (uint32_t)((uint32_t)(((const uint8_t*)pData)[0] << 24) | 
+								  (uint32_t)(((const uint8_t*)pData)[1] << 16) | 
+								  (uint32_t)(((const uint8_t*)pData)[2] <<  8) | 
+								  (uint32_t)(((const uint8_t*)pData)[3] <<  0));
+			}
+
+			inline int32_t ReadFromBigEndianInt32(const void* pData)
+				{ return (int32_t)ReadFromBigEndianUint32(pData); }
+
+			inline uint64_t ReadFromBigEndianUint64(const void* pData)
+			{
+				uint64_t nReturnValue(0);
+				const uint8_t* const pData8 = (const uint8_t*)pData;
+				for(size_t i = 0; i < sizeof(uint64_t); ++i)
+					nReturnValue |= ((uint64_t)pData8[sizeof(uint64_t) - 1 - i] << (i * 8));
+				return nReturnValue;
+			}
+
+			inline int64_t ReadFromBigEndianInt64(const void* pData)
+				{ return (int64_t)ReadFromBigEndianUint64(pData); }
+		#endif
+
+
+		inline float ReadFromBigEndianFloat(const void* pData)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint32_t nResult = ReadFromBigEndianUint32(pData);
+			return detail::FloatFromBitRepr(nResult);
+		}
+
+		inline double ReadFromBigEndianDouble(const void* pData)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint64_t nResult = ReadFromBigEndianUint64(pData);
+			return detail::DoubleFromBitRepr(nResult);
+		}
+
+		inline uint128_t ReadFromBigEndianUint128(const void* pData)
+		{
+			uint128_t nReturnValue((uint32_t)0);
+			const uint8_t* const pData8 = (const uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(uint128_t); ++i)
+			{
+				uint128_t temp(pData8[sizeof(uint128_t) - 1 - i]);
+				temp <<= (i * 8);
+				nReturnValue |= temp;
+			}
+			return nReturnValue;
+		}
+
+		inline int128_t ReadFromBigEndianInt128(const void* pData)
+		{
+			int128_t nReturnValue((uint32_t)0);
+			const uint8_t* const pData8 = (const uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(int128_t); ++i)
+			{
+				int128_t temp(pData8[sizeof(int128_t) - 1 - i]);
+				temp <<= (i * 8);
+				nReturnValue |= temp;
+			}
+			return nReturnValue;
+		}
+
+
+		// This function provided for backwards compatibility with older EA code.
+		// Reads nSourceBytes stored in big-endian ordering into a uint32_t.
+		// nSourceBytes can be a number in the range of 1..4. nSourceBytes counts
+		// below 4 refer to the lower significant bytes of the uint32_t. The primary
+		// purpose of this function is to decode packed integer streams, as a uint32_t
+		// normally uses 4 bytes but if it's just small numbers then it's not useful
+		// to store the higher bytes.
+		// This function currently requires pSource to be 32 bit aligned or requires the 
+		// given platform to be able to read uint32_t values that aren't 32 bit aligned.
+		inline uint32_t ReadFromBigEndian(const void* pSource, int32_t nSourceBytes)
+		{
+			#if defined(EA_SYSTEM_BIG_ENDIAN)
+				switch (nSourceBytes)
+				{
+					case 1:
+						return (uint32_t)*(const uint8_t*)pSource;
+					case 2: 
+						return (uint32_t)*(const uint16_t*)pSource;
+					case 3:
+						return (uint32_t)*(const uint32_t*)pSource >> 8;
+					case 4:
+						return (uint32_t)*(const uint32_t*)pSource;
+					default: 
+						return 0;
+				}
+			#else
+				switch (nSourceBytes)
+				{
+					case 1:
+						return 
+							  (uint32_t)*((const uint8_t*)pSource + 0);
+					case 2: 
+						return 
+							(((uint32_t)*((const uint8_t*)pSource + 0)) << 8) |
+							(((uint32_t)*((const uint8_t*)pSource + 1)));
+					case 3:
+						return 
+							(((uint32_t)*((const uint8_t*)pSource + 0)) << 16) |
+							(((uint32_t)*((const uint8_t*)pSource + 1)) << 8)  |
+							(((uint32_t)*((const uint8_t*)pSource + 2)));
+					case 4:
+						return 
+							(((uint32_t)*((const uint8_t*)pSource + 0)) << 24) |
+							(((uint32_t)*((const uint8_t*)pSource + 1)) << 16) |
+							(((uint32_t)*((const uint8_t*)pSource + 2)) << 8)  |
+							(((uint32_t)*((const uint8_t*)pSource + 3)));
+					default: 
+						return 0;
+				}
+			#endif
+		}
+
+		inline void ReadFromBigEndian(const void* pSource, void* pDest, size_t sizeOfData)
+		{
+			// Currently we require the destination address to be aligned with the data type size.
+			switch(sizeOfData)
+			{
+				case 16:
+					*((uint128_t*)pDest) = ReadFromBigEndianUint128(pSource);
+					break;
+				case 8:
+					*((uint64_t*)pDest) = ReadFromBigEndianUint64(pSource);
+					break;
+				case 4:
+					*((uint32_t*)pDest) = ReadFromBigEndianUint32(pSource);
+					break;
+				case 2:
+					*((uint16_t*)pDest) = ReadFromBigEndianUint16(pSource);
+					break;
+				case 1:
+					*((uint8_t*)pDest) = *((uint8_t*)pSource);
+					break;
+			}
+		}
+
+		#ifdef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+			// Runtime reading data from an address.
+			// The address need not be aligned to the data size -- but unaligned accesses are acceptable on x64 processors.
+			EA_FORCE_INLINE uint16_t ReadFromLittleEndianUint16(const void* pData) { return (static_cast<const uint16_t*>(pData)[0]); }
+			EA_FORCE_INLINE uint32_t ReadFromLittleEndianUint32(const void* pData) { return (static_cast<const uint32_t*>(pData)[0]); }
+			EA_FORCE_INLINE uint64_t ReadFromLittleEndianUint64(const void* pData) { return (static_cast<const uint64_t*>(pData)[0]); }
+			EA_FORCE_INLINE int16_t ReadFromLittleEndianInt16(const void* pData)   { return (static_cast<const int16_t*>( pData)[0]); }
+			EA_FORCE_INLINE int32_t ReadFromLittleEndianInt32(const void* pData)   { return (static_cast<const int32_t*>( pData)[0]); }
+			EA_FORCE_INLINE int64_t ReadFromLittleEndianInt64(const void* pData)   { return (static_cast<const int64_t*>( pData)[0]); }
+
+		#else
+			// Runtime reading data from an address.
+			// Address need not be aligned to the data size.
+			// Thus we cannot safely simply cast the address to the desired data type.
+			inline uint16_t ReadFromLittleEndianUint16(const void* pData)
+			{
+				return (uint16_t)((uint32_t)(((const uint8_t*)pData)[0] << 0) | 
+								  (uint32_t)(((const uint8_t*)pData)[1] << 8));
+			}
+
+			inline int16_t ReadFromLittleEndianInt16(const void* pData)
+				{ return (int16_t)ReadFromLittleEndianUint16(pData); }
+
+			inline uint32_t ReadFromLittleEndianUint32(const void* pData)
+			{
+				return (uint32_t)((uint32_t)(((const uint8_t*)pData)[0] <<  0) | 
+								  (uint32_t)(((const uint8_t*)pData)[1] <<  8) | 
+								  (uint32_t)(((const uint8_t*)pData)[2] << 16) | 
+								  (uint32_t)(((const uint8_t*)pData)[3] << 24));
+			}
+
+			inline int32_t ReadFromLittleEndianInt32(const void* pData)
+				{ return (int32_t)ReadFromLittleEndianUint32(pData); }
+
+			inline uint64_t ReadFromLittleEndianUint64(const void* pData)
+			{
+				uint64_t nReturnValue(0);
+				const uint8_t* const pData8 = (const uint8_t*)pData;
+				for(size_t i = 0; i < sizeof(uint64_t); ++i)
+					nReturnValue |= ((uint64_t)pData8[i] << (i * 8));
+				return nReturnValue;
+			}
+
+			inline int64_t ReadFromLittleEndianInt64(const void* pData)
+				{ return (int64_t)ReadFromLittleEndianUint64(pData); }
+		#endif
+
+		inline float ReadFromLittleEndianFloat(const void* pData)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint32_t nResult = ReadFromLittleEndianUint32(pData);
+			return detail::FloatFromBitRepr(nResult);
+		}
+
+		inline double ReadFromLittleEndianDouble(const void* pData)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint64_t nResult = ReadFromLittleEndianUint64(pData);
+			return detail::DoubleFromBitRepr(nResult);
+		}
+
+		inline uint128_t ReadFromLittleEndianUint128(const void* pData)
+		{
+			uint128_t nReturnValue((uint32_t)0);
+			const uint8_t* const pData8 = (const uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(uint128_t); ++i)
+				nReturnValue |= ((uint128_t)pData8[i] << (i * 8));
+			return nReturnValue;
+		}
+
+		inline int128_t ReadFromLittleEndianInt128(const void* pData)
+		{
+			int128_t nReturnValue((uint32_t)0);
+			const uint8_t* const pData8 = (const uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(int128_t); ++i)
+				nReturnValue |= ((int128_t)pData8[i] << (i * 8));
+			return nReturnValue;
+		}
+
+		inline void ReadFromLittleEndian(const void* pSource, void* pDest, size_t sizeOfData)
+		{
+			// Currently we require the destination address to be aligned with the data type size.
+			switch(sizeOfData)
+			{
+				case 16:
+					*((uint128_t*)pDest) = ReadFromLittleEndianUint128(pSource);
+					break;
+				case 8:
+					*((uint64_t*)pDest) = ReadFromLittleEndianUint64(pSource);
+					break;
+				case 4:
+					*((uint32_t*)pDest) = ReadFromLittleEndianUint32(pSource);
+					break;
+				case 2:
+					*((uint16_t*)pDest) = ReadFromLittleEndianUint16(pSource);
+					break;
+				case 1:
+					*((uint8_t*)pDest) = *((uint8_t*)pSource);
+					break;
+			}
+		}
+
+		#ifdef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+			inline void WriteToBigEndian(void* pData, uint16_t x) { static_cast<uint16_t*>(pData)[0] = detail::bswap16(x); }
+			inline void WriteToBigEndian(void* pData, uint32_t x) { static_cast<uint32_t*>(pData)[0] = detail::bswap32(x); }
+			inline void WriteToBigEndian(void* pData, uint64_t x) { static_cast<uint64_t*>(pData)[0] = detail::bswap64(x); }
+			inline void WriteToBigEndian(void* pData, int16_t x)  { static_cast<int16_t*>( pData)[0] = detail::bswap16(x); }
+			inline void WriteToBigEndian(void* pData, int32_t x)  { static_cast<int32_t*>( pData)[0] = detail::bswap32(x); }
+			inline void WriteToBigEndian(void* pData, int64_t x)  { static_cast<int64_t*>( pData)[0] = detail::bswap64(x); }
+		#else
+			// Runtime swizzling of write data to an address.
+			// Address need not be aligned to the data size.
+			inline void WriteToBigEndian(void* pData, uint16_t x)
+			{
+				((uint8_t*)pData)[0] = (uint8_t)(x >> 8);
+				((uint8_t*)pData)[1] = (uint8_t)(x);
+			}
+
+			inline void WriteToBigEndian(void* pData, int16_t x)
+				{ WriteToBigEndian(pData, (uint16_t)x); }
+
+			inline void WriteToBigEndian(void* pData, uint32_t x)
+			{
+				((uint8_t*)pData)[0] = (uint8_t)(x >> 24);
+				((uint8_t*)pData)[1] = (uint8_t)(x >> 16);
+				((uint8_t*)pData)[2] = (uint8_t)(x >> 8);
+				((uint8_t*)pData)[3] = (uint8_t)(x);
+			}
+
+			inline void WriteToBigEndian(void* pData, int32_t x)
+				{ WriteToBigEndian(pData, (uint32_t)x); }
+
+			inline void WriteToBigEndian(void* pData, uint64_t x)
+			{
+				uint8_t* const pData8 = (uint8_t*)pData;
+				for(size_t i = 0; i < sizeof(uint64_t); ++i)
+					pData8[sizeof(uint64_t) - 1 - i] = (uint8_t)(x >> (i * 8));
+			}
+
+			inline void WriteToBigEndian(void* pData, int64_t x)
+				{ WriteToBigEndian(pData, (uint64_t)x); }
+		#endif
+
+		inline void WriteToBigEndian(void* pData, float x)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint32_t nValue = detail::BitReprOfFloat(x);
+			WriteToBigEndian(pData, nValue);
+		}
+
+		inline void WriteToBigEndian(void* pData, double x)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint64_t nValue = detail::BitReprOfDouble(x);
+			WriteToBigEndian(pData, nValue);
+		}
+
+		inline void WriteToBigEndian(void* pData, uint128_t x)
+		{
+			uint8_t* const pData8 = (uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(uint128_t); ++i)
+				pData8[sizeof(uint128_t) - 1 - i] = (x >> (i * 8)).AsUint8();
+		}
+
+		inline void WriteToBigEndian(void* pData, int128_t x)
+		{
+			uint8_t* const pData8 = (uint8_t*)pData;
+			for(int i = 0; i < (int)sizeof(int128_t); ++i)
+				pData8[sizeof(int128_t) - 1 - i] = (x >> (i * 8)).AsUint8();
+		}
+
+		// This function provided for backwards compatibility with older EA code.
+		// Reads nSourceBytes stored in big-endian ordering into a uint32_t.
+		// nSourceBytes can be a number in the range of 1..4. nSourceBytes counts
+		// below 4 refer to the lower significant bytes of the uint32_t. The primary
+		// purpose of this function is to decode packed integer streams, as a uint32_t
+		// normally uses 4 bytes but if it's just small numbers then it's not useful
+		// to store the higher bytes.
+		// This function currently requires pSource to be 32 bit aligned or requires the 
+		// given platform to be able to read uint32_t values that aren't 32 bit aligned.
+		inline void WriteToBigEndian(const void* pDest, uint32_t data, int32_t nSourceBytes)
+		{
+			#if defined(EA_SYSTEM_BIG_ENDIAN)
+				switch (nSourceBytes)
+				{
+					case 1:
+						((uint8_t*)pDest)[0] = (uint8_t)data;
+						break;
+
+					case 2: 
+						((uint16_t*)pDest)[0] = (uint16_t)data;
+						break;
+
+					case 3:
+						((uint32_t*)pDest)[0] = (uint32_t)data;
+						break;
+
+					case 4:
+						((uint8_t*)pDest)[0] = (uint8_t)(data >> 16);
+						((uint8_t*)pDest)[1] = (uint8_t)(data >>  8);
+						((uint8_t*)pDest)[2] = (uint8_t)(data >>  0);
+						break;
+				}
+			#else
+				switch (nSourceBytes)
+				{
+					case 1:
+						((uint8_t*)pDest)[0] = (uint8_t)(data >> 0);
+						break;
+
+					case 2: 
+						((uint8_t*)pDest)[0] = (uint8_t)(data >> 8);
+						((uint8_t*)pDest)[1] = (uint8_t)(data >> 0);
+						break;
+
+					case 3:
+						((uint8_t*)pDest)[0] = (uint8_t)(data >> 16);
+						((uint8_t*)pDest)[1] = (uint8_t)(data >>  8);
+						((uint8_t*)pDest)[2] = (uint8_t)(data >>  0);
+						break;
+
+					case 4:
+						((uint8_t*)pDest)[0] = (uint8_t)(data >> 24);
+						((uint8_t*)pDest)[1] = (uint8_t)(data >> 16);
+						((uint8_t*)pDest)[2] = (uint8_t)(data >>  8);
+						((uint8_t*)pDest)[3] = (uint8_t)(data >>  0);
+						break;
+				}
+			#endif
+		}
+
+		inline void WriteToBigEndian(const void* pSource, void* pDest, size_t sizeOfData)
+		{
+			// Currently we require the source address to be aligned with the data type size.
+			switch(sizeOfData)
+			{
+				case 16:
+					WriteToBigEndian(pDest, *((uint128_t*)pSource));
+					break;
+				case 8:
+					WriteToBigEndian(pDest, *((uint64_t*)pSource));
+					break;
+				case 4:
+					WriteToBigEndian(pDest, *((uint32_t*)pSource));
+					break;
+				case 2:
+					WriteToBigEndian(pDest, *((uint16_t*)pSource));
+					break;
+				case 1:
+					*((uint8_t*)pDest) = *((uint8_t*)pSource);
+					break;
+			}
+		}
+
+		#ifdef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+			// Runtime swizzling of write data to an address.
+			// Address need not be aligned to the data size -- but unaligned accesses are acceptable on x64 processors.
+			inline void WriteToLittleEndian(void* pData, uint16_t x) { (static_cast<uint16_t*>(pData))[0] = x; }
+			inline void WriteToLittleEndian(void* pData, uint32_t x) { (static_cast<uint32_t*>(pData))[0] = x; }
+			inline void WriteToLittleEndian(void* pData, uint64_t x) { (static_cast<uint64_t*>(pData))[0] = x; }
+			inline void WriteToLittleEndian(void* pData, int16_t x)  { (static_cast<int16_t*>( pData))[0] = x; }
+			inline void WriteToLittleEndian(void* pData, int32_t x)  { (static_cast<int32_t*>( pData))[0] = x; }
+			inline void WriteToLittleEndian(void* pData, int64_t x)  { (static_cast<int64_t*>( pData))[0] = x; }
+		#else
+			// Runtime swizzling of write data to an address.
+			// Address need not be aligned to the data size.
+			inline void WriteToLittleEndian(void* pData, uint16_t x)
+			{
+				((uint8_t*)pData)[0] = (uint8_t)(x);
+				((uint8_t*)pData)[1] = (uint8_t)(x >> 8);
+			}
+
+			inline void WriteToLittleEndian(void* pData, int16_t x)
+				{ WriteToLittleEndian(pData, (uint16_t)x); }
+
+			inline void WriteToLittleEndian(void* pData, uint32_t x)
+			{
+				((uint8_t*)pData)[0] = (uint8_t)(x);
+				((uint8_t*)pData)[1] = (uint8_t)(x >> 8);
+				((uint8_t*)pData)[2] = (uint8_t)(x >> 16);
+				((uint8_t*)pData)[3] = (uint8_t)(x >> 24);
+			}
+
+			inline void WriteToLittleEndian(void* pData, int32_t x)
+				{  WriteToLittleEndian(pData, (uint32_t)x); }
+
+			inline void WriteToLittleEndian(void* pData, uint64_t x)
+			{
+				uint8_t* const pData8 = (uint8_t*)pData;
+				for(int i = sizeof(uint64_t) - 1; i >= 0; --i)
+					pData8[i] = (uint8_t)(x >> (i * 8));
+			}
+
+			inline void WriteToLittleEndian(void* pData, int64_t x)
+				{ WriteToLittleEndian(pData, (uint64_t)x); }
+		#endif
+
+		inline void WriteToLittleEndian(void* pData, float x)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint32_t nValue = detail::BitReprOfFloat(x);
+			WriteToLittleEndian(pData, nValue);
+		}
+
+		inline void WriteToLittleEndian(void* pData, double x)
+		{
+			// Working with floating point values is safe here because 
+			// at no time does the FPU see an invalid swizzled float.
+			uint64_t nValue = detail::BitReprOfDouble(x);
+			WriteToLittleEndian(pData, nValue);
+		}
+
+		inline void WriteToLittleEndian(void* pData, uint128_t x)
+		{
+			uint8_t* const pData8 = (uint8_t*)pData;
+			for(int i = sizeof(uint128_t) - 1; i >= 0; --i)
+				pData8[i] = (x >> (i * 8)).AsUint8();
+		}
+
+		inline void WriteToLittleEndian(void* pData, int128_t x)
+		{
+			uint8_t* const pData8 = (uint8_t*)pData;
+			for(int i = sizeof(int128_t) - 1; i >= 0; --i)
+				pData8[i] = (x >> (i * 8)).AsUint8();
+		}
+
+		inline void WriteToLittleEndian(const void* pSource, void* pDest, size_t sizeOfData)
+		{
+			// Currently we require the source address to be aligned with the data type size.
+			switch(sizeOfData)
+			{
+				case 16:
+					WriteToLittleEndian(pDest, *((uint128_t*)pSource));
+					break;
+				case 8:
+					WriteToLittleEndian(pDest, *((uint64_t*)pSource));
+					break;
+				case 4:
+					WriteToLittleEndian(pDest, *((uint32_t*)pSource));
+					break;
+				case 2:
+					WriteToLittleEndian(pDest, *((uint16_t*)pSource));
+					break;
+				case 1:
+					*((uint8_t*)pDest) = *((uint8_t*)pSource);
+					break;
+			}
+		}
+
+
+
+
+		// Compiler/platform-specific implementations
+		// Implement this for as many of the following platforms as possible,
+		// defining EA_SWIZZLE_X_DEFINED for each one implemented:
+		//       SNSystems/PlayStation
+		//       Microsoft/XBox
+		//       Microsoft/PC
+		//       GCC/PC
+		//       GCC/Linux
+		//       GCC/Macintosh
+
+		#ifdef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+			EA_FORCE_INLINE uint16_t Swizzle(uint16_t x) { return detail::bswap16(x); }
+			EA_FORCE_INLINE int16_t  Swizzle(int16_t x)  { return detail::bswap16(x); }
+			#define EA_SWIZZLE_16_DEFINED
+			
+			EA_FORCE_INLINE uint32_t Swizzle(uint32_t x) { return detail::bswap32(x); }
+			EA_FORCE_INLINE int32_t  Swizzle(int32_t x)  { return detail::bswap32(x); }
+			#define EA_SWIZZLE_32_DEFINED
+			
+			EA_FORCE_INLINE uint64_t Swizzle(uint64_t x) { return detail::bswap64(x); }
+			EA_FORCE_INLINE int64_t  Swizzle(int64_t x)  { return detail::bswap64(x); }
+			#define EA_SWIZZLE_64_DEFINED
+
+		// MSVC7 / any platform
+		#elif defined(EA_PLATFORM_MICROSOFT) && (_MSC_VER >= 1300)
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				return _byteswap_ushort(x); // The Microsoft compiler will fail to link _byteswap_ushort unless you 
+			}                               // enable intrinsic functions or link in an appropriate library.
+			inline int16_t Swizzle(int16_t x)
+			{
+				return (int16_t)_byteswap_ushort((uint16_t)x);
+			}
+
+			#define EA_SWIZZLE_16_DEFINED          
+
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				return _byteswap_ulong(x);  // The Microsoft compiler will fail to link _byteswap_ulong unless you 
+			}                               // enable intrinsic functions or link in an appropriate library.
+			inline int32_t Swizzle(int32_t x)
+			{
+				return (int32_t)_byteswap_ulong((uint32_t)x);
+			}
+			#define EA_SWIZZLE_32_DEFINED          
+
+		#elif defined(EA_PROCESSOR_X86) && (defined(EA_COMPILER_MSVC))
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				__asm xor eax, eax
+				__asm mov ax, x
+				__asm xchg ah, al
+			}
+			inline int16_t Swizzle(int16_t x)
+				{ return (int16_t)Swizzle((uint16_t)x); }
+			#define EA_SWIZZLE_16_DEFINED
+
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				// VC7 and later have a _bswap_ulong() function which converts over to a bswap 
+				// instruction if you use use /Ox ("full optimization") compiler option. 
+				// However, with other optimization options it yields rather poor code. So we 
+				// take the reliable route here and use asm and don't use _bswap_ulong().
+				__asm mov eax, x
+				__asm bswap eax
+			}
+			inline int32_t Swizzle(int32_t x)
+				{ return (int32_t)Swizzle((uint32_t)x); }
+			#define EA_SWIZZLE_32_DEFINED
+
+
+		// GNUC/IntelX86
+		#elif defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_GNUC)
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				__asm__ __volatile__("xchgb %b0,%h0" : "=q" (x) : "0" (x));
+				return x;
+			}
+			inline int16_t Swizzle(int16_t x)
+				{ return (int16_t)Swizzle((uint16_t)x); }
+			#define EA_SWIZZLE_16_DEFINED
+
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				__asm__ __volatile__("bswap %0" : "=r" (x) : "0" (x));
+				return x;
+			}
+			inline int32_t Swizzle(int32_t x)
+				{ return (int32_t)Swizzle((uint32_t)x); }
+			#define EA_SWIZZLE_32_DEFINED
+
+
+
+		// GNUC/PowerPC
+		#elif defined(EA_PROCESSOR_POWERPC) && defined(EA_COMPILER_GNUC)
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				uint16_t temp;
+				__asm__ __volatile__("lhbrx %0,0,%1" : "=r" (temp) : "r" (&x): "memory"); 
+				return temp;
+			}
+			inline int16_t Swizzle(int16_t x)
+				{ return (int16_t)Swizzle((uint16_t)x); }
+			#define EA_SWIZZLE_16_DEFINED
+
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				uint32_t temp;
+				__asm__ __volatile__("lwbrx %0,0,%1" : "=r" (temp) : "r" (&x): "memory"); 
+				return temp;
+			}
+			inline int32_t Swizzle(int32_t x)
+				{ return (int32_t)Swizzle((uint32_t)x); }
+			#define EA_SWIZZLE_32_DEFINED
+
+		// GNUC/ARM (ARM is a handheld device processor)
+		// GCC for ARM generates bad swizzle code which doesn't use the rev instruction. So we don't use them here.
+		// http://hardwarebug.org/2010/01/14/beware-the-builtins/
+		#elif defined(EA_PROCESSOR_ARM) && defined(EA_COMPILER_GNUC) && !defined(__THUMB_INTERWORK__) // The Thumb instruction set doesn't have endian reversing instructions.
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				int temp;
+				__asm__ __volatile__(
+					"and %1, %2, #0xff\n"
+					"mov %0, %2, lsr #8\n"
+					"orr %0, %0, %1, lsl #8"
+					: "=r" (x), "=r" (temp)
+					: "r" (x));
+				return (x);
+			}
+			inline int16_t Swizzle(int16_t x)
+				{ return (int16_t)Swizzle((uint16_t)x); }
+			#define EA_SWIZZLE_16_DEFINED
+
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				int temp;
+				__asm__ __volatile__(
+					"eor   %1, %2, %2, ror #16\n"
+					"bic   %1, %1, #0x00ff0000\n"
+					"mov   %0, %2, ror #8\n"
+					"eor   %0, %0, %1, lsr #8"
+					: "=r" (x), "=r" (temp)
+					: "r" (x));
+				return (x);
+			}
+			inline int32_t Swizzle(int32_t x)
+				{ return (int32_t)Swizzle((uint32_t)x); }
+			#define EA_SWIZZLE_32_DEFINED
+
+		#endif
+
+
+		// Compiler/platform-independent implementations.
+		#ifndef EA_SWIZZLE_16_DEFINED
+			inline uint16_t Swizzle(uint16_t x)
+			{
+				return (uint16_t) ((x >> 8) | (x << 8));
+			}
+			inline int16_t Swizzle(int16_t x)
+				{ return (int16_t)Swizzle((uint16_t)x); }
+		#endif
+
+		#ifndef EA_SWIZZLE_32_DEFINED
+			inline uint32_t Swizzle(uint32_t x)
+			{
+				// An alternative to the mechanism of using shifts and ors below
+				// is to use byte addressing. However, tests have shown that  
+				// while the shifts and ORs look heavier than byte addressing, 
+				// they are easier for the compiler to optimize and make it much
+				// more likely that the compiler will optimize away the code
+				// below and simply precalculate the result.
+				return (uint32_t)
+					((x >> 24)               |
+					((x << 24) & 0xff000000) |
+					((x <<  8) & 0x00ff0000) |
+					((x >>  8) & 0x0000ff00)); 
+			}
+			inline int32_t Swizzle(int32_t x)
+				{ return (int32_t)Swizzle((uint32_t)x); }
+		#endif
+
+		#ifndef EA_SWIZZLE_64_DEFINED
+			inline uint64_t Swizzle(uint64_t x)
+			{
+				const uint32_t high32Bits = Swizzle((uint32_t)(x));
+				const uint32_t low32Bits  = Swizzle((uint32_t)(x >> 32));
+
+				return ((uint64_t)high32Bits << 32) | low32Bits;
+			}
+			inline int64_t Swizzle(int64_t x)
+				{ return (int64_t)Swizzle((uint64_t)x); }
+		#endif
+
+		#ifndef EA_SWIZZLE_128_DEFINED
+			inline uint128_t Swizzle(uint128_t x)
+			{
+				const uint64_t high64Bits = Swizzle(x.AsUint64());
+				const uint64_t low64Bits  = Swizzle((x >> 64).AsUint64());
+
+				return ((uint128_t)high64Bits << 64) | low64Bits;
+			}
+
+			inline int128_t Swizzle(int128_t x)
+			{
+				const uint64_t high64Bits = Swizzle(x.AsUint64());
+				const uint64_t low64Bits  = Swizzle((x >> 64).AsUint64());
+
+				return ((int128_t)high64Bits << 64) | low64Bits;
+			}
+		#endif
+
+		// This is done by pointer instead of value because floating point values
+		// touch the FPU, and you can't swizzle values that touch the FPU.
+		inline void Swizzle(float* pFloat)
+		{
+			union FloatConv
+			{
+				float    f;
+				uint32_t i;
+			} fc = { *pFloat };
+
+			fc.i = Swizzle(fc.i);
+			*pFloat = fc.f;
+		}
+
+		// This is done by pointer instead of value because floating point values
+		// touch the FPU, and you can't swizzle values that touch the FPU.
+		inline void Swizzle(double* pDouble)
+		{
+			union DoubleConv
+			{
+				double   d;
+				uint64_t i;
+			} fc = { *pDouble };
+
+			fc.i = Swizzle(fc.i);
+			*pDouble = fc.d;
+		}
+
+
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			inline uint16_t  ToBigEndian(uint16_t x)      { return x; }
+			inline  int16_t  ToBigEndian( int16_t x)      { return x; }
+			inline uint32_t  ToBigEndian(uint32_t x)      { return x; } 
+			inline  int32_t  ToBigEndian( int32_t x)      { return x; } 
+			inline uint64_t  ToBigEndian(uint64_t x)      { return x; }
+			inline  int64_t  ToBigEndian( int64_t x)      { return x; }
+			inline uint128_t ToBigEndian(uint128_t x)     { return x; }
+			inline  int128_t ToBigEndian( int128_t x)     { return x; }
+			inline void      ToBigEndian(float*)          { }
+			inline void      ToBigEndian(double*)         { }
+
+			inline uint16_t  FromBigEndian(uint16_t x)    { return x; }
+			inline  int16_t  FromBigEndian( int16_t x)    { return x; }
+			inline uint32_t  FromBigEndian(uint32_t x)    { return x; }
+			inline  int32_t  FromBigEndian( int32_t x)    { return x; }
+			inline uint64_t  FromBigEndian(uint64_t x)    { return x; }
+			inline  int64_t  FromBigEndian( int64_t x)    { return x; }
+			inline uint128_t FromBigEndian(uint128_t x)   { return x; }
+			inline  int128_t FromBigEndian( int128_t x)   { return x; }
+			inline void      FromBigEndian(float*)        { }
+			inline void      FromBigEndian(double*)       { }
+
+			inline uint16_t  ToLittleEndian(uint16_t x)    { return Swizzle(x); }
+			inline  int16_t  ToLittleEndian( int16_t x)    { return Swizzle(x); }
+			inline uint32_t  ToLittleEndian(uint32_t x)    { return Swizzle(x); }
+			inline  int32_t  ToLittleEndian( int32_t x)    { return Swizzle(x); }
+			inline uint64_t  ToLittleEndian(uint64_t x)    { return Swizzle(x); }
+			inline  int64_t  ToLittleEndian( int64_t x)    { return Swizzle(x); }
+			inline uint128_t ToLittleEndian(uint128_t x)   { return Swizzle(x); }
+			inline  int128_t ToLittleEndian( int128_t x)   { return Swizzle(x); }
+			inline void      ToLittleEndian(float* x)      { Swizzle(x); }
+			inline void      ToLittleEndian(double* x)     { Swizzle(x); }
+
+			inline uint16_t  FromLittleEndian(uint16_t x)  { return Swizzle(x); }
+			inline  int16_t  FromLittleEndian( int16_t x)  { return Swizzle(x); }
+			inline uint32_t  FromLittleEndian(uint32_t x)  { return Swizzle(x); }
+			inline  int32_t  FromLittleEndian( int32_t x)  { return Swizzle(x); }
+			inline uint64_t  FromLittleEndian(uint64_t x)  { return Swizzle(x); }
+			inline  int64_t  FromLittleEndian (int64_t x)  { return Swizzle(x); }
+			inline uint128_t FromLittleEndian(uint128_t x) { return Swizzle(x); }
+			inline  int128_t FromLittleEndian( int128_t x) { return Swizzle(x); }
+			inline void      FromLittleEndian(float* x)    { Swizzle(x); }
+			inline void      FromLittleEndian(double* x)   { Swizzle(x); }
+
+		#elif defined(EA_SYSTEM_LITTLE_ENDIAN)
+
+			inline uint16_t  ToBigEndian(uint16_t x)       { return Swizzle(x); }
+			inline  int16_t  ToBigEndian( int16_t x)       { return Swizzle(x); }
+			inline uint32_t  ToBigEndian(uint32_t x)       { return Swizzle(x); }
+			inline  int32_t  ToBigEndian( int32_t x)       { return Swizzle(x); }
+			inline uint64_t  ToBigEndian(uint64_t x)       { return Swizzle(x); }
+			inline  int64_t  ToBigEndian( int64_t x)       { return Swizzle(x); }
+			inline uint128_t ToBigEndian(uint128_t x)      { return Swizzle(x); }
+			inline  int128_t ToBigEndian( int128_t x)      { return Swizzle(x); }
+			inline void      ToBigEndian(float* x)         { Swizzle(x); }
+			inline void      ToBigEndian(double* x)        { Swizzle(x); }
+
+			inline uint16_t  FromBigEndian(uint16_t x)     { return Swizzle(x); }
+			inline  int16_t  FromBigEndian( int16_t x)     { return Swizzle(x); }
+			inline uint32_t  FromBigEndian(uint32_t x)     { return Swizzle(x); }
+			inline  int32_t  FromBigEndian( int32_t x)     { return Swizzle(x); }
+			inline uint64_t  FromBigEndian(uint64_t x)     { return Swizzle(x); }
+			inline  int64_t  FromBigEndian( int64_t x)     { return Swizzle(x); }
+			inline uint128_t FromBigEndian(uint128_t x)    { return Swizzle(x); }
+			inline  int128_t FromBigEndian( int128_t x)    { return Swizzle(x); }
+			inline void      FromBigEndian(float* x)       { Swizzle(x); }
+			inline void      FromBigEndian(double* x)      { Swizzle(x); }
+
+			inline uint16_t  ToLittleEndian(uint16_t x)    { return x; }
+			inline  int16_t  ToLittleEndian( int16_t x)    { return x; }
+			inline uint32_t  ToLittleEndian(uint32_t x)    { return x; }
+			inline  int32_t  ToLittleEndian( int32_t x)    { return x; }
+			inline uint64_t  ToLittleEndian(uint64_t x)    { return x; }
+			inline  int64_t  ToLittleEndian( int64_t x)    { return x; }
+			inline uint128_t ToLittleEndian(uint128_t x)   { return x; }
+			inline  int128_t ToLittleEndian( int128_t x)   { return x; }
+			inline void      ToLittleEndian(float*)        { }
+			inline void      ToLittleEndian(double*)       { }
+
+			inline uint16_t  FromLittleEndian(uint16_t x)  { return x; }
+			inline  int16_t  FromLittleEndian( int16_t x)  { return x; }
+			inline uint32_t  FromLittleEndian(uint32_t x)  { return x; }
+			inline  int32_t  FromLittleEndian( int32_t x)  { return x; }
+			inline uint64_t  FromLittleEndian(uint64_t x)  { return x; }
+			inline  int64_t  FromLittleEndian( int64_t x)  { return x; }
+			inline uint128_t FromLittleEndian(uint128_t x) { return x; }
+			inline  int128_t FromLittleEndian( int128_t x) { return x; }
+			inline void      FromLittleEndian(float*)      { }
+			inline void      FromLittleEndian(double*)     { }
+
+		#endif
+
+
+		// SwizzleConst
+		// Used for swizzling compile-time constants.
+		// The implementations are defined in a simplistic way so that 
+		// the compiler can optimize away the logic in each function.
+		// These functions are intentionally not optimized with tricks such 
+		// as assembly language, compiler intrinsics, or memory tricks, as
+		// such things would interfere with the compiler's ability to optimize
+		// away these operations. All of this functions should compile away
+		// when used with compile-time constants.
+		EA_FORCE_INLINE uint16_t SwizzleConst(uint16_t x)
+		{
+			return (uint16_t) ((x >> 8) | (x << 8));
+		}
+		EA_FORCE_INLINE int16_t SwizzleConst(int16_t x)
+			{ return (int16_t)SwizzleConst((uint16_t)x); }
+
+		EA_FORCE_INLINE uint32_t SwizzleConst(uint32_t x)
+		{
+			return (uint32_t)
+				((x >> 24)               |
+				((x << 24) & 0xff000000) |
+				((x <<  8) & 0x00ff0000) |
+				((x >>  8) & 0x0000ff00)); 
+		}
+		EA_FORCE_INLINE int32_t SwizzleConst(int32_t x)
+			{ return (int32_t)SwizzleConst((uint32_t)x); }
+
+		EA_FORCE_INLINE uint64_t SwizzleConst(uint64_t x)
+		{
+			const uint32_t high32Bits = Swizzle((uint32_t)(x));
+			const uint32_t low32Bits  = Swizzle((uint32_t)(x >> 32));
+
+			return ((uint64_t)high32Bits << 32) | low32Bits;
+		}
+		EA_FORCE_INLINE int64_t SwizzleConst(int64_t x)
+			{ return (int64_t)SwizzleConst((uint64_t)x); }
+
+		inline uint128_t SwizzleConst(uint128_t x)
+		{
+			const uint64_t high64Bits = SwizzleConst(x.AsUint64());
+			const uint64_t low64Bits  = SwizzleConst((x >> 64).AsUint64());
+
+			return ((uint128_t)high64Bits << 64) | low64Bits;
+		}
+		inline int128_t SwizzleConst(int128_t x)
+		{
+			const uint64_t high64Bits = SwizzleConst(x.AsUint64());
+			const uint64_t low64Bits  = SwizzleConst((x >> 64).AsUint64());
+
+			return ((int128_t)high64Bits << 64) | low64Bits;
+		}
+
+
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			inline uint16_t  ToBigEndianConst(uint16_t x)         { return x; }
+			inline  int16_t  ToBigEndianConst( int16_t x)         { return x; }
+			inline uint32_t  ToBigEndianConst(uint32_t x)         { return x; }
+			inline  int32_t  ToBigEndianConst( int32_t x)         { return x; }
+			inline uint64_t  ToBigEndianConst(uint64_t x)         { return x; }
+			inline  int64_t  ToBigEndianConst( int64_t x)         { return x; }
+			inline uint128_t ToBigEndianConst(uint128_t x)        { return x; }
+			inline  int128_t ToBigEndianConst( int128_t x)        { return x; }
+
+			inline uint16_t  FromBigEndianConst(uint16_t x)       { return x; }
+			inline uint32_t  FromBigEndianConst(uint32_t x)       { return x; }
+			inline uint64_t  FromBigEndianConst(uint64_t x)       { return x; }
+			inline uint128_t FromBigEndianConst(uint128_t x)      { return x; }
+			inline  int16_t  FromBigEndianConst( int16_t x)       { return x; }
+			inline  int32_t  FromBigEndianConst( int32_t x)       { return x; }
+			inline  int64_t  FromBigEndianConst( int64_t x)       { return x; }
+			inline  int128_t FromBigEndianConst( int128_t x)      { return x; }
+
+			inline uint16_t  ToLittleEndianConst(uint16_t x)      { return SwizzleConst(x); }
+			inline  int16_t  ToLittleEndianConst( int16_t x)      { return SwizzleConst(x); }
+			inline uint32_t  ToLittleEndianConst(uint32_t x)      { return SwizzleConst(x); }
+			inline  int32_t  ToLittleEndianConst( int32_t x)      { return SwizzleConst(x); }
+			inline uint64_t  ToLittleEndianConst(uint64_t x)      { return SwizzleConst(x); }
+			inline  int64_t  ToLittleEndianConst( int64_t x)      { return SwizzleConst(x); }
+			inline uint128_t ToLittleEndianConst(uint128_t x)     { return SwizzleConst(x); }
+			inline  int128_t ToLittleEndianConst( int128_t x)     { return SwizzleConst(x); }
+
+			inline uint16_t  FromLittleEndianConst(uint16_t x)    { return SwizzleConst(x); }
+			inline  int16_t  FromLittleEndianConst( int16_t x)    { return SwizzleConst(x); }
+			inline uint32_t  FromLittleEndianConst(uint32_t x)    { return SwizzleConst(x); }
+			inline  int32_t  FromLittleEndianConst( int32_t x)    { return SwizzleConst(x); }
+			inline uint64_t  FromLittleEndianConst(uint64_t x)    { return SwizzleConst(x); }
+			inline  int64_t  FromLittleEndianConst( int64_t x)    { return SwizzleConst(x); }
+			inline uint128_t FromLittleEndianConst(uint128_t x)   { return SwizzleConst(x); }
+			inline  int128_t FromLittleEndianConst( int128_t x)   { return SwizzleConst(x); }
+
+		#elif defined(EA_SYSTEM_LITTLE_ENDIAN)
+
+			inline uint16_t  ToBigEndianConst(uint16_t x)         { return SwizzleConst(x); }
+			inline  int16_t  ToBigEndianConst( int16_t x)         { return SwizzleConst(x); }
+			inline uint32_t  ToBigEndianConst(uint32_t x)         { return SwizzleConst(x); }
+			inline  int32_t  ToBigEndianConst( int32_t x)         { return SwizzleConst(x); }
+			inline uint64_t  ToBigEndianConst(uint64_t x)         { return SwizzleConst(x); }
+			inline  int64_t  ToBigEndianConst( int64_t x)         { return SwizzleConst(x); }
+			inline uint128_t ToBigEndianConst(uint128_t x)        { return SwizzleConst(x); }
+			inline  int128_t ToBigEndianConst( int128_t x)        { return SwizzleConst(x); }
+
+			inline uint16_t  FromBigEndianConst(uint16_t x)       { return SwizzleConst(x); }
+			inline  int16_t  FromBigEndianConst( int16_t x)       { return SwizzleConst(x); }
+			inline uint32_t  FromBigEndianConst(uint32_t x)       { return SwizzleConst(x); }
+			inline  int32_t  FromBigEndianConst( int32_t x)       { return SwizzleConst(x); }
+			inline uint64_t  FromBigEndianConst(uint64_t x)       { return SwizzleConst(x); }
+			inline  int64_t  FromBigEndianConst( int64_t x)       { return SwizzleConst(x); }
+			inline uint128_t FromBigEndianConst(uint128_t x)      { return SwizzleConst(x); }
+			inline  int128_t FromBigEndianConst( int128_t x)      { return SwizzleConst(x); }
+
+			inline uint16_t  ToLittleEndianConst(uint16_t x)      { return x; }
+			inline  int16_t  ToLittleEndianConst( int16_t x)      { return x; }
+			inline uint32_t  ToLittleEndianConst(uint32_t x)      { return x; }
+			inline  int32_t  ToLittleEndianConst( int32_t x)      { return x; }
+			inline uint64_t  ToLittleEndianConst(uint64_t x)      { return x; }
+			inline  int64_t  ToLittleEndianConst( int64_t x)      { return x; }
+			inline uint128_t ToLittleEndianConst(uint128_t x)     { return x; }
+			inline  int128_t ToLittleEndianConst( int128_t x)     { return x; }
+
+			inline uint16_t  FromLittleEndianConst(uint16_t x)    { return x; }
+			inline  int16_t  FromLittleEndianConst( int16_t x)    { return x; }
+			inline uint32_t  FromLittleEndianConst(uint32_t x)    { return x; }
+			inline  int32_t  FromLittleEndianConst( int32_t x)    { return x; }
+			inline uint64_t  FromLittleEndianConst(uint64_t x)    { return x; }
+			inline  int64_t  FromLittleEndianConst( int64_t x)    { return x; }
+			inline uint128_t FromLittleEndianConst(uint128_t x)   { return x; }
+			inline  int128_t FromLittleEndianConst( int128_t x)   { return x; }
+
+		#endif
+
+
+		#undef EASTDC_LITTLE_ENDIAN_WITH_BSWAP
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+

+ 1018 - 0
include/EAStdC/EAFixedPoint.h

@@ -0,0 +1,1018 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements a fairly complete implementation of a fixed point
+// C++ numerical data type. You can use the data type freely in mathematical
+// statements just like any other data type. The following default types are
+// defined:
+//    SFixed16 -- Signed   Fixed Point, 16:16 precision
+//    UFixed16 -- Unsigned Fixed Point, 16:16 precision
+// You can easily define any other precision type by simply uncommenting 
+// the appropriate typedefs at the bottom of the file, such as SFixed20 
+// (Signed Fixed Point, 20:12 precision). You must also implement a custom
+// version of each of FixedMul, FixedDiv, etc. See the cpp file for versions
+// of these functions. 
+//
+// The following code is perfectly legal and tested. Note that arbitrary 
+// mixing of data types:
+//
+//    SFixed16      a(1), b(2), c(3);
+//    float         f = 4.5;
+//    double        d = 3.2;
+//    int           i =   6;
+//    a = b * f;
+//    a = (c / d) + b + f;
+//    a = c / d * (b % i) + f / c;
+//    a = i * -c / (b++);
+//    a = sin(a) + pow(b, d) * sqrt(a);
+//    a = log(a)/log(f);
+//
+// You will likely want to write special versions of the trigonometry functions
+// to provide fast speeds. Simple versions below are implemented that simply 
+// use the cpu's built-in floating point math hardware.
+//
+// Fixed point
+//      - Limited range
+//        Using fixed 16:16 which is common on PC's numbers are between -32767
+//        and +32767. The fractional part is accurate to 1/65536. 
+//      + Can be very fast
+//        A lot of fixed point procedures are achieved with adds and bitwise shifts which
+//        can be paired on the Pentium. So we have potentially two additions per clock
+//        cycle. Multiplies take longer. Although less accurate, square roots and trig
+//        functions can be really fast. 
+//      + Executes concurrently
+//        PCs with an FPU can execute integer instructions at the same time as
+//        floating-point instructions. 
+//      - Harder to code in high-level languages
+//        Fixed point routines sometimes aren't high-level friendly. It's much easier to see
+//        what's going on in a program if you use the standard floating point types. If you
+//        are writing in C or Pascal, it's best to do the fixed point bits in embedded
+//        assembler. 
+//
+//  Floating point
+//      + Large range
+//        The range for floating point numbers is typically in
+//        excess of 1E-100 to 1E+100 and have an accuracy
+//        of about 13 decimal places. 
+//      - Sometimes slow
+//        Generally fast for multiplies and addition but may be
+//        slow for trig and square roots, depending on the processor. 
+//        You should use the FPU for these if you require a high degree
+//        of accuracy. Conversions from float to int are slow too.
+//      + Executes concurrently
+//        PCs with an FPU can execute integer instructions at
+//        the same time as floating-point instructions. 
+//
+// Note that if you are using the Microsoft C++ compiler, then you can add this
+// to your autoexp.dat file to have it auto-expand fixed point values to their
+// floating point equivalents while holding the cursor over them or viewing then
+// under the watch window:
+//    SFixed16      = Value=<value/65536.F,f>
+//    UFixed16      = Value=<value/65536.F,f>
+//    FPTemplate<*> = Value=<value/65536.F,f>
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAFIXEDPOINT_H
+#define EASTDC_EAFIXEDPOINT_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+#include <math.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Classic C-style 16:16 fixed point functions/macros 
+//
+#ifndef EA_C_STYLE_FIXED_POINT_MATH
+#define EA_C_STYLE_FIXED_POINT_MATH
+	typedef int32_t EAFixed16;
+
+	#define           EAMAX_FIXED16        0x7fffffff
+	#define           EAMIN_FIXED16        0x80000000
+	#define           EAFixed16ToInt(a)    ((int32_t)(a) >> 16)
+	#define           EAIntToFixed16(a)    ((EAFixed16)((a) << 16))
+	#define           EAFixed16ToDouble(a) (((double)a) / 65536.0)
+	#define           EADoubleToFixed16(a) ((EAFixed16)((a) * 65536.0))
+	#define           EAFixed16ToFloat(a)  (((float)a) / 65536.f)
+	#define           EAFloatToFixed16(a)  ((EAFixed16)((a) * 65536.f))
+	#define           EAFixed16Negate(a)   (-a)
+	EAFixed16         EAFixed16Mul         (EAFixed16 a, EAFixed16 b);                  // Returns (a * b).
+	EAFixed16         EAFixed16Div         (EAFixed16 a, EAFixed16 b);                  // Returns (a / y).
+	EAFixed16         EAFixed16DivSafe     (EAFixed16 a, EAFixed16 b);                  // Returns (a / y). Returns max value possible if b is zero.
+	EAFixed16         EAFixed16MulDiv      (EAFixed16 a, EAFixed16 b, EAFixed16 c);     // Calculates (a * b / c) faster than separate mul and div.
+	EAFixed16         EAFixed16MulDivSafe  (EAFixed16 a, EAFixed16 b, EAFixed16 c);     // Calculates (a * b / c) faster than separate mul and div. Returns max value possible if z is zero.
+	EAFixed16         EAFixed16Mod         (EAFixed16 a, EAFixed16 b);                  // Retuns a modulo b, in fixed point format. For example, 3 % 2 = 1.
+	EAFixed16         EAFixed16ModSafe     (EAFixed16 a, EAFixed16 b);                  // Retuns a modulo b, in fixed point format. For example, 3 % 2 = 1. Returns max value possible if z is zero.
+	inline  EAFixed16 EAFixed16Abs         (EAFixed16 a) { return (a >= 0) ? a : -a; } 
+#endif
+///////////////////////////////////////////////////////////////////////////////
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// FP_USE_INTEL_ASM (Fixed point type use Intel asm)
+//
+#if defined(EA_PROCESSOR_X86) && defined(EA_ASM_STYLE_INTEL)
+	#define FP_USE_INTEL_ASM
+	#define FP_PASCAL EA_PASCAL
+#else
+	#define FP_PASCAL
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// class FPTemplate 
+//
+#define FPTemplateDeclaration template<class T, int upShiftInt, int downShiftInt, int upMulInt, int downDivInt>
+#define FPTemplateType FPTemplate<T, upShiftInt, downShiftInt, upMulInt, downDivInt>
+
+template <class  T,                            //'T' must be a signed or unsigned integer type (e.g. long, unsigned long, etc.)
+		  int  upShiftInt, int downShiftInt, 
+		  int  upMulInt,   int downDivInt>
+
+struct FPTemplate
+{
+	// Data
+	T value;
+
+	// Types
+	typedef T type;
+	   
+	//Functions
+	FPTemplate() {}
+	FPTemplate(const FPTemplate&    newValue) { value = newValue.value; }
+	FPTemplate(const int&           newValue) { value = newValue << upShiftInt; }
+	FPTemplate(const unsigned int&  newValue) { value = newValue << upShiftInt; }
+	FPTemplate(const long&          newValue) { value = newValue << upShiftInt; }
+	FPTemplate(const unsigned long& newValue) { value = newValue << upShiftInt; }
+	FPTemplate(const float&         newValue) { value = (int)(newValue * (float)upMulInt);  }
+	FPTemplate(const double&        newValue) { value = (int)(newValue * (double)upMulInt); }
+	void FromFixed(const int&       newValue) { value = newValue; }    // Accepts an int that is in fixed point format (i.e. shifted) already. 
+	T    AsFixed  ()                          { return value;     }    // Allows you to get the fixed point value itself and mess with it as you want. 
+
+	// We don't define the conversion operators because that would cause a lot of compiler
+	// errors complaining about not knowing what function to call. Thus, we have functions
+	// below such as "AsInt()" to do explicit conversions. 
+	// operator int()           const { return (int)          (value>>upShiftInt);          }
+	// operator unsigned int()  const { return (unsigned int) (value>>upShiftInt);          }
+	// operator long()          const { return (long)         (value>>upShiftInt);          }
+	// operator unsigned long() const { return (unsigned long)(value>>upShiftInt);          }
+	// operator float()         const { return (float)        (value /(float)upMulInt);     }
+	// operator double()        const { return (double)       (value /(double)upMulInt);    }
+
+	int           AsInt()         const { return (int)          (value>>upShiftInt);       }
+	unsigned int  AsUnsignedInt() const { return (unsigned int) (value>>upShiftInt);       }
+	long          AsLong()        const { return (long)         (value>>upShiftInt);       }
+	unsigned long AsUnsignedLong()const { return (unsigned long)(value>>upShiftInt);       }
+	float         AsFloat()       const { return (float)        (value /(float)upMulInt);  }
+	double        AsDouble()      const { return (double)       (value /(double)upMulInt); }
+
+	FPTemplate& operator=(const FPTemplate&    newValue) { value = newValue.value;                     return *this; }
+	FPTemplate& operator=(const int&           newValue) { value = newValue << upShiftInt;             return *this; }
+	FPTemplate& operator=(const unsigned int&  newValue) { value = newValue << upShiftInt;             return *this; }
+	FPTemplate& operator=(const long&          newValue) { value = newValue << upShiftInt;             return *this; }
+	FPTemplate& operator=(const unsigned long& newValue) { value = newValue << upShiftInt;             return *this; }
+	FPTemplate& operator=(const float&         newValue) { value = (T)(newValue * (float)upMulInt);  return *this; }
+	FPTemplate& operator=(const double&        newValue) { value = (T)(newValue * (double)upMulInt); return *this; }
+
+	bool operator< (const FPTemplate& compareValue) const { return value <  compareValue.value; }
+	bool operator> (const FPTemplate& compareValue) const { return value >  compareValue.value; }
+	bool operator>=(const FPTemplate& compareValue) const { return value >= compareValue.value; }
+	bool operator<=(const FPTemplate& compareValue) const { return value <= compareValue.value; }
+	bool operator==(const FPTemplate& compareValue) const { return value == compareValue.value; }
+	bool operator!=(const FPTemplate& compareValue) const { return value != compareValue.value; }
+	   
+	bool operator< (const int& compareValue) const { return value <  (T)(compareValue<<upShiftInt); }  
+	bool operator> (const int& compareValue) const { return value >  (T)(compareValue<<upShiftInt); } 
+	bool operator>=(const int& compareValue) const { return value >= (T)(compareValue<<upShiftInt); }
+	bool operator<=(const int& compareValue) const { return value <= (T)(compareValue<<upShiftInt); }
+	bool operator==(const int& compareValue) const { return value == (T)(compareValue<<upShiftInt); }
+	bool operator!=(const int& compareValue) const { return value != (T)(compareValue<<upShiftInt); }
+	   
+	bool operator< (const unsigned int& compareValue) const { return value <  (T)(compareValue<<upShiftInt); } 
+	bool operator> (const unsigned int& compareValue) const { return value >  (T)(compareValue<<upShiftInt); }  
+	bool operator>=(const unsigned int& compareValue) const { return value >= (T)(compareValue<<upShiftInt); }
+	bool operator<=(const unsigned int& compareValue) const { return value <= (T)(compareValue<<upShiftInt); }
+	bool operator==(const unsigned int& compareValue) const { return value == (T)(compareValue<<upShiftInt); }
+	bool operator!=(const unsigned int& compareValue) const { return value != (T)(compareValue<<upShiftInt); }
+	   
+	bool operator< (const long& compareValue) const { return value <  (T)(compareValue<<upShiftInt); }  
+	bool operator> (const long& compareValue) const { return value >  (T)(compareValue<<upShiftInt); } 
+	bool operator>=(const long& compareValue) const { return value >= (T)(compareValue<<upShiftInt); }
+	bool operator<=(const long& compareValue) const { return value <= (T)(compareValue<<upShiftInt); }
+	bool operator==(const long& compareValue) const { return value == (T)(compareValue<<upShiftInt); }
+	bool operator!=(const long& compareValue) const { return value != (T)(compareValue<<upShiftInt); }
+	   
+	bool operator< (const unsigned long& compareValue) const { return value <  (T)(compareValue<<upShiftInt); } 
+	bool operator> (const unsigned long& compareValue) const { return value >  (T)(compareValue<<upShiftInt); } 
+	bool operator>=(const unsigned long& compareValue) const { return value >= (T)(compareValue<<upShiftInt); }
+	bool operator<=(const unsigned long& compareValue) const { return value <= (T)(compareValue<<upShiftInt); }
+	bool operator==(const unsigned long& compareValue) const { return value == (T)(compareValue<<upShiftInt); }
+	bool operator!=(const unsigned long& compareValue) const { return value != (T)(compareValue<<upShiftInt); }
+
+	bool operator< (const float& compareValue) const { return value <  compareValue*(float)upMulInt; } //Note that we convert the float down rather 
+	bool operator> (const float& compareValue) const { return value >  compareValue*(float)upMulInt; } //that convert the fixed up.
+	bool operator>=(const float& compareValue) const { return value >= compareValue*(float)upMulInt; }
+	bool operator<=(const float& compareValue) const { return value <= compareValue*(float)upMulInt; }
+	bool operator==(const float& compareValue) const { return value == compareValue*(float)upMulInt; }
+	bool operator!=(const float& compareValue) const { return value != compareValue*(float)upMulInt; }
+	   
+	bool operator< (const double& compareValue) const { return value <  compareValue*(double)upMulInt; } //Note that we convert the float down rather 
+	bool operator> (const double& compareValue) const { return value >  compareValue*(double)upMulInt; } //that convert the fixed up.
+	bool operator>=(const double& compareValue) const { return value >= compareValue*(double)upMulInt; }
+	bool operator<=(const double& compareValue) const { return value <= compareValue*(double)upMulInt; }
+	bool operator==(const double& compareValue) const { return value == compareValue*(double)upMulInt; }
+	bool operator!=(const double& compareValue) const { return value != compareValue*(double)upMulInt; }
+	bool operator! ()                           const { return value == 0; }
+	   
+	FPTemplate  operator~ ()                    const { FPTemplate temp; temp.value = ~value; return temp; } 
+	FPTemplate  operator- ()                    const { FPTemplate temp; temp.value = -value; return temp; }
+	FPTemplate  operator+ ()                    const { return *this; }
+
+	FPTemplate& operator+=(const FPTemplate&    argValue) { value += argValue.value;            return *this; }
+	FPTemplate& operator+=(const int&           argValue) { value += (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator+=(const unsigned int&  argValue) { value += (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator+=(const long &         argValue) { value += (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator+=(const unsigned long& argValue) { value += (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator+=(const float&         argValue) { value += int(argValue*(float)upMulInt);  return *this; }
+	FPTemplate& operator+=(const double&        argValue) { value += int(argValue*(double)upMulInt); return *this; }
+	FPTemplate& operator-=(const FPTemplate&    argValue) { value -= argValue.value;            return *this; }
+	FPTemplate& operator-=(const int&           argValue) { value -= (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator-=(const unsigned int&  argValue) { value -= (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator-=(const long&          argValue) { value -= (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator-=(const unsigned long& argValue) { value -= (T)(argValue<<upShiftInt); return *this; }
+	FPTemplate& operator-=(const float&         argValue) { value -= int(argValue*(float)upMulInt);  return *this; }
+	FPTemplate& operator-=(const double&        argValue) { value -= int(argValue*(double)upMulInt); return *this; }
+	FPTemplate& operator*=(const FPTemplate&    argValue) { value = FixedMul(value, argValue.value);        return *this; }
+	FPTemplate& operator*=(const int&           argValue) { value = FixedMul(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator*=(const unsigned int&  argValue) { value = FixedMul(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator*=(const long&          argValue) { value = FixedMul(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator*=(const unsigned long& argValue) { value = FixedMul(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator*=(const float&         argValue) { value = FixedMul(value, (type)(argValue*(float)upMulInt));   return *this; }
+	FPTemplate& operator*=(const double&        argValue) { value = FixedMul(value, (type)(argValue*(double)upMulInt));  return *this; }
+	FPTemplate& operator/=(const FPTemplate&    argValue) { value = FixedDiv(value, argValue.value);  return *this; }
+	FPTemplate& operator/=(const int&           argValue) { value = FixedDiv(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator/=(const unsigned int&  argValue) { value = FixedDiv(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator/=(const long&          argValue) { value = FixedDiv(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator/=(const unsigned long& argValue) { value = FixedDiv(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator/=(const float&         argValue) { value = FixedDiv(value, (type)(argValue*(float)upMulInt));   return *this; }
+	FPTemplate& operator/=(const double&        argValue) { value = FixedDiv(value, (type)(argValue*(double)upMulInt));  return *this; }
+	FPTemplate& operator%=(const FPTemplate&    argValue) { value = FixedMod(value, argValue.value);  return *this; }
+	FPTemplate& operator%=(const int&           argValue) { value = FixedMod(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator%=(const unsigned int&  argValue) { value = FixedMod(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator%=(const long&          argValue) { value = FixedMod(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator%=(const unsigned long& argValue) { value = FixedMod(value, argValue<<upShiftInt);  return *this; }
+	FPTemplate& operator%=(const float&         argValue) { value = FixedMod(value, (type)(argValue*(float)upMulInt));   return *this; }
+	FPTemplate& operator%=(const double&        argValue) { value = FixedMod(value, (type)(argValue*(double)upMulInt));  return *this; }
+
+	FPTemplate& operator|=(const FPTemplate& argValue) { value |=  argValue.value;         return *this;}
+	FPTemplate& operator|=(const int&        argValue) { value |= (argValue<<upShiftInt);  return *this;} //Convert Fixed to int, then do operation
+	FPTemplate& operator&=(const FPTemplate& argValue) { value &= argValue.value;          return *this;}
+	FPTemplate& operator&=(const int&        argValue) { value &= (argValue<<upShiftInt);  return *this;} //Convert Fixed to int, then do operation
+	FPTemplate& operator^=(const FPTemplate& argValue) { value ^= argValue.value;          return *this;}
+	FPTemplate& operator^=(const int&        argValue) { value ^= (argValue<<upShiftInt);  return *this;} //Convert Fixed to int, then do operation
+
+	FPTemplate operator<<(int numBits) const { return value << numBits; }
+	FPTemplate operator>>(int numBits) const { return value << numBits; }
+
+	FPTemplate& operator<<=(int numBits) { value <<= numBits; return *this;}
+	FPTemplate& operator>>=(int numBits) { value >>= numBits; return *this;}
+
+	FPTemplate& operator++()    { value += 1<<upShiftInt; return *this; } 
+	FPTemplate& operator--()    { value -= 1<<upShiftInt; return *this; }
+	FPTemplate  operator++(int) { FPTemplate temp(*this); value += 1<<upShiftInt; return temp; }
+	FPTemplate  operator--(int) { FPTemplate temp(*this); value -= 1<<upShiftInt; return temp; }
+
+	FPTemplate  Abs() { if(value<0) return -value; return value; }
+	FPTemplate  DivSafe(const FPTemplate& denominator)       { FPTemplate temp; temp.FromFixed(FixedDivSafe(value, denominator.value)); return temp; }
+	FPTemplate& DivSafeAssign(const FPTemplate& denominator) { value = FixedDivSafe(value, denominator.value); return *this; }
+
+	// These are left public for practical utility purposes.
+	static EASTDC_API T FP_PASCAL FixedMul       (const T t1, const T t2);              // If you are using one of these functions directly or
+	static EASTDC_API T FP_PASCAL FixedDiv       (const T t1, const T t2);              // indirectly through one of the above operators, you 
+	static EASTDC_API T FP_PASCAL FixedDivSafe   (const T t1, const T t2);              // need to define the function in a separate .cpp file.
+	static EASTDC_API T FP_PASCAL FixedMulDiv    (const T t1, const T t2, const T t3);  // The actual implementation may be processor- or compiler-specific. 
+	static EASTDC_API T FP_PASCAL FixedMulDivSafe(const T t1, const T t2, const T t3);  //
+	static EASTDC_API T FP_PASCAL FixedMod       (const T t1, const T t2);              // The 'Safe' versions of functions return the maximum possible
+	static EASTDC_API T FP_PASCAL FixedModSafe   (const T t1, const T t2);              // value when a overflow or division by zero would occur, instead of bombing.
+};
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   FPTemplateType temp;
+   temp.value = t1.value+t2.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const int& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const int& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = t1.value+(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const unsigned int& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const unsigned int& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)+t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const long& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const long& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)+t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const unsigned long& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const unsigned long& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)+t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const float& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(typename FPTemplateType::type)(t2*(float)upMulInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const float& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (typename FPTemplateType::type)(t2*(float)upMulInt)+t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const FPTemplateType& t1, const double& t2){
+   FPTemplateType temp;
+   temp.value = t1.value+(typename FPTemplateType::type)(t2*(double)upMulInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator+(const double& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (typename FPTemplateType::type)(t2*(double)upMulInt)+t1.value;
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   FPTemplateType temp;
+   temp.value = t1.value-t2.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const int& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const int& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)-t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const unsigned int& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const unsigned int& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)-t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const long& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const long& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)-t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const unsigned long& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const unsigned long& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (t2<<upShiftInt)-t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const float& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(typename FPTemplateType::type)(t2*(float)upMulInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const float& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (typename FPTemplateType::type)(t2*(float)upMulInt)-t1.value;
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const FPTemplateType& t1, const double& t2){
+   FPTemplateType temp;
+   temp.value = t1.value-(typename FPTemplateType::type)(t2*(double)upMulInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator-(const double& t2, const FPTemplateType& t1){
+   FPTemplateType temp;
+   temp.value = (typename FPTemplateType::type)(t2*(double)upMulInt)-t1.value;
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, t2.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const int& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const int& t2, const FPTemplateType& t1){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const unsigned int& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const unsigned int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const long& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const unsigned long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const unsigned long& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const float& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, (typename FPTemplateType::type)(t2*(float)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const float& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul((typename FPTemplateType::type)(t2*(float)upMulInt), t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const FPTemplateType& t1, const double& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul(t1.value, (typename FPTemplateType::type)(t2*(double)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator*(const double& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMul((typename FPTemplateType::type)(t2*(double)upMulInt), t1.value);
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, t2.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const int& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const int& t2, const FPTemplateType& t1){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const unsigned int& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const unsigned int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const long& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const unsigned long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const unsigned long& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const float& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, (typename FPTemplateType::type)(t2*(float)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const float& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv((typename FPTemplateType::type)(t2*(float)upMulInt), t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const FPTemplateType& t1, const double& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv(t1.value, (typename FPTemplateType::type)(t2*(double)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator/(const double& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedDiv((typename FPTemplateType::type)(t2*(double)upMulInt), t1.value);
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, t2.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const int& t2){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const int& t2, const FPTemplateType& t1){ 
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const unsigned int& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const unsigned int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const unsigned long& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, t2<<upShiftInt);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const unsigned long& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t2<<upShiftInt, t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const float& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, (typename FPTemplateType::type)(t2*(float)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const float& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod((typename FPTemplateType::type)(t2*(float)upMulInt), t1.value);
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const FPTemplateType& t1, const double& t2){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod(t1.value, (typename FPTemplateType::type)(t2*(double)upMulInt));
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator%(const double& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = FPTemplateType::FixedMod((typename FPTemplateType::type)(t2*(double)upMulInt), t1.value);
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator|(const FPTemplateType& t1, const FPTemplateType& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value | t2.value; 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator|(const FPTemplateType& t1, const int& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value | (t2<<upShiftInt); 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator|(const int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = (t2<<upShiftInt) | t1.value; 
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator&(const FPTemplateType& t1, const FPTemplateType& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value & t2.value; 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator&(const FPTemplateType& t1, const int& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value & (t2<<upShiftInt); 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator&(const int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = (t2<<upShiftInt) & t1.value; 
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+FPTemplateDeclaration
+inline FPTemplateType operator^(const FPTemplateType& t1, const FPTemplateType& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value ^ t2.value; 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator^(const FPTemplateType& t1, const int& t2){
+   FPTemplateType temp; 
+   temp.value = t1.value ^ (t2<<upShiftInt); 
+   return temp;
+}
+
+FPTemplateDeclaration
+inline FPTemplateType operator^(const int& t2, const FPTemplateType& t1){
+   FPTemplateType temp; 
+   temp.value = (t2<<upShiftInt) ^ t1.value; 
+   return temp;
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+FPTemplateDeclaration
+inline FPTemplateType sin(const FPTemplateType& t){ 
+   return FPTemplateType(::sin(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType asin(const FPTemplateType& t){ 
+   return FPTemplateType(::asin(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType cos(const FPTemplateType& t){ 
+   return FPTemplateType(::cos(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType acos(const FPTemplateType& t){ 
+   return FPTemplateType(::acos(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType tan(const FPTemplateType& t){ 
+   return FPTemplateType(::tan(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType atan(const FPTemplateType& t){ 
+   return FPTemplateType(::atan(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType atan2(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   return FPTemplateType(::atan2(t1.AsDouble(), t2.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType atan2(const double& t1, const FPTemplateType& t2){ 
+   return FPTemplateType(::atan2(t1, t2.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType atan2(const FPTemplateType& t1, const double& t2){ 
+   return FPTemplateType(::atan2(t1.AsDouble(), t2));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType sqrt(const FPTemplateType& t){ 
+   return FPTemplateType(::sqrt(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType pow(const FPTemplateType& t1, const FPTemplateType& t2){ 
+   return FPTemplateType(::pow(t1.AsDouble(), t2.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType pow(const double& t1, const FPTemplateType& t2){ 
+   return FPTemplateType(::pow(t1, t2.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType pow(const FPTemplateType& t1, const double& t2){ 
+   return FPTemplateType(::pow(t1.AsDouble(), t2));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType exp(const FPTemplateType& t){ 
+   return FPTemplateType(::exp(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType log(const FPTemplateType& t){ 
+   return FPTemplateType(::log(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType log10(const FPTemplateType& t){ 
+   return FPTemplateType(::log10(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType ceil(const FPTemplateType& t){ 
+   return FPTemplateType(::ceil(t.AsDouble()));
+}
+
+FPTemplateDeclaration
+inline FPTemplateType floor(const FPTemplateType& t){ 
+   return FPTemplateType(::floor(t.AsDouble()));
+}
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Actual types
+//
+// SFixed16_16  --   signed 16:16 fixed point
+// UFixed16_16  -- unsigned 16:16 fixed point
+//
+// SFixed32_32  --   signed 32:32 fixed point
+// UFixed32_32  -- unsigned 32:32 fixed point
+//
+// You can actually define these types externally. However, you will likely 
+// have to provde an implementation of FixedMul, FixedDiv, and FixedMod
+// like we do for SFixed16 and UFixed16 in the .cpp file for this header.
+//
+
+typedef FPTemplate<int32_t,     8, 24,      256,  16777216>   SFixed24;  //  24:8 fixed point (8 bits of fraction)
+typedef FPTemplate<uint32_t,    8, 24,      256,  16777216>   UFixed24;
+
+typedef FPTemplate<int32_t,    10, 22,     1024,   4194304>   SFixed22;   // 22:10 fixed point (10 bits of fraction)
+typedef FPTemplate<uint32_t,   10, 22,     1024,   4194304>   UFixed22;
+
+typedef FPTemplate<int32_t,    12, 20,     4096,   1048576>   SFixed20;   // 20:12 fixed point (12 bits of fraction)
+typedef FPTemplate<uint32_t,   12, 20,     4096,   1048576>   UFixed20;
+
+typedef FPTemplate<int32_t,    14, 18,    16384,    262144>   SFixed18;   // 18:14 fixed point (14 bits of fraction)
+typedef FPTemplate<uint32_t,   14, 18,    16384,    262144>   UFixed18;
+
+typedef FPTemplate<int32_t,    16, 16,    65536,     65536>   SFixed16;   // 16:16 fixed point (16 bits of fraction)
+typedef FPTemplate<uint32_t,   16, 16,    65536,     65536>   UFixed16;
+
+typedef FPTemplate<int32_t,    18, 14,   262144,     16384>   SFixed14;   // 14:18 fixed point (18 bits of fraction)
+typedef FPTemplate<uint32_t,   18, 14,   262144,     16384>   UFixed14;
+
+typedef FPTemplate<int32_t,    20, 12,  1048576,      4096>   SFixed12;   // 12:20 fixed point (20 bits of fraction)
+typedef FPTemplate<uint32_t,   20, 12,  1048576,      4096>   UFixed12;
+
+typedef FPTemplate<int32_t,    22, 10,  4194304,      1024>   SFixed10;   // 10:22 fixed point (22 bits of fraction)
+typedef FPTemplate<uint32_t,   10, 22,  4194304,      1024>   UFixed10;
+
+typedef FPTemplate<int32_t,    24,  8, 16777216,       256>    SFixed8;   //  8:24 fixed point (24 bits of fraction)
+typedef FPTemplate<uint32_t,   24,  8, 16777216,       256>    UFixed8;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define FPDeclareTemplateSpecializations(TypeDef) \
+	template<> EASTDC_API int32_t FP_PASCAL SFixed16::FixedMul(const int32_t a, const int32_t b); \
+	template<> EASTDC_API int32_t FP_PASCAL SFixed16::FixedDiv(const int32_t a, const int32_t b); \
+	template<> EASTDC_API int32_t FP_PASCAL SFixed16::FixedMod(const int32_t a, const int32_t b)
+
+FPDeclareTemplateSpecializations(SFixed16);
+FPDeclareTemplateSpecializations(UFixed16);
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+

+ 806 - 0
include/EAStdC/EAGlobal.h

@@ -0,0 +1,806 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// OS globals are process-wide globals (singletons, actually) and are shared 
+// between an EXE and DLLs, though you can use them on platforms that don't  
+// support DLLs. The Windows OS does not directly support this concept of  
+// global object sharing, as the only way to accomplish this under Windows  
+// is to sequester the global object in its own DLL of which the EXE and all  
+// DLLs explicitly reference. OS Globals allow you to have the global object  
+// anywhere (including the EXE) and does so without requiring explicit linking.  
+// The OS global system works at the operating system level and has  
+// auto-discovery logic so that no pointers or init calls need to be made  
+// between modules for them to link their OS global systems together.  
+// 
+// A primary use of OS globals is in the creation of application singletons  
+// such as the main heap, messaging servers, asset managers, etc. 
+//  
+// Note that the implementation behind OS globals (EAOSGlobal.cpp) may seem  
+// a bit convoluted; this is because it needs to be thread-safe, cross-module,  
+// and independent of application-level memory allocators. For objects for  
+// which order of initialization is clearer and you don't need to reference  
+// singletons across DLLs, EASingleton is probably a better choice, as it is  
+// simpler and has lower overhead. Indeed, another way of looking at OS globals  
+// is to view them as process-wide singletons. 
+//
+// Caveats:
+//     - OS globals have the potential for resulting in code that's duplicated
+//       within each DLL and thus increasing code memory usage. It might be 
+//       useful to use an OS global to store a pointer to an interface rather
+//       than an instance of an interface, and have just a single entity within
+//       the application provide the implementation. With this approach you still
+//       get the benefit of users of the global not having to know ahead of time
+//       where the implementation is as you otherwise need to do with standard
+//       DLL linking via "dllexport."
+//     - OS globals probably aren't needed in cases where you can simply use
+//       dllexport (and other platforms' equivalents).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAGLOBAL_H
+#define EASTDC_EAGLOBAL_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/IntrusiveList.h>
+
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+#include <new>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+#if EASTDC_GLOBALPTR_SUPPORT_ENABLED
+
+namespace EA
+{
+	namespace StdC
+	{
+		///////////////////////////////////////////////////////////////////////////
+		/// GlobalPtr
+		///
+		/// GlobalPtr acts as a reference to a pointer which is global throughout 
+		/// the process (includes the application and any loaded DLLs). The object
+		/// pointed to must define a unique 32-bit kGlobalID if one is not given.
+		/// The pointer is set to NULL on creation.
+		///
+		/// Global pointers may be used from multiple threads once initialized
+		/// to point to an object, but are _not_ thread-safe when being set. 
+		/// If you have a situation where two threads may attempt to set a global
+		/// pointer at the same time, you should use OS globals instead to serialize 
+		/// the creators on the OS global lock and prevent race conditions.
+		///
+		/// A GlobalPtr is not the same thing as simply declaring a pointer at 
+		/// a globally accessible scope, especially on platforms with dynamic
+		/// libraries such as Windows with its DLLs. A GlobalPtr allows multiple
+		/// pieces of code to declare independent pointers to an object, even if
+		/// the pieced of code are in independent DLLs.
+		///
+		/// The pointer assigned to a GlobalPointer need not be a pointer allocated
+		/// dynamically on the heap. It can just as well be the address of some 
+		/// static or local variable.
+		///
+		/// Example usage:
+		///    GlobalPtr<int, 0x11111111> pInteger;
+		///    GlobalPtr<int, 0x11111111> pInteger2;
+		///
+		///    assert(pInteger == NULL);
+		///
+		///    pInteger = new int[2];
+		///    pInteger[0] = 10;
+		///    pInteger[1] = 20;
+		///    assert(pInteger2 == pInteger);
+		///    assert(pInteger2[0] == pInteger[0]);
+		///
+		///    delete[] pInteger;
+		///    pInteger = NULL;
+		///    assert(pInteger2 == NULL);
+		///
+		template<class T, uint32_t kGlobalId = T::kGlobalId>
+		class GlobalPtr
+		{
+		public:
+			/// this_type
+			/// This is an alias for this class.
+			typedef GlobalPtr<T, kGlobalId> this_type;
+
+			/// GlobalPtr
+			///
+			/// Default constructor. Sets member pointer to whatever the 
+			/// shared version is. If this is the first usage of the shared
+			/// version, the pointer will be set to NULL.
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass;
+			///
+			GlobalPtr() 
+				: mppObject(GetStaticPtr())
+			{
+			}
+
+			/// GlobalPtr (copy constructor)
+			///
+			/// Default constructor. Sets member pointer to whatever the 
+			/// shared version is. If this is the first usage of the shared
+			/// version, the pointer will be set to NULL.
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
+			///    pSomeClass1 = new pSomeClass;
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass2(pSomeClass1);
+			///    pSomeClass2->DoSomething();
+			///
+			explicit GlobalPtr(const this_type& /*globalPtr*/)
+				: mppObject(GetStaticPtr())
+			{
+			}
+
+			/// operator =
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
+			///    pSomeClass1 = new pSomeClass;
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass2(pSomeClass1);
+			///    pSomeClass2->DoSomething();
+			///
+			this_type& operator=(const this_type& /*globalPtr*/) {
+				// We need do nothing, as both this and the function argument 
+				// are references to the same thing.
+				// mppObject = globalPtr.mppObject;  
+				return *this;
+			}
+
+			/// operator =
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
+			///    pSomeClass1 = new pSomeClass;
+			///    delete pSomeClass1;
+			///    pSomeClass1 = new pSomeClass;
+			///
+			this_type& operator=(T* p)
+			{
+				mppObject = p;
+				return *this;
+			}
+
+			/// operator T*
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass;
+			///    FunctionWhichUsesSomeClassPtr(pSomeClass);
+			///
+			operator T*() const
+			{
+				return mppObject;
+			}
+
+			/// operator T*
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass;
+			///    CallFunctionWhichUsesSomeClassPtr(pSomeClass);
+			///
+			T& operator*() const
+			{
+				return *mppObject;
+			}
+
+			/// operator ->
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass;
+			///    pSomeClass->DoSomething();
+			///
+			T* operator->() const
+			{ 
+				return  mppObject;
+			}
+
+			/// operator !
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass;
+			///    if(!pSomeClass)
+			///        pSomeClass = new SomeClass;
+			///
+			bool operator!() const
+			{
+				return mppObject == NULL;
+			}
+
+			/// get
+			///
+			/// Returns the owned pointer.
+			///
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass = new SomeClass;
+			///    SomeClass* pSC = pSomeClass.get();
+			///    pSC->DoSomething();
+			///
+			T* get() const
+			{
+				return mppObject;
+			}
+
+			/// Implicit operator bool
+			///
+			/// Allows for using a scoped_ptr as a boolean.
+			/// 
+			/// Example usage:
+			///    GlobalPtr<SomeClass, 0x12345678> pSomeClass = new SomeClass;
+			///    if(pSomeClass)
+			///        pSomeClass->DoSomething();
+			///
+			/// Note that below we do not use operator bool(). The reason for this
+			/// is that booleans automatically convert up to short, int, float, etc.
+			/// The result is that this: if(scopedPtr == 1) would yield true (bad).
+			///
+			///////////////////////////////////////////////////////////////////////
+			// Disabled as long as operatorT*() exists, as these two are 
+			// incompatible. By some arguments, such an operator isn't safe.
+			//
+			// typedef T* (this_type::*bool_)() const;
+			// operator bool_() const
+			// {
+			//     if(mppObject)
+			//         return &this_type::get;
+			//     return NULL;
+			// }
+
+		protected:
+			T*& mppObject;
+
+			static T*& GetStaticPtr();
+		};
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// OSGlobalNode
+		///
+		/// All OS globals must derive from this object or act as if they do.
+		/// If you are using AutoOSGlobalPtr or AutoStaticOSGlobalPtr this 
+		/// derivation is handled for you.
+		///
+		/// Note that above we say 'or act as if they do'. You can make a smart
+		/// pointer container that overrides operator ->, operator *, etc. while
+		/// referring to an actual object that is somewhere else. In fact, the 
+		/// AutoOSGlobalPtr and AutoStaticOSGlobalPtr templates we provide do this.
+		///
+		struct EASTDC_API OSGlobalNode : public EA::StdC::intrusive_list_node
+		{
+			uint32_t mOSGlobalID;          // Globally unique id.
+			uint32_t mOSGlobalRefCount;    // Reference count. This is modified via atomic operations.
+		};
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// OSGlobalFactoryPtr
+		///
+		/// Defines a factory function for a given OSGlobalNode.
+		/// 
+		typedef OSGlobalNode *(*OSGlobalFactoryPtr)();
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// GetOSGlobal
+		///
+		/// Browses for a OS global with the given ID and either returns the
+		/// existing object or attempts to create and register one. Returns NULL
+		/// if the OS global could not be created. This won't happen in practice
+		/// because the memory allocator will have already successfully 
+		/// initialized the OSGlobal system.
+		///
+		/// Each successful call to GetOSGlobal must be matched with a call to
+		/// ReleaseOSGlobal.
+		///
+		/// This function doesn't directly allocate any memory. However, the 
+		/// user-supplied factory function may allocate memory, depending on 
+		/// the implementation of the function.
+		///
+		/// This function can safely be called from multiple threads. 
+		///
+		EASTDC_API OSGlobalNode *GetOSGlobal(uint32_t id, OSGlobalFactoryPtr pFactory);
+
+		/// Kettle doesn't have native support for OS Globals, so instead we have to
+		/// search physical memory for a fixed free location to share globals from
+		/// starting at the very end of virtual memory and working backwards.
+		/// This constant defines the size of the search space. Normally this would
+		/// be a hidden implementation detail, however it's useful for memory systems
+		/// to know where EAStdC might be consuming memory in an otherwise empty heap
+		const uint64_t kKettleOSGlobalSearchSpace = 256 * 1024 * 1024;
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// SetOSGlobal
+		///
+		/// Adds a user-specified OSGlobal. 
+		/// This is useful for setting a specifi instance of an object before
+		/// any automatic creation of the object is done.
+		///
+		EASTDC_API bool SetOSGlobal(uint32_t id, OSGlobalNode *);
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// ReleaseOSGlobal
+		///
+		/// Releases a reference to a OS global obtained from GetOSGlobal.
+		///
+		/// Returns false if the OS global is still in use, and true if the last
+		/// reference was just released. The caller is responsible for destroying
+		/// the OS global in the latter case.
+		///
+		/// This function can safely be called from multiple threads. 
+		///
+		EASTDC_API bool ReleaseOSGlobal(OSGlobalNode *p);
+
+
+
+
+		//////////////////////////////////////////////////////////////////////////
+		/// AutoOSGlobalPtr
+		///
+		/// Holds a reference to an OS global of the specified type and Id. 
+		/// If the OS global does not exist, a new one is created in the 
+		/// shared heap. The Id parameter is an arbitrary guid and allows the
+		/// user to have multiple OSGlobalPtrs of the same stored type T.
+		///
+		/// AutoStaticOSGlobalPtrs and AutoOSGlobalPtrs should not be mixed when
+		/// referring to a OS global. You should reference an OSGlobal via either
+		/// one or more AutoOSGlobalPtrs, one or more AutoStaticOSGlobalPtrs, but
+		/// not both at the same time.
+		///
+		/// OS global lookup is not very fast so the preferred usage of this
+		/// class is to wrap it in an accessor. This also ensures that the
+		/// OS global stays around while created.
+		///
+		/// This class can safely be used from multiple threads. 
+		///
+		/// Example usage:
+		///     AutoOSGlobalPtr<Widget, 0x11111111> gpWidget1A;
+		///     AutoOSGlobalPtr<Widget, 0x11111111> gpWidget1B;
+		///     AutoOSGlobalPtr<Widget, 0x22222222> gpWidget2;
+		///     
+		///     void Foo() {
+		///         assert(gpWidget1A == gpWidget1B);
+		///         assert(gpWidget1A != gpWidget2);
+		///
+		///         gpWidget1A->DoSomething();
+		///         gpWidget2->DoSomething();
+		///     }
+		///
+		/// Example of how to write an accessor wrapper:
+		///     Foo *GetFoo() {
+		///         static AutoOSGlobalPtr<Foo, kFooId> sPtr;
+		///         return sPtr;
+		///     }
+		///
+		///     Better yet, double-wrap it so clients don't have 
+		///     to #include this header:
+		///
+		///     Foo *InlineGetFoo() {
+		///        extern void GetFoo();
+		///        static Foo * const sPtr = GetFoo();
+		///        return sPtr;
+		///     }
+		///
+		template<class T, uint32_t id>
+		class AutoOSGlobalPtr
+		{
+		public:
+			/// value_type
+			/// This is an alias for the contained class T.
+			typedef T value_type;
+
+			/// this_type
+			/// This is an alias for this class.
+			typedef AutoOSGlobalPtr<T, id> this_type;
+
+		public:
+			/// AutoOSGlobalPtr
+			///
+			/// Default constructor. Creates a new version of the OSGlobalPtr
+			/// object if it hasn't been created yet and sets its reference
+			/// count to one.
+			///
+			AutoOSGlobalPtr();
+
+			/// AutoOSGlobalPtr (copy constructor)
+			///
+			/// Copy constructor. Copies the OSGlobalPtr from the source.
+			/// Increases the reference count on the copied OSGlobalPtr.
+			///
+			AutoOSGlobalPtr(const this_type& x);
+
+			/// AutoOSGlobalPtr
+			///
+			/// Decrements the reference count on the held OSGlobalPtr if
+			/// it has been set. Destroys the OSGlobalPtr node if the reference
+			/// count goes to zero.
+			///
+		   ~AutoOSGlobalPtr();
+
+			/// operator =
+			///
+			/// Assignment operator. Copies the OSGlobalPtr from the source.
+			/// Increases the reference count on the copied OSGlobalPtr.
+			/// Decreases the reference count on the previous OSGlobalPtr.
+			///
+			this_type& operator=(const this_type& x);
+
+			/// operator T*
+			///
+			/// Allows this class to act like a pointer.
+			///
+			operator T*() const;
+
+			/// operator *
+			///
+			/// Allows this class to act like a pointer.
+			///
+			T& operator*() const;
+
+			/// operator ->
+			///
+			/// Allows this class to act like a pointer.
+			///
+			T *operator->() const;
+
+			/// get
+			///
+			/// Returns the owned pointer.
+			///
+			T *get() const;
+
+		protected:
+			struct Node : public OSGlobalNode {
+				T mObject;
+				Node() : mObject() {}
+			};
+
+			enum NodeAlignment {
+				kNodeAlignment = EA_ALIGN_OF(Node)
+			};
+
+			static OSGlobalNode *Create();
+
+			Node * const mpOSGlobal;
+		};
+
+
+
+
+		namespace AutoOSGlobalInternal
+		{
+			/// AlignedBuffer
+			///
+			/// The AlignedBuffer template is required due to a weakness of VC++ __declspec(align). 
+			/// In particular, VC++ is only able to use __declspec(align) via an integral constant. 
+			/// Thus, you cannot do this: __declspec(align(__alignof(T))) but can only do things like 
+			/// this __declspec(align(4)). This has the unfortunate result of making it impossible to 
+			/// declare an object X as having the same alignment as object Y. Yet that's what we need
+			/// to do. It turns out that the template constructs below allow for us to work around 
+			/// the VC++ limitation, albeit via some template trickery.
+			///
+			template <size_t size, size_t alignment>  struct AlignedBuffer { typedef char Type[size]; };
+
+			#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900	// VS2015+
+				EA_DISABLE_VC_WARNING(5029);  // nonstandard extension used: alignment attributes in C++ apply to variables, data members and tag types only
+			#endif
+			template<size_t size> struct AlignedBuffer<size, 2>    { typedef EA_PREFIX_ALIGN(2)    char Type[size] EA_POSTFIX_ALIGN(2);    };
+			template<size_t size> struct AlignedBuffer<size, 4>    { typedef EA_PREFIX_ALIGN(4)    char Type[size] EA_POSTFIX_ALIGN(4);    };
+			template<size_t size> struct AlignedBuffer<size, 8>    { typedef EA_PREFIX_ALIGN(8)    char Type[size] EA_POSTFIX_ALIGN(8);    };
+			template<size_t size> struct AlignedBuffer<size, 16>   { typedef EA_PREFIX_ALIGN(16)   char Type[size] EA_POSTFIX_ALIGN(16);   };
+			template<size_t size> struct AlignedBuffer<size, 32>   { typedef EA_PREFIX_ALIGN(32)   char Type[size] EA_POSTFIX_ALIGN(32);   };
+			template<size_t size> struct AlignedBuffer<size, 64>   { typedef EA_PREFIX_ALIGN(64)   char Type[size] EA_POSTFIX_ALIGN(64);   };
+			template<size_t size> struct AlignedBuffer<size, 128>  { typedef EA_PREFIX_ALIGN(128)  char Type[size] EA_POSTFIX_ALIGN(128);  };
+			template<size_t size> struct AlignedBuffer<size, 256>  { typedef EA_PREFIX_ALIGN(256)  char Type[size] EA_POSTFIX_ALIGN(256);  };
+			template<size_t size> struct AlignedBuffer<size, 512>  { typedef EA_PREFIX_ALIGN(512)  char Type[size] EA_POSTFIX_ALIGN(512);  };
+			template<size_t size> struct AlignedBuffer<size, 1024> { typedef EA_PREFIX_ALIGN(1024) char Type[size] EA_POSTFIX_ALIGN(1024); };
+			template<size_t size> struct AlignedBuffer<size, 2048> { typedef EA_PREFIX_ALIGN(2048) char Type[size] EA_POSTFIX_ALIGN(2048); };
+			template<size_t size> struct AlignedBuffer<size, 4096> { typedef EA_PREFIX_ALIGN(4096) char Type[size] EA_POSTFIX_ALIGN(4096); };
+			#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900	// VS2015+
+				EA_RESTORE_VC_WARNING();
+			#endif
+		}
+
+
+
+		///////////////////////////////////////////////////////////////////////////
+		/// AutoStaticOSGlobalPtr
+		///
+		/// Holds a reference to a OS global of the specified type and ID. 
+		/// If the OS global does not exist, a new one is created using static memory.
+		/// The Id parameter is an arbitrary guid and allows the user to have 
+		/// multiple OSGlobalPtrs of the same stored type T.
+		///
+		/// AutoStaticOSGlobalPtrs and AutoOSGlobalPtrs should not be mixed when
+		/// referring to a OS global. You should reference an OSGlobal via either
+		/// one or more AutoOSGlobalPtrs, one or more AutoStaticOSGlobalPtrs, but
+		/// not both at the same time.
+		///
+		/// The advantage of this class is that it uses static memory, so it does
+		/// not contribute to heap usage and always succeeds in allocating the object. 
+		/// The disadvantage is that if multiple DLLs are involved each will have 
+		/// its own static space reserved for the OS global.
+		///
+		/// This class can safely be used from multiple threads. 
+		///
+		/// Example usage:
+		///     AutoStaticOSGlobalPtr<Widget, 0x11111111> gpWidget1A;
+		///     AutoStaticOSGlobalPtr<Widget, 0x11111111> gpWidget1B;
+		///     AutoStaticOSGlobalPtr<Widget, 0x22222222> gpWidget2;
+		///     
+		///     void Foo() {
+		///         assert(gpWidget1A == gpWidget1B);
+		///         assert(gpWidget1A != gpWidget2);
+		///
+		///         gpWidget1A->DoSomething();
+		///         gpWidget2->DoSomething();
+		///     }
+		///
+		template<class T, uint32_t id>
+		class AutoStaticOSGlobalPtr
+		{
+		public:
+			/// value_type
+			/// This is an alias for the contained class T.
+			typedef T value_type;
+
+			/// this_type
+			/// This is an alias for this class.
+			typedef AutoStaticOSGlobalPtr<T, id> this_type;
+
+		public:
+			/// AutoStaticOSGlobalPtr
+			///
+			/// Default constructor. Creates a new version of the OSGlobalPtr
+			/// object if it hasn't been created yet and sets its reference
+			/// count to one.
+			///
+			AutoStaticOSGlobalPtr();
+
+			/// AutoStaticOSGlobalPtr (copy constructor)
+			///
+			/// Copy constructor. Copies the OSGlobalPtr from the source.
+			/// Increases the reference count on the copied OSGlobalPtr.
+			///
+			AutoStaticOSGlobalPtr(const this_type& x);
+
+			/// ~AutoStaticOSGlobalPtr
+			///
+			/// Decrements the reference count on the held OSGlobalPtr if
+			/// it has been set. Destroys the OSGlobalPtr node if the reference
+			/// count goes to zero.
+			///
+		   ~AutoStaticOSGlobalPtr();
+
+			/// operator =
+			///
+			/// Assignment operator. Copies the OSGlobalPtr from the source.
+			/// Increases the reference count on the copied OSGlobalPtr.
+			/// Decreases the reference count on the previous OSGlobalPtr.
+			///
+			this_type& operator=(const this_type& x);
+
+			/// operator T*
+			///
+			/// Allows this class to act like a pointer.
+			///
+			operator T*() const;
+
+			/// operator *
+			///
+			/// Allows this class to act like a pointer.
+			///
+			T& operator*() const;
+
+			/// operator ->
+			///
+			/// Allows this class to act like a pointer.
+			///
+			T* operator->() const;
+
+			/// get
+			///
+			/// Returns the owned pointer.
+			///
+			T* get() const;
+
+		protected:
+			static OSGlobalNode* Create();
+
+			struct Node : public OSGlobalNode {
+				T mObject;
+				Node() : mObject() {}
+			};
+
+			static const size_t kNodeAlignment   = EA_ALIGN_OF(Node);
+			static const int    kStorageTypeSize = sizeof(Node) + (kNodeAlignment - 1); // Add sufficient additional storage space so that we can align the base pointer to the correct boundary in the worst case.
+
+			Node * const mpOSGlobal;
+			static char sStorage[kStorageTypeSize];
+		};
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA
+{
+	namespace StdC
+	{
+
+		template<class T, uint32_t kGlobalId>
+		T*& GlobalPtr<T, kGlobalId>::GetStaticPtr()
+		{
+			// For now, we use the OS global system to hold the pointer. 
+			// This may change in the future. The pointer must be static 
+			// because we need something to hold onto the OS global.
+			// Note: Using static here may be problematic with GCC,  
+			// because it doesn't do static variable shutdown properly.
+			static EA::StdC::AutoOSGlobalPtr<T*, kGlobalId> sSharedPtr;
+
+			return *sSharedPtr;
+		}
+
+
+		///////////////////////////////////////////////////////////////////////////
+		// AutoOSGlobalPtr
+		///////////////////////////////////////////////////////////////////////////
+
+		template <class T, uint32_t id>
+		inline AutoOSGlobalPtr<T, id>::AutoOSGlobalPtr()
+			: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
+		{
+		}
+
+		template <class T, uint32_t id>
+		inline AutoOSGlobalPtr<T, id>::AutoOSGlobalPtr(const this_type&)
+			: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
+		{
+		}
+
+		template <class T, uint32_t id>
+		inline AutoOSGlobalPtr<T, id>::~AutoOSGlobalPtr() {
+			if (ReleaseOSGlobal(mpOSGlobal))
+				delete mpOSGlobal; // Matches the new from Create.
+		}
+
+		template <class T, uint32_t id>
+		inline typename AutoOSGlobalPtr<T, id>::this_type& 
+		AutoOSGlobalPtr<T, id>::operator=(const this_type&) { 
+			// Intentionally do nothing. We should already point to the correct object.
+			return *this;
+		}
+
+		template <class T, uint32_t id>
+		inline AutoOSGlobalPtr<T, id>::operator T*() const {
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T * AutoOSGlobalPtr<T, id>::operator->() const {
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T& AutoOSGlobalPtr<T, id>::operator*()  const {
+			return mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T * AutoOSGlobalPtr<T, id>::get() const {
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline OSGlobalNode * AutoOSGlobalPtr<T, id>::Create() {
+			return EASTDC_NEW_ALIGNED(kNodeAlignment, (size_t)0, EASTDC_ALLOC_PREFIX "OSGlobal") Node;
+		}
+
+
+
+
+		///////////////////////////////////////////////////////////////////////////
+		// AutoStaticOSGlobalPtr
+		///////////////////////////////////////////////////////////////////////////
+
+		template <class T, uint32_t id>
+		inline AutoStaticOSGlobalPtr<T, id>::AutoStaticOSGlobalPtr()
+			: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
+		{
+		}
+
+		template <class T, uint32_t id>
+		inline AutoStaticOSGlobalPtr<T, id>::AutoStaticOSGlobalPtr(const this_type& /*x*/)
+			: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
+		{
+		}
+
+		template <class T, uint32_t id>
+		inline AutoStaticOSGlobalPtr<T, id>::~AutoStaticOSGlobalPtr() {
+			if (ReleaseOSGlobal(mpOSGlobal))
+				mpOSGlobal->~Node();
+		}
+
+		template <class T, uint32_t id>
+		inline typename AutoStaticOSGlobalPtr<T, id>::this_type& 
+		AutoStaticOSGlobalPtr<T, id>::operator=(const this_type&) { 
+			// Intentionally do nothing. We should already point to the correct object.
+			return *this;
+		}
+
+		template <class T, uint32_t id>
+		inline AutoStaticOSGlobalPtr<T, id>::operator T*() const { 
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T * AutoStaticOSGlobalPtr<T, id>::operator->() const {
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T& AutoStaticOSGlobalPtr<T, id>::operator*() const {
+			return mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline T * AutoStaticOSGlobalPtr<T, id>::get() const
+		{
+			return &mpOSGlobal->mObject;
+		}
+
+		template <class T, uint32_t id>
+		inline OSGlobalNode * AutoStaticOSGlobalPtr<T, id>::Create() {
+			char* pStorageAligned = (char*)(((uintptr_t)&sStorage[0] + (kNodeAlignment - 1)) & ~(kNodeAlignment - 1));
+			return ::new(pStorageAligned) Node;
+		}
+
+		// AutoStaticOSGlobalPtr::sStorage declaration
+		template<class T, uint32_t id>
+		char AutoStaticOSGlobalPtr<T, id>::sStorage[kStorageTypeSize];
+
+	} // namespace StdC
+
+} // namespace EA
+
+#endif // EASTDC_GLOBALPTR_SUPPORT_ENABLED
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 197 - 0
include/EAStdC/EAHashCRC.h

@@ -0,0 +1,197 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// Defines the following:
+//    uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue = kCRC16InitialValue, bool bFinalize = true);
+//    uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC24InitialValue, bool bFinalize = true);
+//    uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
+//    uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue = kCRC64InitialValue, bool bFinalize = true);
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAHASHCRC_H
+#define EASTDC_EAHASHCRC_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/// CRC16 (Cyclical Redundancy Code)
+	///
+	/// Classification:
+	///    Binary hash
+	///    CRC16 is not a cryptographic-level hash.
+	///
+	/// Description:
+	///    This is a well-known standardized hash which does a decent
+	///    job of generating non-colliding hash values.
+	///
+	/// You can use this function as a one-shot CRC or iteratively 
+	/// when your data is not contiguous. In the case of iterative 
+	/// calculation, you must set bFinalize to false for all but the 
+	/// final iteration. If you don't know that an iteration is the 
+	/// final iteration until after you have called this function, 
+	/// then you merely need to do this to 'finalize' the crc value:
+	///    crc = ~crc;
+	///
+	/// Example usage (single call):
+	///     uint16_t crc = CRC16(pSomeData, nSomeDataLength);
+	///
+	/// Example usage (multiple successive calls):
+	///     char     pDataArray[32][1024];
+	///     uint16_t crc = kCRC16InitialValue;
+	///
+	///     for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
+	///         crc = CRC16(pDataArray[i], 1024, crc, i == 31);
+	///
+	const uint16_t kCRC16InitialValue = 0xffff;
+
+	EASTDC_API uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue = kCRC16InitialValue, bool bFinalize = true);
+
+
+
+	/// CRC24 (Cyclical Redundancy Code)
+	///
+	/// Classification:
+	///    Binary hash
+	///    CRC24 is not a cryptographic-level hash.
+	/// Description:
+	///    This is a well-known standardized hash which does a decent
+	///    job of generating non-colliding hash values.
+	///
+	/// You can use this function as a one-shot CRC or iteratively 
+	/// when your data is not contiguous. In the case of iterative 
+	/// calculation, you must set bFinalize to false for all but the 
+	/// final iteration. If you don't know that an iteration is the 
+	/// final iteration until after you have called this function, 
+	/// then you merely need to do this to 'finalize' the crc value:
+	///    crc = ~crc;
+	///
+	/// Example usage (single call):
+	///     uint32_t crc = CRC24(pSomeData, nSomeDataLength);
+	///
+	/// Example usage (multiple successive calls):
+	///     char     pDataArray[32][1024];
+	///     uint32_t crc = kCRC24InitialValue;
+	///
+	///     for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
+	///         crc = CRC24(pDataArray[i], 1024, crc, i == 31);
+	///
+	const uint32_t kCRC24InitialValue = 0x00b704ce; // This is the conventionally used initial value.
+
+	EASTDC_API uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC24InitialValue, bool bFinalize = true);
+
+
+
+	/// CRC32 (Cyclical Redundancy Code)
+	///
+	/// Classification:
+	///    Binary hash
+	///    CRC32 is not a cryptographic-level hash.
+	///
+	/// Description:
+	///    This is a well-known standardized hash which does a decent
+	///    job of generating non-colliding hash values.
+	///
+	/// You can use this function as a one-shot CRC or iteratively 
+	/// when your data is not contiguous. In the case of iterative 
+	/// calculation, you must set bFinalize to false for all but the 
+	/// final iteration. If you don't know that an iteration is the 
+	/// final iteration until after you have called this function, 
+	/// then you merely need to do this to 'finalize' the crc value:
+	///    crc = ~crc;
+	///
+	/// Example usage (single call):
+	///     uint32_t crc = CRC32(pSomeData, nSomeDataLength);
+	///
+	/// Example usage (multiple successive calls):
+	///     char     pDataArray[32][1024];
+	///     uint32_t crc = kCRC32InitialValue;
+	///
+	///     for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
+	///         crc = CRC32(pDataArray[i], 1024, crc, i == 31);
+	///
+	const uint32_t kCRC32InitialValue = 0xffffffff;
+
+	EASTDC_API uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
+
+
+	/// CRC32Reverse
+	///
+	/// This implements reverse CRC32 as used by some software.
+	/// See http://en.wikipedia.org/wiki/Cyclic_redundancy_check, for example.
+	///
+	EASTDC_API uint32_t CRC32Reverse(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
+
+
+	/// CRC32Rwstdc
+	/// Provided for compatibility with rwstdc. This version acts identically to the CRC32 
+	/// from the rwstdc package. Users are advised to switch to the regular CRC32 function,
+	/// as it follows the CRC standard and thus is portable. 
+	EASTDC_API uint32_t CRC32Rwstdc(const void* pData, size_t nLength);
+
+
+
+	/// CRC64 (Cyclical Redundancy Code)
+	///
+	/// Classification:
+	///    Binary hash
+	///    CRC64 is not a cryptographic-level hash.
+	///
+	/// Description:
+	///    This is a well-known standardized hash which does a decent
+	///    job of generating non-colliding hash values.
+	///
+	/// You can use this function as a one-shot CRC or iteratively 
+	/// when your data is not contiguous. In the case of iterative 
+	/// calculation, you must set bFinalize to false for all but the 
+	/// final iteration. If you don't know that an iteration is the 
+	/// final iteration until after you have called this function, 
+	/// then you merely need to do this to 'finalize' the crc value:
+	///    crc ^= UINT64_C(0xffffffffffffffff);
+	///
+	/// Example usage (single call):
+	///     uint64_t crc = CRC64(pSomeData, nSomeDataLength);
+	///
+	/// Example usage (multiple successive calls):
+	///     char     pDataArray[32][1024];
+	///     uint64_t crc = kCRC64InitialValue;
+	///
+	///     for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
+	///         crc = CRC64(pDataArray[i], 1024, crc, i == 31);
+	///
+	const uint64_t kCRC64InitialValue = UINT64_C(0xffffffffffffffff);
+
+	EASTDC_API uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue = kCRC64InitialValue, bool bFinalize = true);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 247 - 0
include/EAStdC/EAHashString.h

@@ -0,0 +1,247 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// Defines the following:
+//    uint32_t FNV1         (const void*     pData, size_t nLength, uint32_t nInitialValue = kFNV1InitialValue);
+//    uint32_t FNV1_String8 (const char8_t*  pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+//    uint32_t FNV1_String16(const char16_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+//    
+//    uint64_t FNV64         (const void*     pData, size_t nLength, uint64_t nInitialValue = kFNV1InitialValue);
+//    uint64_t FNV64_String8 (const char8_t*  pData, uint64_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+//    uint64_t FNV64_String16(const char16_t* pData, uint64_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+//    
+//    template<> class CTStringHash;
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAHASHSTRING_H
+#define EASTDC_EAHASHSTRING_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/// CharCase
+	/// 
+	/// Defines an enumeration that refers to character case, such as 
+	/// upper case and lower case. Hashing functions that work on text
+	/// (not necessarily just string hashes) use this enumeration.
+	///
+	enum CharCase
+	{
+		kCharCaseAny,   /// Treat the text to be hashed as it is.
+		kCharCaseLower, /// Treat the text to be hashed as lower-case.
+		kCharCaseUpper  /// Treat the text to be hashed as upper-case.
+	};
+
+
+	/// FNV1 (Fowler / Noll / Vo)
+	///
+	/// Reference:
+	///   http://www.isthe.com/chongo/tech/comp/fnv/
+	///
+	/// Classification:
+	///   String hash. 
+	///   FNV1 is not a cryptographic-level hash.
+	///
+	/// Description:
+	///   FNV hashes are designed primarily to hash strings quickly.
+	///   They are very fast, as the algorithm uses one multiply and one 
+	///   xor per charcter. These hashes are just a little bit slower than
+	///   DJB2 hashes on most hardware but yield significantly better 
+	///   dispersion and thus lower collision rates.
+	///   Note that we implement the FNV1 algorithm and not the FNV1a 
+	///   variation. For most data sets there is not a benefit with 
+	///   the FNV1a variation.
+	///
+	/// Notes:
+	///   This string hash hashes the characters, not the binary bytes. 
+	///   As a result, the hash of "hello" and EA_CHAR16("hello") will yield the 
+	///   same result. 
+	///
+	/// Algorithm:
+	///   This hash is basically a loop like this:
+	///      while(char c = *pData++)
+	///         hash = (hash * 16777619) ^ c;
+	///
+	const uint32_t kFNV1InitialValue = 2166136261U;
+
+	EASTDC_API uint32_t FNV1         (const void*     pData, size_t nLength, uint32_t nInitialValue = kFNV1InitialValue);
+	EASTDC_API uint32_t FNV1_String8 (const char8_t*  pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+	EASTDC_API uint32_t FNV1_String16(const char16_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+	EASTDC_API uint32_t FNV1_String32(const char32_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
+
+
+	/// FNV64
+	/// 64 bit version of the FNV1 algorithm.
+	const uint64_t kFNV64InitialValue = UINT64_C(14695981039346656037);
+
+	EASTDC_API uint64_t FNV64         (const void*     pData, size_t nLength, uint64_t nInitialValue = kFNV64InitialValue);
+	EASTDC_API uint64_t FNV64_String8 (const char8_t*  pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
+	EASTDC_API uint64_t FNV64_String16(const char16_t* pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
+	EASTDC_API uint64_t FNV64_String32(const char32_t* pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
+
+
+
+	// DJB2 function is deprecated, as FNV1 has been shown to be superior.
+	const uint32_t kDJB2InitialValue = 5381;
+
+	EASTDC_API uint32_t DJB2         (const void*     pData, size_t nLength, uint32_t nInitialValue = kDJB2InitialValue);
+	EASTDC_API uint32_t DJB2_String8 (const char8_t*  pData, uint32_t nInitialValue = kDJB2InitialValue, CharCase charCase = kCharCaseAny);
+	EASTDC_API uint32_t DJB2_String16(const char16_t* pData, uint32_t nInitialValue = kDJB2InitialValue, CharCase charCase = kCharCaseAny);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+#ifdef EA_COMPILER_MSVC
+	#pragma warning(push)
+	#pragma warning(disable: 4307) // Integer constant overflow.
+	#pragma warning(disable: 4309) // Integer constant overflow.
+	#pragma warning(disable: 4310) // Integer constant overflow.
+#endif
+
+#define M(x)   (x) // This is a placeholder for modification of x.
+#define H(x,h) ((uint32_t)(h * (uint64_t)16777619) ^ x)
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/////////////////////////////////////////////////////////////////////////////////
+	/// CTStringHash
+	///
+	/// Compile-time string hash. Generates FNV1 string hashes via compile-time 
+	/// template expansion. Both single byte and Unicode characters are accepted.
+	///
+	/// Example usage:
+	///    const uint32_t h = CTStringHash<'T', 'e', 's', 't'>::value;
+	///
+	/// Example usage:
+	///    void DoSomething(const char* pString) {
+	///        switch(FNV1_String8(pString)) {
+	///           case CTStringHash<'T', 'e', 's', 't'>::value:
+	///               break;
+	///           case CTStringHash<'H', 'e', 'l', 'l', 'o'>::value:
+	///               break;
+	///        }
+	///    }
+	///
+	/// If many string constants need to be CTStringHash'd, the following Python script may be useful:
+	///    def s(str):
+	///    """
+	///        Splits a string into a string of its constituent characters: "abc" --> "'a', 'b', 'c'"
+	///    """
+	///        splitStr = ""
+	///        for c in str:
+	///            splitStr += "'%s', " % c
+	///        print splitStr[:-2]
+	///
+	///    Example usage:
+	///        s("string") // yields 's', 't', 'r', 'i', 'n', 'g'
+	///
+	template<int c00=0, int c01=0, int c02=0, int c03=0, int c04=0, int c05=0, int c06=0, int c07=0, 
+			 int c08=0, int c09=0, int c10=0, int c11=0, int c12=0, int c13=0, int c14=0, int c15=0, 
+			 int c16=0, int c17=0, int c18=0, int c19=0, int c20=0, int c21=0, int c22=0, int c23=0, 
+			 int c24=0, int c25=0, int c26=0, int c27=0, int c28=0, int c29=0, int c30=0, int c31=0>
+	class CTStringHash
+	{
+	private:
+		enum {
+			V00=0x811c9dc5,
+			V01=H(M(c00),V00), V02=H(M(c01),V01), V03=H(M(c02),V02), V04=H(M(c03),V03), V05=H(M(c04),V04),
+			V06=H(M(c05),V05), V07=H(M(c06),V06), V08=H(M(c07),V07), V09=H(M(c08),V08), V10=H(M(c09),V09),
+			V11=H(M(c10),V10), V12=H(M(c11),V11), V13=H(M(c12),V12), V14=H(M(c13),V13), V15=H(M(c14),V14),
+			V16=H(M(c15),V15), V17=H(M(c16),V16), V18=H(M(c17),V17), V19=H(M(c18),V18), V20=H(M(c19),V19),
+			V21=H(M(c20),V20), V22=H(M(c21),V21), V23=H(M(c22),V22), V24=H(M(c23),V23), V25=H(M(c24),V24),
+			V26=H(M(c25),V25), V27=H(M(c26),V26), V28=H(M(c27),V27), V29=H(M(c28),V28), V30=H(M(c29),V29),
+			V31=H(M(c30),V30), V32=H(M(c31),V31)
+		};
+
+	public:
+		static const uint32_t value = (uint32_t)
+			((c00==0)?V00:((c01==0)?V01:((c02==0)?V02:((c03==0)?V03:((c04==0)?V04:((c05==0)?V05:
+			((c06==0)?V06:((c07==0)?V07:((c08==0)?V08:((c09==0)?V09:((c10==0)?V10:((c11==0)?V11:
+			((c12==0)?V12:((c13==0)?V13:((c14==0)?V14:((c15==0)?V15:((c16==0)?V16:((c17==0)?V17:
+			((c18==0)?V18:((c19==0)?V19:((c20==0)?V20:((c21==0)?V21:((c22==0)?V22:((c23==0)?V23:
+			((c24==0)?V24:((c25==0)?V25:((c26==0)?V26:((c27==0)?V27:((c28==0)?V28:((c29==0)?V29:
+			((c30==0)?V30:((c31==0)?V31:V32))))))))))))))))))))))))))))))));
+	};
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#undef H
+#undef M
+
+#ifdef EA_COMPILER_MSVC
+	#pragma warning(pop)
+#endif
+
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Edit-time string hash Visual Studio macro
+// 
+// This macro replaces the currently selected text with an FNV1 hash of it.
+// It does not understand the concept of escaped characters such as \t, as it 
+// merely computes the hash of the \ and t characters indepdendently here.
+//
+/////////////////////////////////////////////////////////////////////////////////
+//   Sub FNV1_32Bit()
+//       Dim i As Integer
+//       Dim iEnd As Integer
+//       Dim FNV1String As String
+//       Dim FNV1Value As Long
+//       Dim CharAscii As Integer
+//       Dim sResult As String
+//
+//       FNV1String = ActiveDocument().Selection.text()
+//       FNV1Value = 2166136261
+//
+//       For i = 1 To Len(FNV1String)
+//           CharAscii = Asc(Mid(FNV1String, i, 1))
+//           FNV1Value = (FNV1Value * 16777619) Xor CharAscii
+//           FNV1Value = FNV1Value And 4294967295
+//       Next
+//
+//       sResult = "0x{0,8:x8}"
+//       sResult = sResult.Format(sResult, FNV1Value)
+//       ActiveDocument.Selection.Insert(sResult)
+//   End Sub
+/////////////////////////////////////////////////////////////////////////////////
+
+
+
+#endif // header sentry
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 480 - 0
include/EAStdC/EAMathHelp.h

@@ -0,0 +1,480 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// Implements fast, specialized scalar math primitives. This is not a general
+// purpose vector math library, but rather simply a portable version of some
+// basic FPU primitives.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAMATHHELP_H
+#define EASTDC_EAMATHHELP_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+	typedef float float32_t;
+	typedef double float64_t;
+
+	union FloatUint32
+	{
+		uint32_t i;
+		float    f;
+	};
+
+	union DoubleUint64
+	{
+		uint64_t i;
+		double   f;
+	};
+
+	///////////////////////////////////////////////////////////////////////////////
+	// Constants
+	//
+	const uint32_t     kFloat32SignMask                = UINT32_C(0x80000000);
+	const uint32_t     kFloat32ExponentMask            = UINT32_C(0x7F800000);
+	const uint32_t     kFloat32MantissaMask            = UINT32_C(0x007FFFFF);
+	const uint32_t     kFloat32SignAndExponentMask     = UINT32_C(0xFF800000);
+	const uint32_t     kFloat32SignAndMantissaMask     = UINT32_C(0x807FFFFF);
+	const uint32_t     kFloat32ExponentAndMantissaMask = UINT32_C(0x7FFFFFFF);
+	const uint32_t     kFloat32PositiveInfinityBits    = UINT32_C(0x7F800000);
+	const unsigned     kFloat32SignBits                = 1;
+	const unsigned     kFloat32ExponentBits            = 8;
+	const unsigned     kFloat32MantissaBits            = 23;
+	const unsigned     kFloat32BiasValue               = 127;
+
+	const uint64_t     kFloat64SignMask                = UINT64_C(0x8000000000000000);
+	const uint64_t     kFloat64ExponentMask            = UINT64_C(0x7FF0000000000000);
+	const uint64_t     kFloat64MantissaMask            = UINT64_C(0x000FFFFFFFFFFFFF);
+	const uint64_t     kFloat64SignAndExponentMask     = UINT64_C(0xFFF0000000000000);
+	const uint64_t     kFloat64SignAndMantissaMask     = UINT64_C(0x800FFFFFFFFFFFFF);
+	const uint64_t     kFloat64ExponentAndMantissaMask = UINT64_C(0x7FFFFFFFFFFFFFFF);
+	const uint64_t     kFloat64PositiveInfinityBits    = UINT64_C(0x7FF0000000000000);
+	const unsigned     kFloat64SignBits                = 1;
+	const unsigned     kFloat64ExponentBits            = 11;
+	const unsigned     kFloat64MantissaBits            = 52;
+	const unsigned     kFloat64BiasValue               = 1023;
+
+	const FloatUint32  kInfinityUnion32                = { kFloat32PositiveInfinityBits };
+	const DoubleUint64 kInfinityUnion64                = { kFloat64PositiveInfinityBits };
+	#define            kFloat32Infinity                  kInfinityUnion32.f
+	#define            kFloat64Infinity                  kInfinityUnion64.f
+
+
+	// bias to integer
+	const float32_t kFToIBiasF32 = (float32_t)(uint32_t(3) << 22);
+	const int32_t   kFToIBiasS32 = 0x4B400000;           // Same as ((int32_t&) kFToIBiasF32), but known to optimizer at compile time.
+	const float64_t kFToIBiasF64 = (float64_t)(uint64_t(3) << 52);
+
+	// bias to 8-bit fraction
+	const float32_t kFToI8BiasF32 = (float32_t)(uint32_t(3) << 14);
+	const int32_t   kFToI8BiasS32 = 0x47400000;          // Same as ((int32_t&) kFToI8BiasF32), but known to optimizer at compile time.
+
+	// bias to 16-bit fraction
+	const float32_t kFToI16BiasF32 = (float32_t)(uint32_t(3) << 6);
+	const int32_t   kFToI16BiasS32 = 0x43400000;         // Same as ((int32_t&) kFToI16BiasF32), but known to optimizer at compile time.
+
+
+	///////////////////////////////////////////////////////////////////////
+	// MulDiv
+	//
+	// Returns the result of the expression a * b / divisor without loss of
+	// precision. This is useful because any operation of two (e.g.) 32 bit
+	// values can generate a 64 bit result.
+	// divisor cannot be 0.
+	//
+	// Example usage:
+	//     int64_t result = MulDiv(INT64_C(0x8000000000000000), 
+	//                             INT64_C(0x8000000000000000), 
+	//                             INT64_C(0x4000000000000000));
+	//     // result -> 0x10000000000000000
+	//
+	int32_t  MulDiv(int32_t  a, int32_t  b, int32_t  divisor);
+	uint32_t MulDiv(uint32_t a, uint32_t b, uint32_t divisor);
+	int64_t  MulDiv(int64_t  a, int64_t  b, int64_t  divisor);
+	uint64_t MulDiv(uint64_t a, uint64_t b, uint64_t divisor);    
+ 
+	
+	///////////////////////////////////////////////////////////////////////
+	// DivMod
+	//
+	// Returns the integer result of dividend / divisor and dividend % divisor 
+	// in a single operation.
+	// This is useful because division is slow on some platforms and performing
+	// a div and mod at the same time will be faster than performing both 
+	// separately. ARM processors don't have an integer divide function even
+	// in 32 bit mode, and most processors don't have a 64 bit integer divide
+	// function in 32 bit mode. In those cases this function is a potential 2x win.
+	// This function is similar to the C Standard div, ldiv, and lldiv functions
+	// except with unsigned versions and portable to all compilers/platforms.
+	// Returns the result of the division. The modulus (remainder of the division) 
+	// is written to modResult.
+	// divisor cannot be 0, and modResult must be valid.
+	//
+	// Example usage:
+	//     int64_t result, remainder;
+	//     result = DivMod(14, 3, &remainder); 
+	//     // result -> 4, remainder-> 2.
+	//
+	int32_t  DivMod(int32_t  dividend, int32_t  divisor, int32_t*  modResult);
+	uint32_t DivMod(uint32_t dividend, uint32_t divisor, uint32_t* modResult);
+	int64_t  DivMod(int64_t  dividend, int64_t  divisor, int64_t*  modResult);
+	uint64_t DivMod(uint64_t dividend, uint64_t divisor, uint64_t* modResult);    
+
+
+	///////////////////////////////////////////////////////////////////////
+	// Full range conversion functions.
+	//
+	// These are good for floats within the full range of a float. Remember
+	// that a single-precision float only has a 24-bit significand so
+	// most integers |x| > 2^24 cannot be represented exactly.
+	//
+	// The result of converting an out-of-range number, infinity, or NaN
+	// is undefined.
+	//
+	inline uint32_t RoundToUint32(float fValue);
+	inline int32_t RoundToInt32(float fValue);
+	inline int32_t FloorToInt32(float fValue);
+	inline int32_t CeilToInt32(float fValue);
+	inline int32_t TruncateToInt32(float fValue);
+
+	///////////////////////////////////////////////////////////////////////
+	// Partial range conversion functions.
+	//
+	// These are only good for |x| <= 2^23. The result of converting an
+	// out-of-range number, infinity, or NaN is undefined.
+	//
+	inline int32_t FastRoundToInt23(float fValue);
+
+	///////////////////////////////////////////////////////////////////////
+	// Unit-to-byte functions.
+	//
+	// Converts real values in the range |x| <= 1 to unsigned 8-bit values
+	// [0, 255]. The result of calling UnitFloatToUint8() with |x|>1 is
+	// undefined.
+	//
+	inline uint8_t UnitFloatToUint8(float fValue);
+	inline uint8_t ClampUnitFloatToUint8(float fValue);
+
+	///////////////////////////////////////////////////////////////////////
+	// IsInvalid
+	//
+	// Returns true if a value does not obey normal arithmetic rules;
+	// specifically, x != x. In the case of Visual C++ 2003, this is true
+	// for NaNs and indefinites, and not for normalized finite values,
+	// denormals, and infinities. Other compilers may return different
+	// results or even false for all values.
+	//
+	// IsInvalid() is useful as a fast assert check that floats are
+	// sane and won't poison computations as NaNs can with masked
+	// exceptions.
+	//
+	inline bool IsInvalid(float32_t fValue);
+	inline bool IsInvalid(float64_t fValue);
+
+	///////////////////////////////////////////////////////////////////////////////
+	// IsNormal
+	//
+	// Returns true if the value is a normalized finite number. That is, it is neither
+	// an infinite, nor a NaN (including indefinite NaN), nor a denormalized number.
+	// You generally want to write math operation checking code that asserts for 
+	// IsNormal() as opposed to checking specifically for IsNaN, etc.
+	//
+	// Normal values are defined as any floating point value with an exponent in 
+	// the range of [1, 254], as 0 is reserved for denormalized (underflow) values 
+	// and 255 is reserved for infinite (overflow) and NaN values. 
+	//
+	inline bool IsNormal(float32_t fValue);
+	inline bool IsNormal(float64_t fValue);
+
+	///////////////////////////////////////////////////////////////////////////////
+	// IsNAN
+	//
+	// A NaN is a special kind of number that is neither finite nor infinite. 
+	// It is the result of doing things like the following:
+	//    float x = 1 * NaN;
+	//    float x = NaN + NaN;
+	//    float x = 0 / 0;
+	//    float x = 0 / infinite;
+	//    float x = infinite - infinite
+	//    float x = sqrt(-1);
+	//    float x = cos(infinite);
+	// Under the VC++ debugger, x will be displayed as 1.#QNAN00 or 1.#IND00 and 
+	// the bit representation of x will be 0x7fc00001 (in the case of 1 * NaN). 
+	// The 'Q' in front of NAN stands for "quiet" and means that use of that value 
+	// in expressions won't generate exceptions. A signaling NaN (SNAN) means that 
+	// use of the value would generate exceptions.
+	// 
+	// NaNs are frequently generated in physics simulations and similar mathematical 
+	// situations when you are simulating an object moving or turning over time but 
+	// the time or distance differential in the calculation is very small. 
+	// Also, floating point roundoff error can generate NaNs if you do things 
+	// like call acos(x) where you didn't take care to clamp x to <= 1. You can 
+	// also get a NaN when memory used to store a floating point value is written 
+	// with random data.
+	//
+	// A curious property of NaNs is that all comparisons between NaNs return 
+	// false except the expression: NaN != NaN. This is so even if the bit 
+	// representation of the two compared NaNs are identical. Thus, with NaNs, 
+	// the following holds:
+	//    x == x is always false
+	//    x < y is always false
+	//    x > y is always false
+	//
+	// As a result, one simple way to test for a NaN without fiddling with bits is 
+	// to simply test for x == x. If this returns false, then you have a NaN. 
+	// Unfortunately, many C and C++ compilers don't obey this, so you are usually 
+	// stuck fiddling with bits.
+	//
+	// With a NaN, all exponent bits are 1 and the mantissa is not zero.
+	// If the highest fraction bit is 1, the NAN is "quiet" -- it represents
+	// and indeterminant operation rather than an invalid one.
+	//
+	inline bool IsNAN(float32_t fValue);
+	inline bool IsNAN(float64_t fValue);
+
+	///////////////////////////////////////////////////////////////////////////////
+	// IsInfinite
+	//
+	// A value is infinity if the exponent bits are all 1 and all the bits of the 
+	// mantissa (significand) are 0. The sign bit indicates positive or negative
+	// infinity. Thus, for Float32, 0x7f800000 is positive infinity and 0xff800000
+	// is negative infinity.
+	//
+	inline bool IsInfinite(float32_t fValue);
+	inline bool IsInfinite(float64_t fValue);
+
+	///////////////////////////////////////////////////////////////////////////////
+	// IsIndefinite
+	//
+	// An indefinite is a special kind of NaN that is used to signify that an 
+	// operation between non-NaNs generated a NaN. Other than that, it really is 
+	// simply another NaN.
+	//
+	inline bool IsIndefinite(float32_t fValue);
+	inline bool IsIndefinite(float64_t fValue);
+
+	///////////////////////////////////////////////////////////////////////////////
+	// IsDenormalized
+	//
+	// Much in the same way that infinite numbers represent an overflow, 
+	// denormalized numbers represent an underflow. A denormalized number is 
+	// indicated by an exponent with a value of zero. You get a denormalized 
+	// number when you do operations such as this:
+	//    float x = 1e-10 / 1e35;
+	// Under the VC++ debugger, x will be displayed as 1.4e-045#DEN and the 
+	// bit representation of x will be 0x00000001. Unlike infinites and NaNs, 
+	// you can still do math with denormalized numbers. However, the results 
+	// of your math will likely have a lot of imprecision. You can also get a 
+	// denormalized value when memory used to store a floating point value is 
+	// written with random data.
+	//
+	inline bool IsDenormalized(float32_t fValue);
+	inline bool IsDenormalized(float64_t fValue);
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+// Common implementations.
+namespace EA
+{
+namespace StdC
+{
+	const int32_t kFloat32LowestExponentBit = kFloat32ExponentMask & (~kFloat32ExponentMask << 1);
+	const int64_t kFloat64LowestExponentBit = kFloat64ExponentMask & (~kFloat64ExponentMask << 1);
+
+	inline bool IsInvalid(float32_t fValue) {
+		return fValue != fValue;
+	}
+
+	inline bool IsInvalid(float64_t fValue) {
+		return fValue != fValue;
+	}
+
+	inline bool IsNormal(float32_t fValue) {
+		const union {
+			float32_t f;
+			int32_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is normalized finite if its exponent field
+		// is not either all zeroes or ones, or if the number is zero;
+		return !(converter.i & ~kFloat32SignMask) || ((converter.i - (uint32_t)kFloat32LowestExponentBit) & kFloat32ExponentMask) < (kFloat32ExponentMask - kFloat32LowestExponentBit);
+	}
+
+	inline bool IsNormal(float64_t fValue) {
+		const union {
+			float64_t f;
+			int64_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is normalized finite if its exponent field
+		// is not either all zeroes or ones.
+		return !(converter.i & ~kFloat64SignMask) || ((converter.i - (uint64_t)kFloat64LowestExponentBit) & kFloat64ExponentMask) < (kFloat64ExponentMask - kFloat64LowestExponentBit);
+	}
+
+	inline bool IsNAN(float32_t fValue) {
+		const union {
+			float32_t f;
+			int32_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is a NaN if all exponent bits are one and
+		// the mantissa is not zero.
+		return (converter.i & ~kFloat32SignMask) > kFloat32ExponentMask;
+	}
+
+	inline bool IsNAN(float64_t fValue) {
+		const union {
+			float64_t f;
+			int64_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is a NaN if all exponent bits are one and
+		// the mantissa is not zero.
+		return (converter.i & ~kFloat64SignMask) > kFloat64ExponentMask;
+	}
+
+	inline bool IsInfinite(float32_t fValue) {
+		const union {
+			float32_t f;
+			int32_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is infinite if all exponent bits are one
+		// and the mantissa is zero.
+		return (converter.i & ~kFloat32SignMask) == kFloat32PositiveInfinityBits;
+	}
+
+	inline bool IsInfinite(float64_t fValue) {
+		const union {
+			float64_t f;
+			int64_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is infinite if all exponent bits are one
+		// and the mantissa is zero.
+		return (converter.i & ~kFloat64SignMask) == kFloat64PositiveInfinityBits;
+	}
+
+	inline bool IsIndefinite(float32_t fValue) {
+		const union {
+			float32_t f;
+			int32_t i;
+		} converter = { fValue };
+
+		return converter.i == (int32_t)UINT32_C(0xFFC00000);
+	}
+
+	inline bool IsIndefinite(float64_t fValue) {
+		const union {
+			float64_t f;
+			int64_t i;
+		} converter = { fValue };
+
+		return converter.i == (int64_t)UINT64_C(0xFFF8000000000000);
+	}
+
+	inline bool IsDenormalized(float32_t fValue) {
+		const union {
+			float32_t f;
+			int32_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is denormalized if its exponent is zero and the
+		// mantissa is non-zero.
+		return (uint32_t)((converter.i & ~kFloat32SignMask) - 1) < kFloat32MantissaMask;
+	}
+
+	inline bool IsDenormalized(float64_t fValue) {
+		const union {
+			float64_t f;
+			int64_t i;
+		} converter = { fValue };
+
+		// An IEEE real value is denormalized if its exponent is zero and the
+		// mantissa is non-zero.
+		return (uint64_t)((converter.i & ~kFloat64SignMask) - 1) < kFloat64MantissaMask;
+	}
+
+} // namespace StdC
+} // namespace EA
+
+
+
+#if   defined(EA_PLATFORM_MICROSOFT)
+	#include <EAStdC/Win32/EAMathHelpWin32.inl>
+#else
+	#define EAMATHHELP_MODE_REFERENCE 1 // Use the reference implementation, which has no optimizations.
+#endif
+
+#if defined(EAMATHHELP_MODE_REFERENCE) && EAMATHHELP_MODE_REFERENCE
+	EA_DISABLE_ALL_VC_WARNINGS()
+	#include <math.h>
+	EA_RESTORE_ALL_VC_WARNINGS()
+
+	namespace EA
+	{
+	namespace StdC
+	{
+		EA_NO_UBSAN inline uint32_t RoundToUint32(float32_t fValue) {
+			return (uint32_t)floorf(fValue + 0.5f);
+		}
+
+		inline int32_t RoundToInt32(float32_t fValue) {
+			return (int32_t)floorf(fValue + 0.5f);
+		}
+
+		inline int32_t FloorToInt32(float32_t fValue) {
+			return (int32_t)floorf(fValue);
+		}
+
+		inline int32_t CeilToInt32(float32_t fValue) {
+			return (int32_t)ceilf(fValue);
+		}
+
+		inline int32_t TruncateToInt32(float32_t fValue) {
+			return (int32_t)fValue;
+		}
+
+		EA_NO_UBSAN inline uint8_t UnitFloatToUint8(float fValue) {
+			return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
+		}
+
+		inline uint8_t ClampUnitFloatToUint8(float fValue) {
+			if (fValue < 0.0f)
+				fValue = 0.0f;
+			else if (fValue > 1.0f)
+				fValue = 1.0f;
+
+			return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
+		}
+
+		// This function is deprecated, as it's not very useful any more.
+		inline int32_t FastRoundToInt23(float32_t fValue) {
+			return (int32_t)floorf(fValue + 0.5f);
+		}
+
+	} // namespace StdC
+	} // namespace EA
+
+
+#endif // EAMATHHELP_MODE_REFERENCE
+
+
+#endif // HEader include guard
+
+
+
+

+ 681 - 0
include/EAStdC/EAMemory.h

@@ -0,0 +1,681 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This module implements the following functions which provide 
+// implementations of basic memory manipulation functions.
+//
+// Note the presence of "C" functions below such as MemcpyC. These refer to 
+// memory functions that operate only on cacheable memory, but are faster 
+// than otherwise. Cacheable memory is standard system RAM used by applications
+// and is the memory you work with 98% of the time. However, on some hardware
+// systems (e.g. gaming console machines) there is uncacheable memory, such as
+// memory that is mapped to video addresses. This memory is typically called
+// write-through or write-combined memory and is useful for writing data in 
+// one direction from system RAM to video memory. The "C" functions do not
+// work on this kind of memory and you can instead use the regular functions
+// such as Memcpy for uncacheable memory.
+//
+//      char8_t*    Memcpy     (void* pDestination, const void* pSource, size_t n);
+//      char8_t*    MemcpyC    (void* pDestination, const void* pSource, size_t n);     // Faster version for cacheable memory (and not video memory).
+//      char8_t*    MemcpyS    (void* pDestination, const void* pSource, size_t n);     // Streaming memory copy, doesn't invalidate the cache.
+//      char8_t*    Memcpy128  (void* pDestination, const void* pSource, size_t n);
+//      char8_t*    Memcpy128C (void* pDestination, const void* pSource, size_t n);     // Faster version for cacheable memory (and not video memory).
+//      char8_t*    Memmove    (void* pDestination, const void* pSource, size_t n);
+//      char8_t*    MemmoveC   (void* pDestination, const void* pSource, size_t n);     // Faster version for cacheable memory (and not video memory).
+//
+//      const void* Memchr(const void* p, char8_t c, size_t n);
+//      int         Memcmp(const void* p1, const void* p2, size_t n);
+//      void*       Memmem(const void* pMemory, size_t memorySize, const void* pFind, size_t findSize);
+//
+//      uint8_t*    Memset8      (void* pDestination, uint8_t  c, size_t uint8Count);
+//      uint8_t*    Memset8C     (void* pDestination, uint8_t  c, size_t uint8Count);   // Faster version for cacheable memory (and not video memory).
+//      uint8_t*    Memset8_128  (void* pDestination, uint8_t  c, size_t uint8Count);
+//      uint8_t*    Memset8_128C (void* pDestination, uint8_t  c, size_t uint8Count);   // Faster version for cacheable memory (and not video memory).
+//      uint16_t*   Memset16     (void* pDestination, uint16_t c, size_t uint16Count);
+//      uint32_t*   Memset32     (void* pDestination, uint32_t c, size_t uint32Count);
+//      uint64_t*   Memset64     (void* pDestination, uint64_t c, size_t uint64Count);
+//      void*       MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount)
+//      void*       MemsetN      (void* pDestination, const void* pSource, size_t sourceBytes, size_t nCount);
+//      void        Memclear     (void* pDestination, size_t n);
+//      void        MemclearC    (void* pDestination, size_t n);                        // Faster version for cacheable memory (and not video memory).
+//
+//      void        Memfill8       (void* pDestination, uint8_t c,  size_t byteCount);  // Same as Memset8
+//      void        Memfill16      (void* pDestination, uint16_t c, size_t byteCount);
+//      void        Memfill24      (void* pDestination, uint32_t c, size_t byteCount);
+//      void        Memfill32      (void* pDestination, uint32_t c, size_t byteCount);
+//      void        Memfill64      (void* pDestination, uint64_t c, size_t byteCount);
+//      void        MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount);
+//
+//      uint8_t*    Memcheck8      (void* pDestination, uint8_t  c, size_t byteCount);
+//      uint16_t*   Memcheck16     (void* pDestination, uint16_t c, size_t byteCount);
+//      uint32_t*   Memcheck32     (void* pDestination, uint32_t c, size_t byteCount);
+//      uint64_t*   Memcheck64     (void* pDestination, uint64_t c, size_t byteCount);
+//
+//      bool        TimingSafeMemEqual(const void* p1, const void* p2, size_t n);
+//      int         TimingSafeMemcmp(const void* p1, const void* p2, size_t n);
+//      int         TimingSafeMemIsClear(const void* p, size_t n);
+//
+//      void*       EAAlloca(size_t n);
+//      void*       EAMalloca(size_t n);
+//      void        EAFreea();
+//
+//      template<size_t> StaticMemory;
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAMEMORY_H
+#define EASTDC_EAMEMORY_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+
+
+EA_DISABLE_ALL_VC_WARNINGS()
+
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+	EA_DISABLE_ALL_VC_WARNINGS()
+	// void* __cdecl _alloca(size_t n);  To consider: directly forward declare _alloca in order to avoid #include of <malloc.h>, as it's slow to compile.
+	#include <malloc.h>         // For alloca.
+	EA_RESTORE_ALL_VC_WARNINGS()
+#elif defined (EA_PLATFORM_BSD)
+	// do nothing since alloca is in stdlib.h
+#elif (defined(__GNUC__) || defined(__clang__)) && !defined(EAAlloca)
+	#define EAAlloca __builtin_alloca
+#endif
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#include <math.h>       // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
+	#include <intrin.h>
+#endif
+
+#if EA_SSE
+	#include <xmmintrin.h>
+#endif
+
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+#if EABASE_VERSION_N <= 20602
+	#if   defined(EA_PROCESSOR_X86)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		const size_t     kCacheLineSize       = 32;  // This is the minimum possible value.
+		const size_t     kCacheLineSizeMask   = 31;
+
+		#if EA_SSE
+			#define EA_CACHE_PREFETCH_128(addr)  _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
+			#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+		#else
+			#define EA_CACHE_PREFETCH_128(addr)  // Need to support _mm_prefetch.
+			#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+		#endif
+
+	#elif defined(EA_PROCESSOR_X86_64)
+		typedef uint64_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		const size_t     kCacheLineSize       = 64;  // This is the minimum possible value
+		const size_t     kCacheLineSizeMask   = 63;
+
+		#define EA_CACHE_PREFETCH_128(addr)  _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+
+	#elif defined(EA_PROCESSOR_ARM)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		const size_t     kCacheLineSize       = 32; // This varies between implementations and is usually 32 or 64.
+		const size_t     kCacheLineSizeMask   = 31;
+
+		// Modern ARM CPUs have auto-prefetch functionality, though it's 
+		// not necessarily smart and has to be enabled by the OS.
+		#if defined(__GNUC__) && (__GNUC__ >= 4)
+			#define EA_CACHE_PREFETCH_128(addr) __builtin_prefetch(addr)
+		#else
+			#define EA_CACHE_PREFETCH_128(addr)
+		#endif
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+
+	#elif (EA_PLATFORM_WORD_SIZE == 4)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		const size_t     kCacheLineSize       = 32;  // This is the minimum possible value
+		const size_t     kCacheLineSizeMask   = 31;
+
+		#define EA_CACHE_PREFETCH_128(addr)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+
+	#else
+		typedef uint64_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		const size_t     kCacheLineSize       = 64;  // This is the minimum possible value
+		const size_t     kCacheLineSizeMask   = 63;
+
+		#define EA_CACHE_PREFETCH_128(addr)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+
+	#endif
+#else
+	#if   defined(EA_PROCESSOR_X86)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		#if EA_SSE
+			#define EA_CACHE_PREFETCH_128(addr)  _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
+			#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+		#else
+			#define EA_CACHE_PREFETCH_128(addr)  // Need to support _mm_prefetch.
+			#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+		#endif
+	#elif defined(EA_PROCESSOR_X86_64)
+		typedef uint64_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		#define EA_CACHE_PREFETCH_128(addr)  _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+
+	#elif defined(EA_PROCESSOR_ARM32)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		// Modern ARM CPUs have auto-prefetch functionality, though it's 
+		// not necessarily smart and has to be enabled by the OS.
+		#if defined(__GNUC__) && (__GNUC__ >= 4)
+			#define EA_CACHE_PREFETCH_128(addr) __builtin_prefetch(addr)
+		#else
+			#define EA_CACHE_PREFETCH_128(addr)
+		#endif
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+	#elif (EA_PLATFORM_WORD_SIZE == 4)
+		typedef uint32_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		#define EA_CACHE_PREFETCH_128(addr)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+	#else
+		typedef uint64_t machine_word_t;
+		const size_t     kMachineWordSize     = sizeof(machine_word_t);
+		const size_t     kMachineWordSizeMask = sizeof(machine_word_t) - 1;
+		#define EA_CACHE_PREFETCH_128(addr)
+		#define EA_CACHE_ZERO_128(addr)      memset(addr, 0, 128)
+	#endif
+
+	const size_t     kCacheLineSize       = EA_CACHE_LINE_SIZE;  
+	const size_t     kCacheLineSizeMask   = (EA_CACHE_LINE_SIZE-1);
+#endif
+
+
+   
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// EAAlloca / EAAllocaAligned
+	///
+	/// Platform-independent version of alloca. This is a #define instead of a
+	/// function, and so it isn't used within a namespace. This #define is located
+	/// within a namespace merely for file organization purposes.
+	/// EAAlloca is prefixed with EA because it is (necessarily) a #define and 
+	/// the EA makes its usage less likely to collide with other defines.
+	///
+	/// As with alloca, emory allocated by EAAalloca must be used and freed within
+	/// the same scope that it is allocated.
+	///
+	/// Return value:
+	/// EAAlloca may return NULL with some implementations, may throw an 
+	/// exception in some implementations, and my return a non-NULL value
+	/// which is usuable beyond an arbitrary number of bytes. Thus EAlloca/alloca is
+	/// dangerous to use unless you are sure it will work for you, and that is very
+	/// platform/compiler/ABI-dependent. Consider using EAMalloca/EAFreea instead.
+	///
+	/// If EAAlloca is not #defined for some platform then it is not supported by 
+	/// that platform. However, as of this writing EAAlloca/alloca is supported by all 
+	/// compilers and platforms that have been significant to EA, including console, 
+	/// mobile, desktop, and hand-held platforms.
+	///
+	/// Declaration:
+	///     void* EAAlloca(size_t n);
+	///
+	/// Example usage:
+	///     void Function() {
+	///         char* buffer = (char*)EAAlloca(24);
+	///         // Beware the return value behavior of EAAlloca (see above).
+	///         sprintf(buffer, "hello world");
+	///         // No need to try to free it.
+	///     }
+	///
+	#if !defined(EAAlloca)
+		#define EAAlloca alloca
+	#endif
+
+	#if !defined(EAAllocaAligned)
+		#define EAAllocaAligned(size, alignment) ((void*)(((uintptr_t)EAAlloca((size)+(alignment)) + ((alignment)-1)) & ~((alignment)-1)))
+	#endif
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// EAMalloca / EAFreea
+	///
+	/// Allocates memory from the stack if possible, otherwise allocates memory
+	/// via malloc. It's not possible to determine ahead of time which of the two
+	/// it will allocate from. EAMalloca must be eventually matched with an EAFreea.
+	/// Memory allocated by EAMalloca must be used and freed within the same scope
+	/// that it is allocated.
+	///
+	/// Return value:
+	/// Returns NULL if the memory could not be allocated. 
+	///
+	/// Declaration:
+	///     void* EAMalloca(size_t n);
+	///     void  EAFreea(void *p);
+	///
+	/// Example usage:
+	///     void Function() {
+	///         char* buffer = (char*)EAMalloca(24);
+	///         if(buffer) { // This will fail only if malloc fails for the given size.
+	///             sprintf(buffer, "hello world");
+	///             EAFreea(buffer);
+	///         }
+	///     }
+	///
+	/// To consider: Implement EAMallocaAligned
+	/// 
+	#if defined(EA_PLATFORM_MICROSOFT) // Microsoft platforms support larger stacks than many other platforms, as well as auto-growable stacks.
+		#define EAMALLOCA_THRESHOLD 8192
+	#else
+		#define EAMALLOCA_THRESHOLD 1024
+	#endif
+	#define EAMALLOCA_TYPE_ALLOCA   0xaa
+	#define EAMALLOCA_TYPE_MALLOC   0xbb
+	#define EAMALLOCA_TYPE_SIZE     EA_PLATFORM_MIN_MALLOC_ALIGNMENT
+
+	#if !defined(EAMalloca)
+		#define EAMalloca(size) ((((size) + EAMALLOCA_TYPE_SIZE) < EAMALLOCA_THRESHOLD) ?                                       \
+									EA::StdC::EAMallocaSetType(EAAlloca((size) + EAMALLOCA_TYPE_SIZE), EAMALLOCA_TYPE_ALLOCA) : \
+									EA::StdC::EAMallocaSetType(malloc((size) + EAMALLOCA_TYPE_SIZE), EAMALLOCA_TYPE_MALLOC))
+	#endif
+	#if !defined(EAFreea)
+		#define EAFreea(p) EA::StdC::EAFreeaImpl(p)
+	#endif
+
+	inline void* EAMallocaSetType(void* p, uint32_t type)
+	{
+		if(p) {
+			*reinterpret_cast<uint32_t*>(p) = type;
+			p = reinterpret_cast<char*>(p) + EAMALLOCA_TYPE_SIZE;
+		}
+		return p;
+	}
+
+	inline void EAFreeaImpl(void* p)
+	{
+		if (p) {
+			p = reinterpret_cast<char*>(p) - EAMALLOCA_TYPE_SIZE;
+			uint32_t type = *reinterpret_cast<uint32_t*>(p);
+			if(type == EAMALLOCA_TYPE_MALLOC)
+				free(p);
+		}
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memcpy
+	///
+	/// Copies nByteCount bytes from pSource to pDestination. 
+	/// The source and destination memory ranges must not overlap.
+	/// Returns pDestination.
+	/// There are no restrictions in the way of size, alignment, or memory type,
+	/// though the source memory must be readable and the destination memory 
+	/// must be writable. 
+	/// Works with uncacheable memory, such as video memory. 
+	///
+	EASTDC_API char8_t* Memcpy(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
+
+	// Cacheable memory copy
+	// Works only with cacheable memory (i.e. conventional system memory).
+	// The source and destination memory must not overlap.
+	// Cannot be relied on to work with uncachable memory, such as video memory.
+	// There are no alignment restrictions on either pDestination or pSource.
+	EASTDC_API char8_t* MemcpyC(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
+
+	// Streaming memcpy
+	// This function copies memory from source to destination without filling the cache with the memory.
+	// This is useful for when you want to write memory that will not be read by the processor used
+	//   to write it, such as when the CPU writes to memory used by the GPU. 
+	// Works on both cacheable and uncacheable memory.
+	// The source and destination memory must not overlap.
+	// There are no alignment restrictions on either pDestination or pSource.
+	EASTDC_API char8_t* MemcpyS(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memcpy128
+	///
+	/// Copies nByteCount bytes from the source to the destination. nByteCount
+	/// must be a multiple of 128, pDestination must be on at least a 128 byte 
+	/// boundary, and pSource must be on at least a 16 byte boundary.
+	///
+	/// Additionally, pDestination and pSource must refer to cacheable memory.
+	/// Cacheable memory is standard RAM application memory as opposed to being
+	/// video memory or IO-mapped memory. On XBox 360, uncacheable memory is 
+	/// that allocated with VirtualAlloc(..., PAGE_NOCACHE) or VirtualAlloc(..., PAGE_WRITECOMBINE).
+	/// In fact, on XBox 360 this function is the same as XMemCpy128.
+	///
+	EASTDC_API char8_t* Memcpy128(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
+
+	// cacheable 128 byte memcpy
+	// This function is useful for higher performance memory copies when the requirements can be met.
+	// The address pointed to by pDestination must be aligned on a 128-byte boundary, and uint8Count must be a multiple of 128.
+	// Works only with cacheable memory (i.e. conventional system memory).
+	// The source and destination memory must not overlap.
+	EASTDC_API char8_t* Memcpy128C(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memmove
+	///
+	/// Copies nByteCount bytes from pSource to pDestination. 
+	/// The source and destination memory ranges may overlap.
+	/// Returns pDestination.
+	/// There are no restrictions in the way of size, alignment, or memory type,
+	/// though the source memory must be readable and the destination memory 
+	/// must be writable. 
+	/// Works with uncacheable memory, such as video memory.
+	///
+	EASTDC_API char8_t* Memmove(void* pDestination, const void* pSource, size_t nByteCount);
+
+	// Cacheable memory move
+	// Works only with cacheable memory (i.e. conventional system memory).
+	// The source and destination memory may overlap.
+	// Cannot be relied on to work with uncachable memory, such as video memory.
+	EASTDC_API char8_t* MemmoveC(void* pDestination, const void* pSource, size_t nByteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memchr
+	///
+	/// Same as memchr and wmemchr.
+	/// Searches the first n characters (not necessarily bytes) of the memory block 
+	/// pointed to by pString for character c.
+	/// Returns a pointer to the character or NULL if not found.
+	/// There are no restrictions about the type of memory p refers
+	/// to except that it be readable.
+	///
+	EASTDC_API const char8_t*  Memchr  (const char8_t*  p, char8_t c,  size_t n);
+	EASTDC_API const char16_t* Memchr16(const char16_t* p, char16_t c, size_t n);
+	EASTDC_API const char32_t* Memchr32(const char32_t* p, char32_t c, size_t n);
+	#if EA_WCHAR_UNIQUE
+		inline const wchar_t* MemchrW(const wchar_t* p, wchar_t c, size_t n)
+		{
+			#if (EA_WCHAR_SIZE == 2)
+				return reinterpret_cast<const wchar_t*>(Memchr16(reinterpret_cast<const char16_t*>(p), (char16_t)c, n));
+			#else
+				return reinterpret_cast<const wchar_t*>(Memchr32(reinterpret_cast<const char32_t*>(p), (char32_t)c, n));
+			#endif
+		}
+	#endif
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memcmp
+	///
+	/// Same as memcmp and wmemcmp.
+	/// Compares the first n bytes of two memory blocks pointed by p1 and p2. 
+	/// The comparison is a bytewise compare and thus for strings it is case-sensitive. 
+	/// For a case-insensitive string comparison, use the Stricmp function.
+	/// Bytes are treated as uint8_t for comparison purposes.
+	/// Returns 0 if the memory is equal, < 0 if p1 < p2, and > 0 if p1 > p2.
+	/// There are no restrictions about the type of memory p1 and p2 refer
+	/// to except that they be readable.
+	///
+	EASTDC_API int Memcmp(const void* p1, const void* p2, size_t n);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memmem
+	///
+	/// Search for pFind/findSize within pMemory/memorySize.
+	/// Return NULL if not found, return the first found location within pMemory/memory size if found.
+	/// If memorySize is 0, return value is NULL. Otherwise if the findSize is 0, then the return value is pMemory.
+	/// If pMemory or pFind is NULL, their respective size must be 0.
+	/// The return value is non-const because that's how C memmem (non-standard function) works.
+	/// There are no restrictions about the type of memory pMemory and pFind refer
+	/// to except that they be readable.
+	/// There are no alignment restrictions on either pMemory or pFind.
+	/// See  http://linux.die.net/man/3/memmem
+	///
+	EASTDC_API void* Memmem(const void* pMemory, size_t memorySize, const void* pFind, size_t findSize);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memclear
+	///
+	/// Has the same effect as memset(pDestination, 0, n) but may be faster.
+	///
+	/// Sets n bytes at pDestination to zero.
+	/// Works with uncacheable memory, such as video memory.
+	/// There are no alignment restrictions on either pDestination or pSource.
+	///
+	EASTDC_API void Memclear(void* pDestination, size_t n);
+
+	/// Sets n bytes at pDestination to zero.
+	/// Works only with cacheable memory (i.e. conventional system memory).
+	/// Cannot be relied on to clear uncachable memory, such as video memory.
+	/// There are no alignment restrictions on either pDestination or pSource.
+	EASTDC_API void MemclearC(void* pDestination, size_t n);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memset8 / Memset16 / Memset32 / Memset64 / MemsetN / MemsetPointer
+	///
+	/// The standard memset function replicates a given 8 bit value into a memory
+	/// block. However, we might want to replicate a 16 bit, 32 bit, or 64 bit value
+	/// into a block. That's what these functions do. The MemsetN function is a 
+	/// generic version which can copy unusual sizes such as 24 bits (e.g. RGB fills).
+	/// The count values for each of these is the count of uint16_t, count of uint32_t,
+	/// count of pointers, etc.
+	/// 
+	/// Memset8 is the same as the C memset function. We provide additional variations
+	/// of memset which set uint16_t values, uint32_t value, etc. instead of uint8_t 
+	/// values like Memset8. MemsetN writes a generic type of any size. In each case 
+	/// pDestination must point to enough memory to hold full values. Thus pDestination
+	/// for Memset32 must have a capacity for at least (uint32Count * sizeof(uint32_t)) bytes.
+	///
+	/// The destination is required to be aligned to its type. Thus the destination
+	/// of Memset32 must be 32 bit aligned.
+	///
+	/// There are no restrictions about the type of memory pDestination refers
+	/// to except that it be writable.
+	///
+	/// Works with uncacheable memory, such as video memory, but also works on conventional 
+	/// cacheable system memory. 
+	EASTDC_API uint8_t*  Memset8      (void* pDestination, uint8_t  c, size_t uint8Count);
+	EASTDC_API uint16_t* Memset16     (void* pDestination, uint16_t c, size_t uint16Count);
+	EASTDC_API uint32_t* Memset32     (void* pDestination, uint32_t c, size_t uint32Count);
+	EASTDC_API uint64_t* Memset64     (void* pDestination, uint64_t c, size_t uint64Count);
+	EASTDC_API void*     MemsetN      (void* pDestination, const void* pSource, size_t sourceBytes, size_t nCount);
+	EASTDC_API void*     MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount);
+
+	// Works only with cacheable memory (i.e. conventional system memory).
+	// Cannot be relied on to work with uncachable memory, such as video memory.
+	// There are no alignment restrictions on either pDestination or pSource.
+	EASTDC_API uint8_t* Memset8C(void* pDestination, uint8_t c, size_t uint8Count);
+
+	// Specialized memset for multiple of 128 byte sized blocks.
+	EASTDC_API uint8_t*  Memset8_128(void* pDestination, uint8_t c, size_t uint8Count);
+
+	// Cacheable 128 byte memory set
+	// Works only with cacheable memory (i.e. conventional system memory).
+	// Cannot be relied on to work with uncachable memory, such as video memory.
+	// The address pointed to by pDestination must be aligned on a 128-byte boundary, and uint8Count must be a multiple of 128.
+	EASTDC_API uint8_t* Memset8_128C(void* pDestination, uint8_t c, size_t uint8Count);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memfill16 / Memfill24 / Memfill32 / Memfill64 / MemfillSpecific
+	/// 
+	/// Memfill is the same as memset except that the count parameter is a count 
+	/// of bytes and not (for example) a count of uint32_t values. Memfill supports 
+	/// byte counts that aren't an even multiple the value size. Thus a call to 
+	/// Memfill32(p, 0x00112233, 3) is valid and does what you would expect. 
+	/// MemfillSpecific fills (and potentially repeats) any source pattern into any destination space.
+	/// There are no restrictions about the type of memory pDestination refers
+	/// to except that it be writable.
+	///
+	EASTDC_API void Memfill8       (void* pDestination, uint8_t c,  size_t byteCount);
+	EASTDC_API void Memfill16      (void* pDestination, uint16_t c, size_t byteCount);
+	EASTDC_API void Memfill24      (void* pDestination, uint32_t c, size_t byteCount);
+	EASTDC_API void Memfill32      (void* pDestination, uint32_t c, size_t byteCount);
+	EASTDC_API void Memfill64      (void* pDestination, uint64_t c, size_t byteCount);
+	EASTDC_API void MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// Memcheck8 / Memcheck16 / Memcheck32 / Memcheck64
+	/// 
+	/// This family of functions is like Memfill except it verifies that the 
+	/// memory is filled as per the value and byte count. Returns a pointer 
+	/// to the first mis-matching byte if there's a mismatch. Returns NULL if 
+	/// there are no mismatches.
+	/// There are no restrictions about the type of memory pDestination refers
+	/// to except that it be readable.
+
+	EASTDC_API const void* Memcheck8 (const void* p, uint8_t  c, size_t byteCount);
+	EASTDC_API const void* Memcheck16(const void* p, uint16_t c, size_t byteCount);
+	EASTDC_API const void* Memcheck32(const void* p, uint32_t c, size_t byteCount);
+	EASTDC_API const void* Memcheck64(const void* p, uint64_t c, size_t byteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// rwstdc compatibility
+	///
+	/// These are identical in behaviour to the EAStdC Memfill functions.
+	/// There are no restrictions about the type of memory pDestination refers
+	/// to except that it be writable.
+	///
+	EASTDC_API void MemFill16(void* pDestination, uint16_t c, unsigned int byteCount);
+	EASTDC_API void MemFill32(void* pDestination, unsigned int value, unsigned int byteCount);
+	EASTDC_API void MemFillSpecific(void* pDestination, const void* pSource, unsigned int destByteCount, unsigned int sourceByteCount);
+
+
+	///////////////////////////////////////////////////////////////////////////
+	/// rwstdc compatibility
+	///
+	/// Deprecated functions.
+	///
+	#if EASTDC_MEMCHR16_ENABLED
+		// This function is deprecated and replaced by Memchr16, as it collides with the Standard C memchr 
+		// function which takes a void* argument.
+		EA_PREFIX_DEPRECATED EASTDC_API const char16_t* Memchr(const char16_t* pString, char16_t c, size_t nCharCount);
+	#endif
+
+	#if EASTDC_MEMCPY16_ENABLED
+		// These function are deprecated. It was mistakenly created during a code migration.
+		// It is scheduled for removal in a future version of this package.
+		EA_PREFIX_DEPRECATED EASTDC_API char16_t* Memcpy(char16_t* pDestination, const char16_t* pSource, size_t nCharCount) EA_POSTFIX_DEPRECATED;
+		EA_PREFIX_DEPRECATED EASTDC_API char16_t* Memmove(char16_t* pDestination, const char16_t* pSource, size_t nCharCount) EA_POSTFIX_DEPRECATED;
+		EA_PREFIX_DEPRECATED EASTDC_API int       Memcmp(const char16_t* pString1, const char16_t* pString2, size_t nCharCount) EA_POSTFIX_DEPRECATED;
+	#endif
+
+
+	/// StaticMemory
+	///
+	/// Allows you to declare memory that's sized and aligned appropriately and 
+	/// is allocated outside of the dynamic heap. But also avoids declaring a
+	/// class or struct, as that might be unfeasible in some circumstances.
+	/// For example, if you have a class that you want to have a global variable
+	/// for, but it's impossible for the class to be constructed before main or
+	/// the compiler simply fails to do so, you can use StaticMemory.
+	///
+	/// Example usage:
+	///     StaticMemory<sizeof(MyClass)> mStaticMemory;
+	///     MyClass* pClass = new(mStaticMemory.Memory()) MyClass;
+	///
+	template <size_t n>
+	struct StaticMemory
+	{
+		EA_PREFIX_ALIGN(8) uint64_t mMemory[(n + 7) / 8];
+		inline void* Memory() { return mMemory; }
+	};
+
+
+	/// TimingSafeMemEqual
+	///
+	/// Similar to Memcmp but returns simply true or false. Performs slightly faster
+	/// than TimingSafeMemcmp.
+	/// Executes in a constant time for any given n value. The primary use case for this is 
+	/// for security in the presence of possible timing attacks.
+	///
+	EASTDC_API bool TimingSafeMemEqual(const void* p1, const void* p2, size_t n);
+
+
+	/// TimingSafeMemcmp
+	///
+	/// Behaves the same as Memcmp, but executes in a constant time for any given n value. 
+	/// The primary use case for this is for security in the presence of possible timing attacks.
+	///
+	EASTDC_API int TimingSafeMemcmp(const void* p1, const void* p2, size_t n);
+
+
+	/// TimingSafeMemIsClear
+	///
+	/// Returns true if the given n bytes of memory are all zero in value, as would be the case
+	/// if the memory was cleared with Memclear.
+	/// Executes in a constant time for any given n value and regardless of whether the return 
+	/// value is true or valse. The primary use case for this is for security in the presence of 
+	/// possible timing attacks.
+	///
+	EASTDC_API bool TimingSafeMemIsClear(const void* p, size_t n);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inlines
+///////////////////////////////////////////////////////////////////////////////
+
+// In optimized non-debug builds, we inline various functions.
+// We don't inline these functions in debug builds because in debug builds they
+// contain diagnostic code that can't be exposed in headers because that would the 
+// user of this header to #include all the debug functionality headers, which isn't 
+// feasible.
+#if EASTDC_MEMORY_INLINE_ENABLED
+	#include <EAStdC/internal/EAMemory.inl>
+#endif
+
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+

+ 385 - 0
include/EAStdC/EAProcess.h

@@ -0,0 +1,385 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This module defines functions for process spawning and query.
+// Normally this functionality is present only on platforms that support
+// multiple processes, such as desktop and server platforms.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EAPROCESS_H
+#define EASTDC_EAPROCESS_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAString.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+#include <stdlib.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+namespace EA
+{
+   namespace StdC
+   {
+		#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+			const int kMaxPathLength      = _MAX_PATH;
+			const int kMaxDirectoryLength = _MAX_PATH;
+		#elif defined(EA_PLATFORM_XBOXONE)
+			const int kMaxPathLength      = 260;
+			const int kMaxDirectoryLength = 260;
+		#elif defined(EA_PLATFORM_SONY)
+			const int kMaxPathLength      = 1024;
+			const int kMaxDirectoryLength = 1024;
+		#elif defined(EA_PLATFORM_OSX)
+			const int kMaxPathLength      = 1024;
+			const int kMaxDirectoryLength = 1024;
+		#elif defined(EA_PLATFORM_LINUX)
+			const int kMaxPathLength      = 1024;
+			const int kMaxDirectoryLength = 1024;
+		#else
+			const int kMaxPathLength      = 512;
+			const int kMaxDirectoryLength = 512;
+		#endif
+
+
+		/// PathFlags
+		///
+		enum PathFlags
+		{
+			kPathFlagNone       = 0x00,
+			kPathFlagBundlePath = 0x01   // Apple-specific: Return the path to the bundle instead of its inner contents. https://developer.apple.com/library/ios/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html
+		};
+
+
+		/// GetCurrentProcessPath
+		///
+		/// Returns the file path to the current process.
+		/// The output parameter pPath must be big enough to hold the largest
+		/// possible path (i.e. IO::kMaxPathLength) for the given platform. 
+		/// The return value is the strlen of the path in the argument pPath.
+		/// The return value is 0 upon failure, but there should never be failure
+		/// as long as the function is implemented on the given platform.
+		/// The pathCapacity must be at least 1.
+		///
+		/// Example usage:
+		///     char16_t path[IO::kMaxPathLength];
+		///
+		///     GetCurrentProcessPath(path, IO::kMaxPathLength);
+		///     printf("Path: %ls\n", path);
+		///
+		EASTDC_API size_t GetCurrentProcessPath(char8_t*  pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
+		EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
+		EASTDC_API size_t GetCurrentProcessPath(char32_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			size_t GetCurrentProcessPath(wchar_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
+		#endif
+
+
+		/// SetCurrentProcessPath
+		///
+		/// Specifies the process path, for platforms in which it's not possible for this library
+		/// to know it without being told. Subsequent calls to GetCurrentProcessPath with flags other
+		/// than kPathFlagNone will ignore such flags and simply return this path.
+		///
+		EASTDC_API void SetCurrentProcessPath(const char8_t* pPath);
+
+
+		/// GetCurrentProcessDirectory
+		///
+		/// Returns the directory path to the current process.
+		/// The output parameter pPath must be big enough to hold the largest
+		/// possible directory path (i.e. IO::kMaxDirectoryLength) for the given platform. 
+		/// The return value is the strlen of the path in the argument pPath.
+		/// The return value is 0 upon failure, but there should never be failure
+		/// as long as the function is implemented on the given platform.
+		/// The pathCapacity must be at least 1.
+		///
+		/// The return value will have a trailing directory separator, as with 
+		/// other directory paths in this system.
+		///
+		/// Example usage:
+		///     char16_t dir[IO::kMaxDirectoryLength];
+		///
+		///     GetCurrentProcessDirectory(dir, IO::kMaxDirectoryLength);
+		///     printf("Directory: %ls\n", dir);
+		///
+		EASTDC_API size_t GetCurrentProcessDirectory(char8_t*  pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
+		EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
+		EASTDC_API size_t GetCurrentProcessDirectory(char32_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			size_t GetCurrentProcessDirectory(wchar_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
+		#endif
+
+
+		/// GetEnvironmentVar
+		///
+		/// Environment variables are per-process global variables. Their advantage
+		/// is that they can be read or written at any time during the process execution
+		/// and are available from any part of the executing process, including from
+		/// dynamic libraries (e.g. DLLs on the Windows platform). 
+		///
+		/// The input pName is a 0-terminated string.
+		/// The output sValue will hold the resulting environment variable string.
+		/// The return value is the strlen of the required string. Thus a return value
+		/// that is less than valueCapacity was able to write the entire result string.
+		/// A return value of zero means that the variable exists but it is empty.
+		/// A return value of size_t(-1) means the variable doesn't exist.
+		///
+		/// This function is named GetEnvironmentVar instead of GetEnvironmentVariable
+		/// because the windows.h file #defines the latter to something else.
+		///
+		/// As of this writing, another version of GetEnvironmentVar exists in the 
+		/// EAEnvironmentVariable module and which uses char buffers instead of string
+		/// objects. You may prefer the char buffers if you are avoiding memory allocations.
+		///
+		/// Example usage:
+		///     wchar_t pValue[64];
+		///
+		///     if(GetEnvironmentVar(L"UserName", pValue, 64) < 64)
+		///         printf("Path: %ls\n", pValue);
+		///
+		EASTDC_API size_t GetEnvironmentVar(const char8_t*  pName, char8_t*  pValue, size_t valueCapacity);
+		EASTDC_API size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity);
+		EASTDC_API size_t GetEnvironmentVar(const char32_t* pName, char32_t* pValue, size_t valueCapacity);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			size_t GetEnvironmentVar(const wchar_t* pName, wchar_t* pValue, size_t valueCapacity);
+		#endif
+
+
+		/// SetEnvironmentVar
+		///
+		/// Sets the given environment variable. Returns true if it could be set. 
+		/// A return value of false means the variable didn't previously exist.
+		/// To remove an environment variable, set pValue to NULL. Removing is different
+		/// from seting to an empty string, as the latter will result in a successful
+		/// return from GetEnvironmentVar, whereas the former will result in an error return.
+		/// This function is named SetEnvironmentVar instead of SetEnvironmentVariable
+		/// because the windows.h file #defines the latter to something else.
+		///
+		/// Example usage:
+		///     if(SetEnvironmentVar("User Name", "Lance Armstrong"))
+		///         printf("Success setting user name.\n");
+		///
+		EASTDC_API bool SetEnvironmentVar(const char8_t*  pName, const char8_t*  pValue);
+		EASTDC_API bool SetEnvironmentVar(const char16_t* pName, const char16_t* pValue);
+		EASTDC_API bool SetEnvironmentVar(const char32_t* pName, const char32_t* pValue);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			bool SetEnvironmentVar(const wchar_t* pName, const wchar_t* pValue);
+		#endif
+
+
+		/// Spawn
+		///
+		/// This function spawns a process whose path is 'pPath'. The arguments for the
+		/// process are specified as an array of pointers to strings.
+		///
+		/// It is customary for the application that spawns another application to pass
+		/// the spawned application path as the first argument. However, we, like the
+		/// standard C spawn function, do not enforce this. It is up to the caller to
+		/// follow this convention.
+		///
+		/// The return value is the exit status of the process, or -1 on error.
+		///
+		/// Given that this is a low-level C runtime replacement function, this function
+		/// does not do string encoding conversion of the input path. The caller must
+		/// do such a conversion manually with a call to:
+		///     EA::TextUtil::ConvertStringEncoding(sSomePathSource, sSomePathDestination, kCodePageSystem);
+		///
+		/// If 'wait' is true, the function does not return until the spawned process completes.
+		///
+		/// This function works only on systems that support multiple concurrent
+		/// processes. Usually that means desktop and server operating systems.
+		///
+		/// Example usage:
+		///   const char16_t* ptrArray[4] = { EA_CHAR16("/System/Utilities/PingSomeAddresses.exe"), EA_CHAR16("www.bozo.com"), EA_CHAR16("www.nifty.com"), NULL };
+		///   int nReturnValue = Spawn("/System/Utilities/PingSomeAddresses.exe", ptrArray, true);
+		///
+		EASTDC_API int Spawn(const char8_t*  pPath, const char8_t*  const* pArgumentArray, bool wait = false);
+		EASTDC_API int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait = false);
+		EASTDC_API int Spawn(const char32_t* pPath, const char32_t* const* pArgumentArray, bool wait = false);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			int Spawn(const wchar_t* pPath, const wchar_t* const* pArgumentArray, bool wait = false);
+		#endif
+
+
+		/// ExecuteShellCommand
+		///
+		/// Similar to the C runtime "system()" function present on some platforms.
+		/// Multiple commands can be executed by separating them with newline characters.
+		///
+		/// This function works only on systems that support system-level command execution. 
+		/// Usually that means desktop and server operating systems.
+		///
+		/// Example usage:
+		///    ExecuteShellCommand("su root");
+		///    ExecuteShellCommand("rm /* -r");
+		///
+		///    ExecuteShellCommand("su root\nrm /* -r");
+		///
+		EASTDC_API int ExecuteShellCommand(const char8_t*  pCommand);
+		EASTDC_API int ExecuteShellCommand(const char16_t* pCommand);
+		EASTDC_API int ExecuteShellCommand(const char32_t* pCommand);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			int ExecuteShellCommand(const wchar_t* pCommand);
+		#endif
+
+
+		/// SearchEnvironmentPath
+		///
+		/// Searches the system application path set for the named application.
+		/// 
+		/// Returns true and the full path to the file if found. Desktop operating
+		/// systems will often have a PATH variable or similar setting which lists 
+		/// a set of directories in which to look for executable programs when the 
+		/// programs are executed by file name (and not directory) alone. 
+		/// This function searches the system path set for the given file name.
+		/// The returned path will have surrounding quotes removed if they were present.
+		///
+		/// The pFileName and pPath parameters may not be NULL.
+		///  
+		/// If the input pEnvironmentVar is non-NULL, the environment variable
+		/// identified by pEnvironmentVar is assumed to refer to a set of 
+		/// paths to search and it is used instead of the default system path set.
+		/// The environment variable should be of the form "<directory>;<directory>;..." 
+		/// where individual paths may be quoted. Note that pEnvironmentVar specifies
+		/// the environment variable name (e.g. "PATH") and not its value.
+		///
+		/// Example usage:
+		///     char16_t fullPath[IO::kMaxPathLength];
+		///
+		///     if(SearchEnvironmentPath("perforce.exe", fullPath, "PATH"))
+		///         printf("Full path to Perforce is "%ls\n", fullPath);
+		///
+		EASTDC_API bool SearchEnvironmentPath(const char8_t*  pFileName, char8_t*  pPath, const char8_t*  pEnvironmentVar = NULL);
+		EASTDC_API bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar = NULL);
+		EASTDC_API bool SearchEnvironmentPath(const char32_t* pFileName, char32_t* pPath, const char32_t* pEnvironmentVar = NULL);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			bool SearchEnvironmentPath(const wchar_t* pFileName, wchar_t* pPath, const wchar_t* pEnvironmentVar = NULL);
+		#endif
+
+
+		/// OpenFile
+		///
+		/// Opens a file via the default system application 
+		/// For example, a .doc file would be opened by your word processor.
+		/// The input pPath must point to a valid path string.
+		///
+		/// This function works only on systems that support multiple concurrent
+		/// processes. Usually that means desktop and server operating systems.
+		///
+		/// Example usage:
+		///    OpenFile("/system/settings/somefile.txt");
+		///    OpenFile("/system/settings/somefile.html");
+		///    OpenFile("http://www.bozo.com/somefile.html");
+		///
+		EASTDC_API bool OpenFile(const char8_t*  pPath);
+		EASTDC_API bool OpenFile(const char16_t* pPath);
+		EASTDC_API bool OpenFile(const char32_t* pPath);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			bool OpenFile(const wchar_t* pPath);
+		#endif
+
+
+   } // namespace StdC
+
+} // namespace EA
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inlines
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA
+{
+	namespace StdC
+	{
+
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+
+			#if !defined(EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST)
+				#if (EA_WCHAR_SIZE == 2)
+					#define EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(x) reinterpret_cast<const char16_t* const*>(reinterpret_cast<const void *>(x))
+				#else
+					#define EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(x) reinterpret_cast<const char32_t* const*>(x)
+				#endif
+			#endif
+			
+			
+			inline size_t GetCurrentProcessPath(wchar_t* pPath, int pathCapacity, int pathFlags)
+			{
+				return GetCurrentProcessPath(EASTDC_UNICODE_CHAR_PTR_CAST(pPath), pathCapacity, pathFlags);
+			}
+			
+			inline size_t GetCurrentProcessDirectory(wchar_t* pDirectory, int pathCapacity, int pathFlags)
+			{
+				return GetCurrentProcessDirectory(EASTDC_UNICODE_CHAR_PTR_CAST(pDirectory), pathCapacity, pathFlags);
+			}
+
+			inline size_t GetEnvironmentVar(const wchar_t* pName, wchar_t* pValue, size_t valueCapacity)
+			{
+				return GetEnvironmentVar(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pName), 
+										 EASTDC_UNICODE_CHAR_PTR_CAST(pValue), 
+										 valueCapacity);
+			}
+
+			inline bool SetEnvironmentVar(const wchar_t* pName, const wchar_t* pValue)
+			{
+				return SetEnvironmentVar(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pName), 
+										 EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pValue));
+			}
+			
+			inline int Spawn(const wchar_t* pPath, const wchar_t* const* pArgumentArray, bool wait)
+			{
+				return Spawn(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPath), 
+							 EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(pArgumentArray), 
+							 wait);
+			}
+			
+			inline int ExecuteShellCommand(const wchar_t* pCommand)
+			{
+				return ExecuteShellCommand(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pCommand));
+			}
+			
+			inline bool SearchEnvironmentPath(const wchar_t* pFileName, wchar_t* pPath, const wchar_t* pEnvironmentVar)
+			{
+				return SearchEnvironmentPath(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pFileName), 
+											 EASTDC_UNICODE_CHAR_PTR_CAST(pPath), 
+											 EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pEnvironmentVar));
+			}
+			
+			inline bool OpenFile(const wchar_t* pPath)
+			{
+				return OpenFile(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPath)); // EASTDC_UNICODE_CONST_CHAR_PTR_CAST is defined in EAString.h
+			}
+			
+
+		#endif
+
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+

+ 583 - 0
include/EAStdC/EARandom.h

@@ -0,0 +1,583 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements a basic set of random number generators suitable for game
+// development usage.
+/////////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EARANDOM_H
+#define EASTDC_EARANDOM_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+#include <string.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/////////////////////////////////////////////////////////////////////////////////////
+	/// Introduction
+	///
+	/// This code includes basic random number generator functionality. It is good for
+	/// most game development uses with the exception of cryptographic uses and legally 
+	/// controlled gambling mechanisms. For example, cryptographic random number generator 
+	/// requirements have additional considerations regarding protection against 
+	/// cryptographic attacks.
+	///
+	/// Purpose
+	/// There are many free C/C++ random number packages available today. Many of them 
+	/// are rather comprehensive and some are fairly flexible. The design of this package
+	/// is based on the needs of game programmers and intentionally avoids some of the 
+	/// complications of other packages. This package is designed first and foremost to 
+	/// be fast and to offer at least the option of low footprint. Secondly this package
+	/// is designed to be very easily understood with a minimum of effort; experience has
+	/// shown that game programmers will not use a library unless they can figure it out 
+	/// on their own in a matter of a few minutes. This last consideration rules out the 
+	/// possibility of using a heavily templated library such as that found in Boost.
+	///
+	/// Distributions
+	/// The generation of random number generation distributions other than linear
+	/// distributions is part of an optional layer that sits on top of this core 
+	/// generation layer. One implementation can be found in earandom_distribution.h.
+	///
+	/// Random Generator Misuses
+	/// It is worth mentioning that it is surprisingly common for users of random number
+	/// generator classes to come to the belief that the generator is broken when in fact
+	/// they are misusing the generator. Common misuses of generators include:
+	///     - Seeding a generator with the same seed every time it's used.
+	///     - Seeding two generators at the same time via the system clock and 
+	///        finding that they produce idential values.
+	///     - Using 'RandomUint32Uniform() % 5000' instead of 'RandomUint32Uniform(5000)'.
+	///     - Inventing flawed distribution generators.
+	///     - Misusing the results of a generator but assuming the generator is
+	///        yielding bad values.
+	///     - Creating a random number generator for a single use right when it 
+	///        is needed. This is usually bad because the first generated value is 
+	///        no more random than the seed used to generate the number.
+	///
+	/// Weaknesses of the C Library rand Function
+	/// The C library rand function does an OK job as a basic random number generator.
+	/// for the purposes of writing applets of various types. However, it is not an 
+	/// optimal generic solution. Some reasons include:
+	///     - Generates only numbers between 0 and RAND_MAX, which is usually 32767.
+	///     - Doesn't generate floating point numbers.
+	///     - Doesn't generate random numbers within a prescribed range. Using 
+	///        the % operator to rectify this is slow (requires integer division) and 
+	///        produces a lopsided distribution unless N is evenly divisble into RAND_MAX.
+	///     - Generates rather poor random numbers. In particular, they tend to have 
+	///        obvious patterns in the low bits.
+	///     - A single instance of a generator is shared with the entire application
+	///        and there is no way to make another instance.
+	///     - Is slow. Generating a random number via rand() was measured as 70% slower
+	///        than the fast generator provided here (Intel Pentium P4 / MSVC++).
+	///
+	/// How to fill a container or sequence with random uint32_t values:
+	///     #include <eastl/algorithm.h>   // or #include <algorithm> to use std STL.
+	///     
+	///     EA::StdC::Random rand(someSeedValue);                       // We can just use EA::StdC::Random directly because 
+	///     eastl::generate(myVector.begin(), myVector.end(), rand);    // it has an operator() that returns uint32_t.
+	///
+	/// How to randomize (shuffle) an existing container or sequence of uint32_t values:
+	///     EA::StdC::Random rand(someSeedValue);
+	///     eastl::random_shuffle(myVector.begin(), myVector.end(), rand);
+	/// 
+	/// How to fill a container or sequence with random double:
+	///     struct GenerateDouble { 
+	///         EA::StdC::Random mRand;                                 // We need to make a tiny struct that simply 
+	///         GenerateDouble(uint32_t seed) : mRand(seed){}           // has an operator() that returns double.
+	///         double operator(){ mRand.RandomDoubleUniform(); }
+	///     };
+	///     GenerateDouble rand(someSeedValue);
+	///     eastl::generate(myVector.begin(), myVector.end(), rand);
+	/////////////////////////////////////////////////////////////////////////////////////
+
+
+
+	/// GetRandomSeed
+	///
+	/// This type of function is also known as an "entropy collector".
+	/// This function generates a pseudorandom number generator seed. As such, this function is 
+	/// a random number generator itself. However, this random number generator is likely to be
+	/// much slower than a standard pseudorandom number generator on most systems. Some systems
+	/// such as Linux support seeding natively via reading from the /dev/random file. Other
+	/// systems that don't support that natively implement it by reading sources of randomness
+	/// on the system such as the system clock, input device state, processor state, etc.
+	EASTDC_API void GetRandomSeed(void* pSeed, size_t nLength);
+
+
+	// Random number generator prototype
+	// For ease of readability, we display a condensed version of a random number generator.
+	// Each function is present for a reason. For example, the functions that take a limit
+	// argument are provide an efficient and reliable implementation of generator with a
+	// limited return value. It is both inefficient and unreliable to generate a random 
+	// integer within a range by using the % operator, as is commonly done. 
+	//
+	// class Random
+	// {
+	// public:
+	//     Random(uint32_t nSeed = 0xffffffff);
+	//     Random(const Random& random);
+	//     Random& operator=(const Random& random);
+	// 
+	//     uint32_t GetSeed() const;
+	//     void     SetSeed(uint32_t nSeed = 0xffffffff);
+	// 
+	//     uint32_t operator()(uint32_t nLimit = 0);
+	//     uint32_t RandomUint32Uniform();                                                        
+	//     uint32_t RandomUint32Uniform(uint32_t nLimit);
+	//     double   RandomDoubleUniform();
+	//     double   RandomDoubleUniform(double limit);
+	// };
+
+
+
+	/// RandomLinearCongruential
+	///
+	/// Implements a random number generator via the linear congruential algorithm.
+	/// This algorithm generates good enough pseudorandom numbers for most simulation
+	/// uses. Its biggest weakness is that there are some patterns that occur in the 
+	/// lower bits. 
+	///
+	/// This generator optimizes speed and size at the cost of randomness.
+	///
+	class EASTDC_API RandomLinearCongruential
+	{
+	public:
+		typedef uint32_t result_type;
+
+		/// RandomLinearCongruential
+		/// Constructs the random number generator with a given initial state (seed).
+		/// If the seed is 0xffffffff (MAX_UINT32), then the seed is generated automatically
+		/// by a semi-random internal mechanism such as reading the system clock. Note that 
+		/// seeding by this mechanism can yield unexpected poor results if you create multiple
+		/// instances of this class within a short period of time, as they will all get the 
+		/// same seed due to the system clock having not advanced.
+		RandomLinearCongruential(uint32_t nSeed = 0xffffffff);
+
+		/// RandomLinearCongruential
+		/// Copy constructor
+		RandomLinearCongruential(const RandomLinearCongruential& randomLC);
+
+		/// operator =
+		RandomLinearCongruential& operator=(const RandomLinearCongruential& randomLC);
+
+		/// GetSeed
+		/// Gets the state of the random number generator, which can be entirely 
+		/// defined by a single uint32_t. 
+		uint32_t GetSeed() const;
+
+		/// SetSeed
+		/// Sets the current state of the random number generator, which can be 
+		/// entirely defined by a single uint32_t. If you want random number generation
+		/// to appear random, the seeds that you supply must themselves be randomly 
+		/// selected. 
+		void SetSeed(uint32_t nSeed = 0xffffffff);
+
+		/// operator ()
+		/// Generates a random uint32 with an optional limit. Acts the same as the 
+		/// RandomUint32Uniform(uint32_t nLimit) function. This function is useful for
+		/// some templated uses whereby you want the class to act as a function object.
+		/// If the input nLimit is 0, then the return value is from 0 to MAX_UINT32 inclusively.
+		uint32_t operator()(uint32_t nLimit = 0);
+
+		/// RandomUint32Uniform
+		/// Return value is from 0 to MAX_UINT32 inclusively, with uniform probability.
+		/// This is the most basic random integer generator for this class; it has no 
+		/// extra options but is also the fastest. Note that if you want a random
+		/// integer between 0 and some value, you should use RandomUint32Uniform(nLimit)
+		/// and not use RandomUint32Uniform() % nLimit. The former is both faster and 
+		/// more random; using % to achieve a truly random distribution fails unless
+		/// nLimit is evenly divisible into MAX_UINT32.
+		uint32_t RandomUint32Uniform();
+
+		/// RandomUint32Uniform (with limit)
+		/// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
+		uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+		/// RandomDoubleUniform
+		/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
+		double RandomDoubleUniform();
+
+		/// RandomDoubleUniform (with limit)
+		/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
+		/// limit is a value > 0.
+		double RandomDoubleUniform(double limit);
+
+	protected:
+		uint32_t mnSeed;
+	};
+
+
+
+	/// RandomTaus
+	///
+	/// P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators",
+	/// Mathematics of Computation, 65, 213 (1996), 203-213.
+	///
+	/// RandomTaus is slower than the other EARandom generators but has only 12 bytes of 
+	/// state data. RandomLinearCongruental has only 4 bytes of data but is not as 
+	/// random as RandomTaus. RandomMersenneTwister is more random than RandomTaus but 
+	/// has about 2500 bytes of state data. Thus RandomTaus is a tradeoff.
+	///
+	/// This generator optimizes randomness and and to some degree size at the cost of speed.
+	///
+	class EASTDC_API RandomTaus
+	{
+	public:
+		typedef uint32_t result_type;
+
+		RandomTaus(uint32_t nSeed = 0xffffffff);
+		RandomTaus(const uint32_t* pSeedArray); // Array of 3 uint32_t values.
+
+		RandomTaus(const RandomTaus& randomT);
+		RandomTaus& operator=(const RandomTaus& randomT);
+
+		// Single uint32_t version, for compatibility.
+		// Use the seed array version for best behavior.
+		// Not guaranteed to return the uint32_t set by SetSeed(uint32_t).
+		uint32_t GetSeed() const;
+		void     SetSeed(uint32_t nSeed = 0xffffffff);
+
+		void GetSeed(uint32_t* pSeedArray) const; // Array of 3 uint32_t values.
+		void SetSeed(const uint32_t* pSeedArray); // Array of 3 uint32_t values.
+
+		/// Output is in range of [0, nLimit) with uniform distribution.
+		uint32_t operator()(uint32_t nLimit = 0);
+
+		/// Output is in range of [0, UINT32_MAX] with uniform distribution.
+		uint32_t RandomUint32Uniform();
+
+		/// Output is in range of [0, nLimit) with uniform distribution.
+		uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+		/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
+		double RandomDoubleUniform();
+
+		/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
+		/// limit is a value > 0.
+		double RandomDoubleUniform(double limit);
+
+	protected:
+		uint32_t mState[3];
+	};
+
+
+
+	/// \class RandomMersenneTwister
+	/// \brief Implements a random number generator via the Mersenne Twister algorithm. 
+	///
+	/// This algorithm is popular for its very high degree of randomness (period of 2^19937-1
+	/// with 623-dimensional equidistribution) while achieving good speed. 
+	///
+	/// This generator optimizes randomness and to some degree speed at the cost of size.
+	///
+	/// Algorithm Reference:
+	/// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
+	/// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions 
+	/// on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
+	/// See http://www.math.keio.ac.jp/~matumoto/emt.html
+	///
+	/// The Mersenne Twister is an algorithm for generating random numbers. 
+	/// It was designed with consideration of the flaws in various other 
+	/// generators. It has a period of 2^19937-1 and the order of equidistribution
+	/// of 623 dimensions. It is also quite fast; it avoids multiplication and
+	/// division.
+	/// 
+	/// License:
+	///     Permission of Commercial Use of Mersenne Twister
+	///     We Makoto Matsumoto and Takuji Nishimura decided to 
+	///     let MT be used in commercial products freely.
+	///
+	class EASTDC_API RandomMersenneTwister
+	{
+	public:
+		/// enum kSeedArrayCount
+		/// This enum is public because it allows the user to know how much 
+		/// data or space to provide for the GetSeed and SetSeed functions.
+		enum { kSeedArrayCount = 625 };  // 624 + 1.
+
+		RandomMersenneTwister(uint32_t nSeed = 0xffffffff);
+		RandomMersenneTwister(const uint32_t seedArray[], unsigned nSeedArraySize);
+		RandomMersenneTwister(const RandomMersenneTwister& randomMT);
+
+		RandomMersenneTwister& operator=(const RandomMersenneTwister& randomMT);
+
+		// GetSeed retrieves the current seed. The nSeedArraySize parameter should 
+		// how many values the seedArray holds. Normally it should be kSeedArrayCount values.
+		// The return value is the number of items written to the seedArray, which will
+		// be min(nSeedArraySize, kSeedArrayCount).
+		unsigned GetSeed(uint32_t seedArray[], unsigned nSeedArraySize = kSeedArrayCount) const;
+
+		// Sets the seed to be used for random number generation. Using GetSeed and SetSeed
+		// allows for saving the state of the random number generator between application runs.
+		// nSeedArraySize must be at least two.
+		void SetSeed(const uint32_t seedArray[], unsigned nSeedArraySize = kSeedArrayCount);
+
+		// This is a simple seed specification function. It will work OK for many cases but 
+		// doesn't provide the direct mapping of state that the other SetSeed function does.
+		// A seed of 0xffffffff results in the generation of a random seed from system information.
+		void SetSeed(uint32_t nSeed = 0xffffffff);
+
+		/// Output is in range of [0, nLimit) with uniform distribution.
+		uint32_t operator()(uint32_t nLimit = 0);
+
+		/// Output is in range of [0, UINT32_MAX] with uniform distribution.
+		uint32_t RandomUint32Uniform();
+
+		/// Output is in range of [0, nLimit) with uniform distribution.                                                  
+		uint32_t RandomUint32Uniform(uint32_t nLimit);
+
+		/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
+		double   RandomDoubleUniform();
+
+		/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
+		/// limit is a value > 0.
+		double   RandomDoubleUniform(double limit);
+
+	protected:
+		void     Reload();
+		uint32_t Hash(int t, int c);
+
+	protected:
+		enum { kStateCount = 624 };
+
+		uint32_t  mState[kStateCount]; // State data.
+		uint32_t* mpNextState;         // Next state data.
+		int32_t   mnCountRemaining;    // Count of remaining entries before reloading.
+	};
+
+
+
+	// Typedefs
+
+	/// class Random
+	/// Default random number generator. We use the RandomLinearCongruential 
+	/// generator as random because for most uses it is random enough and it 
+	/// uses up very little space and is fairly fast.
+	typedef RandomLinearCongruential Random;
+
+	/// class RandomSmall
+	/// Implements a random number generator with a small footprint. 
+	/// The tradeoff is that it is not as highly random as other generators 
+	/// and perhaps not as fast as other generators.
+	typedef RandomLinearCongruential RandomSmall;
+
+	/// class RandomFast
+	/// Implements a random number generator optimized for speed. 
+	/// The tradeoff is that it is not as highly random as other generators 
+	/// and perhaps not as fast as other generators.
+	typedef RandomLinearCongruential RandomFast;
+
+	/// class RandomQuality
+	/// Implements a random number generator optimized for high randomness. 
+	/// The tradeoff for being highly random is that the space used by the 
+	/// class is not small.
+	typedef RandomMersenneTwister RandomQuality;
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inlines
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA
+{
+	namespace StdC
+	{
+		////////////////////////////////
+		// RandomLinearCongruential   //
+		////////////////////////////////
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline RandomLinearCongruential::RandomLinearCongruential(uint32_t nSeed)
+		{
+			SetSeed(nSeed);
+		}
+
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline RandomLinearCongruential::RandomLinearCongruential(const RandomLinearCongruential& randomLC)
+		{
+			SetSeed(randomLC.GetSeed());
+		}
+
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline RandomLinearCongruential& RandomLinearCongruential::operator=(const RandomLinearCongruential& randomLC)
+
+		{
+			SetSeed(randomLC.GetSeed());
+			return *this;
+		}
+
+
+		// Inlined because an inlined implementation would be smaller than 
+		// a function call, as it is simply a reading of a variable.
+		inline uint32_t RandomLinearCongruential::GetSeed() const
+		{
+			return mnSeed;
+		}
+
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline uint32_t RandomLinearCongruential::operator()(uint32_t nLimit)
+		{
+			return RandomUint32Uniform(nLimit);
+		}
+
+
+		inline uint32_t RandomLinearCongruential::RandomUint32Uniform()
+		{
+			// If mnSeed == 0, then we would have a problem. But in practice you 
+			// will never get an mnSeed of zero from an mnSeed of non-zero.
+			const uint64_t nResult64 = mnSeed * (uint64_t)1103515245 + 12345;
+			mnSeed = (uint32_t)nResult64;
+			return (uint32_t)(nResult64 >> 16);
+		}
+
+
+		// Inlined because an inlined implementation would be hardly larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call. Since the only operation beyond the function
+		// call is a multiply, we can feel safe that inlining this is a win.
+		inline double RandomLinearCongruential::RandomDoubleUniform(double limit)
+		{
+			// For the time being, we simply return rand * limit. This is 
+			// however not an ideal solution because in going from a range
+			// of [0,1) to a range of [0,2000) you are expanding the bit
+			// information and thus not yielding all possible values 
+			// between 0 and 2000. At least you will have a distribution
+			// that is largely still uniform between 0 and 2000. Ideally
+			// we can find an algorithm that generates more possible bit
+			// patterns between 0 and 2000 (for example).
+			return RandomDoubleUniform() * limit;
+		}
+
+
+
+		////////////////////////////////
+		// RandomTaus                 //
+		////////////////////////////////
+
+		inline RandomTaus::RandomTaus(uint32_t nSeed)
+		{
+			SetSeed(nSeed);
+		}
+
+		inline RandomTaus::RandomTaus(const uint32_t* pSeedArray)
+		{
+			SetSeed(pSeedArray);
+		}
+
+		inline RandomTaus::RandomTaus(const RandomTaus& randomT)
+		{
+			memcpy(mState, randomT.mState, sizeof(mState));
+		}
+
+		inline RandomTaus& RandomTaus::operator=(const RandomTaus& randomT)
+		{
+			memcpy(mState, randomT.mState, sizeof(mState));
+			return *this;
+		}
+
+		inline void RandomTaus::GetSeed(uint32_t* pSeedArray) const
+		{
+			memcpy(pSeedArray, mState, sizeof(mState));
+		}
+
+		inline uint32_t RandomTaus::operator()(uint32_t nLimit)
+		{
+			return RandomUint32Uniform(nLimit);
+		}
+
+
+
+		////////////////////////////////
+		// RandomMersenneTwister      //
+		////////////////////////////////
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline RandomMersenneTwister::RandomMersenneTwister(const RandomMersenneTwister& randomMT)
+		{
+			operator=(randomMT); 
+		}
+
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline uint32_t RandomMersenneTwister::operator()(uint32_t nLimit)
+		{
+			return RandomUint32Uniform(nLimit);
+		}
+
+
+		// Inlined because an inlined implementation would be no larger than 
+		// non-inline implementation due to the fact that the code here is but 
+		// a single function call.
+		inline double RandomMersenneTwister::RandomDoubleUniform(double limit)
+		{
+			// For the time being, we simply return rand * limit. This is 
+			// however not an ideal solution because in going from a range
+			// of [0,1) to a range of [0,2000) you are expanding the bit
+			// information and thus not yielding all possible values 
+			// between 0 and 2000. At least you will have a distribution
+			// that is largely still uniform between 0 and 2000. Ideally
+			// we can find an algorithm that generates more possible bit
+			// patterns between 0 and 2000 (for example).
+			return RandomDoubleUniform() * limit;
+		}
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 445 - 0
include/EAStdC/EARandomDistribution.h

@@ -0,0 +1,445 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements a basic set of random number generators suitable for game
+// development usage.
+///////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+// This code includes basic random number generator functionality. It is good for
+// most game development uses with the exception of cryptographic uses and legally 
+// controlled gambling mechanisms. For example, cryptographic random number generator 
+// requirements have additional considerations regarding protection against 
+// cryptographic attacks.
+//
+// There are many free C/C++ random number packages available today. Many of them 
+// are rather comprehensive and some are fairly flexible. The design of this package
+// is based on the needs of game programmers and intentionally avoids some of the 
+// complications of other packages. This package is designed first and foremost to 
+// be fast and to offer at least the option of low footprint. Secondly this package
+// is designed to be very easily understood with a minimum of effort; experience has
+// shown that game programmers will not use a library unless they can figure it out 
+// on their own in a matter of a few minutes. This last consideration rules out the 
+// possibility of using a heavily templated library such as that found in Boost.
+//
+// Functions:
+//     bool     RandomBool(Random& r);
+//     int32_t  Random2(Random& r);
+//     int32_t  Random4(Random& r);
+//     int32_t  Random8(Random& r);
+//     int32_t  Random16(Random& r);
+//     int32_t  Random32(Random& r);
+//     int32_t  Random64(Random& r);
+//     int32_t  Random128(Random& r);
+//     int32_t  Random256(Random& r);
+//     uint32_t RandomLimit(Random& r, uint32_t nLimit);
+//     int32_t  RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo);
+//     int32_t  RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd);
+//     double   RandomDoubleUniformRange(Random& r, double begin, double end);
+//     uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, float weights[]);
+//     int32_t  RandomInt32GaussianRange(Random& r, int32_t nBegin, int32_t nEnd);
+//     Float    RandomFloatGaussianRange(Random& r, Float fBegin, Float fEnd);
+//     int32_t  RandomInt32TriangleRange(Random& r, int32_t nBegin, int32_t nEnd);
+//     Float    RandomFloatTriangleRange(Random& r, Float fBegin, Float fEnd);
+//
+/////////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EARANDOMDISTRIBUTION_H
+#define EASTDC_EARANDOMDISTRIBUTION_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+#include <EAAssert/eaassert.h>
+#include <EAStdC/EARandom.h>
+#include <math.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/// RandomBool
+	/// Returns true or false.
+	template <typename Random>
+	bool RandomBool(Random& r)
+	{
+		return (r.RandomUint32Uniform() & 0x80000000) != 0;
+	} 
+
+
+	/// Random2
+	/// Returns a value between 0 and 1, inclusively.
+	template <typename Random>
+	int32_t Random2(Random& r)
+	{  // Don't trust the low bits, as some generators don't have good low bits.
+		return (int32_t)(r.RandomUint32Uniform() >> 31);
+	} 
+
+
+	/// Random4
+	/// Returns a value between 0 and 3, inclusively.
+	template <typename Random>
+	int32_t Random4(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 30);
+	} 
+
+
+	/// Random8
+	/// Returns a value between 0 and 7, inclusively.
+	template <typename Random>
+	int32_t Random8(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 29);
+	} 
+
+
+	/// Random16
+	/// Returns a value between 0 and 15, inclusively.
+	template <typename Random>
+	int32_t Random16(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 28);
+	} 
+
+
+	/// Random32
+	/// Returns a value between 0 and 31, inclusively.
+	template <typename Random>
+	int32_t Random32(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 27);
+	} 
+
+
+	/// Random64
+	/// Returns a value between 0 and 63, inclusively.
+	template <typename Random>
+	int32_t Random64(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 26);
+	} 
+
+
+	/// Random128
+	/// Returns a value between 0 and 127, inclusively.
+	template <typename Random>
+	int32_t Random128(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 25);
+	} 
+
+
+	/// Random256
+	/// Returns a value between 0 and 255, inclusively.
+	template <typename Random>
+	int32_t Random256(Random& r)
+	{
+		return (int32_t)(r.RandomUint32Uniform() >> 24);
+	} 
+
+
+	/// RandomLimit
+	/// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
+	template <typename Random>
+	uint32_t RandomLimit(Random& r, uint32_t nLimit)
+	{
+		if((nLimit & (nLimit - 1)) == 0)  // If nLimit is an unsigned power of 2...
+			 return (uint32_t)((r.RandomUint32Uniform() * (uint64_t)nLimit) >> 32); // Scales the value from [0, MAX_UINT32] to a range of [0, nLimit).
+
+		uint32_t bits, returnValue;
+		do
+		{
+			bits = r.RandomUint32Uniform();
+			returnValue = (bits % nLimit);
+		}                                                      // Ignore the highest bits of the representation that are remainder bits. 
+		while((bits + ((nLimit - 1) - returnValue) < bits));   // This is a faster way of testing that (bits < (0xFFFFFFFF - (0xFFFFFFFF % nLimit))). 
+															   // This depends on unsigned integer wraparound occurring.
+		return returnValue;
+	} 
+
+
+	/// RandomLimitFastWithBias
+	/// Return value is from 0 to nLimit-1 inclusively, but with a small amount of bias towards
+	/// some values. This function is not suitable for uses where you absolutely need perfectly
+	/// uniform distribution, and probably not suitable for use with limits greater than ~2^20.
+	/// However, this function is significantly faster than the RandomLimit function and is useful 
+	/// for generating many values quickly where truly uniform results aren't critical.
+	/// The bias is roughly one in a billion for smaller numbers, but larger for huge numbers (e.g. numbers > 2^20). 
+	/// If you were to call RandomUint32Uniform(0xfffffff0) 2^32 times, you'd find that 16 of the numbers 
+	/// would be returned twice, while the rest would be returned once.
+	template <typename Random>
+	uint32_t RandomLimitFastBiased(Random& r, uint32_t nLimit)
+	{
+		const uint32_t nRandNoLimit = r.RandomUint32Uniform();
+		const uint32_t nReturnValue = (uint32_t)((nRandNoLimit * (uint64_t)nLimit) >> 32);
+		return nReturnValue; 
+	} 
+
+
+	/// RandomPowerOfTwo
+	/// Returns a value between 0 and 2 ^ nPowerOfTwo - 1, inclusively. 
+	/// This is a generalized form of the RandomN set of functions.
+	template <typename Random>
+	int32_t RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo)
+	{
+		//assert(nPowerOfTwo <= 32);
+		return (int32_t)(r.RandomUint32Uniform() >> (32 - nPowerOfTwo));
+	} 
+
+
+	/// RandomInt32UniformRange
+	/// Return value is from nBegin to nEnd-1 inclusively, with uniform probability.
+	template <typename Random>
+	int32_t RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd)
+	{
+		return nBegin + (int32_t)r.RandomUint32Uniform((uint32_t)(nEnd - nBegin));
+	}
+
+
+	/// RandomDoubleUniformRange
+	/// Return value is in range of [nBegin, nEnd) with uniform probability.
+	template <typename Random>
+	double RandomDoubleUniformRange(Random& r, double begin, double end)
+	{
+		const double result = begin + r.RandomDoubleUniform(end - begin);
+
+		if(result >= end)   // FPU roundoff errors can cause the result to 
+			return end;     // go slightly outside the range. We deal with 
+		if(result < begin)  // the possibility of this.
+			return begin;
+		return result;
+	}
+
+
+	/// RandomUint32WeightedChoice
+	///
+	/// Return value is from 0 to nLimit-1 inclusively, with probabilities proportional to weights.
+	/// The input array weights must be of length <nLimit>. These values are used to 
+	/// determine the probability of each choice. That is, weight[i] is proportional 
+	/// to the probability that this function will return i. Negative values are ignored. 
+	/// This function is useful in generating a custom distribution.
+	///
+	/// Example usage:
+	///    const float weights[7] = { 128, 64, 32, 16, 8, 4, 2 };  // Create a logarithmic distribution in the range of [0, 6).
+	///
+	///    uint32_t x = RandomUint32WeightedChoice(random, 7, weights);
+	///
+	template <typename Random>
+	uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, const float weights[])
+	{
+		if(nLimit >= 2)
+		{
+			float weightSum = 0;
+
+			for(uint32_t i = 0; i < nLimit; ++i)
+			{
+				const float weight = weights[i];
+
+				if(weight > 0)
+					weightSum += weight;
+			}
+
+			if(weightSum > 0)
+			{
+				float value = (float)RandomDoubleUniformRange(r, 0, weightSum);
+
+				// Do a linear search. A binary search would be faster for 
+				// cases where the array size is > ~10.
+				for(uint32_t j = 0; j < nLimit; ++j)
+				{
+					const float weight = weights[j];
+
+					if(weight > 0)
+					{
+						if(value < weight)
+							return j;
+						value -= weight;
+					}
+				}
+			}
+			else
+				return r.RandomUint32Uniform(nLimit);
+		}
+
+		// Normally we shouldn't get here, but we might due to rounding errors.
+		return nLimit - 1;
+	}
+
+
+
+
+	/// RandomInt32GaussianRange
+	///
+	///  Creates an approximation to a normal distribution (a.k.a. "Gaussian", 
+	///  "bell-curve") in the range of [nBegin, nEnd).
+	///
+	///         |                                       
+	///         |                ****                   
+	///         |              *      *                 
+	///         |             *        *                
+	///         |            *          *               
+	///         |           *            *              
+	///         |          *              *             
+	///         |         *                *            
+	///         |        *                  *           
+	///         |    * *                      * *       
+	///         ----------------------------------------
+	///              |                          |
+	///            begin                       end
+	///
+	template <typename Random>
+	int32_t RandomInt32GaussianRange(Random& r, int32_t nBegin, int32_t nEnd)
+	{
+		const uint32_t t0     = r.RandomUint32Uniform();
+		const uint32_t t1     = r.RandomUint32Uniform();
+		const uint32_t t2     = r.RandomUint32Uniform();
+		const uint32_t t3     = r.RandomUint32Uniform();
+		const uint64_t tcubic = ((((uint64_t)t0 + t1) + ((uint64_t)t2 + t3) + 2) >> 2);
+
+		return nBegin + (int32_t)((tcubic * (uint32_t)(nEnd - nBegin)) >> 32);
+	}
+
+
+	/// RandomFloatGaussianRange
+	///
+	/// Generates a floating point value with an approximated 
+	/// Guassian distribution in the range of [fBegin, fEnd).
+	///
+	/// Example usage:
+	///    float x = RandomFloatGaussianRange(random, 0.f, 100.f);
+	///
+	template <typename Random, typename Float>
+	Float RandomFloatGaussianRange(Random& r, Float fBegin, Float fEnd)
+	{
+		const Float x0 = (Float)r.RandomDoubleUniform();
+		const Float x1 = (Float)r.RandomDoubleUniform();
+		const Float x2 = (Float)r.RandomDoubleUniform();
+
+		return fBegin + ((fEnd - fBegin) * Float(0.33333333) * (x0 + x1 + x2));
+	}
+
+
+
+	/// RandomInt32TriangleRange
+	///
+	/// Creates a "triangle" distribution in the range of [nBegin, nEnd).
+	///
+	///         |                                     
+	///         |                *                    
+	///         |                                     
+	///         |             *     *                 
+	///         |                                     
+	///         |          *           *              
+	///         |                                     
+	///         |       *                 *           
+	///         |                                     
+	///         |    *                       *        
+	///         --------------------------------------
+	///              |                       |
+	///            begin                    end
+	///
+	template <typename Random>
+	int32_t RandomInt32TriangleRange(Random& r, int32_t nBegin, int32_t nEnd)
+	{
+		const uint32_t t0   = r.RandomUint32Uniform(); 
+		const uint32_t t1   = r.RandomUint32Uniform();
+		const uint64_t ttri = (t0 >> 1) + (t1 >> 1) + (t0 & t1 & 1); // triangular from 0...2^31-1
+
+		return nBegin + (int32_t)((ttri * (uint32_t)(nEnd - nBegin)) >> 32);
+	}
+
+
+	/// RandomFloatTriangleRange
+	///
+	/// Generates a floating point value with a triangular distribution 
+	/// in the range of [fBegin, fEnd).
+	///
+	/// Example usage:
+	///    double x = RandomFloatTriangleRange(random, 0.0, 100.0);
+	///
+	template <typename Random, typename Float>
+	Float RandomFloatTriangleRange(Random& r, Float fBegin, Float fEnd)
+	{
+		const Float u0 = (Float)r.RandomDoubleUniform();
+		const Float u1 = (Float)r.RandomDoubleUniform();
+
+		return fBegin + (fEnd - fBegin) * Float(0.5) * (u0 + u1);
+	}
+
+	///	Devroye, Luc (1986). "Discrete Univariate Distributions" (PDF).
+	///	Non-Uniform Random Variate Generation. New York: Springer-Verlag. p. 505.
+	///
+	///	Poisson generator based upon the inversion by sequential search.
+	///
+	///	This Poisson Random generator only works for a mean that is <= 100.
+	///
+	/// "x" is a uniform distributed float in the range of 0.0f and 1.0f.
+	///	"mean" is the expected number of occurrences during a given interval.
+	///
+	inline int32_t RandomInt32Poisson(float x, float mean)
+	{
+		EA_ASSERT(x >= 0.0f && x <= 1.0f);
+		EA_ASSERT_MSG(mean >= 0.f && mean <= 100.0f, "Poisson random generator only works for means that are <= 100.0f");
+
+		// clamp x value
+		if (x < 0.f) x = 0.f;
+		else if (x > 1.f) x = 1.f;
+
+		// clamp mean value
+		if (mean < 0.f) mean = 0.f;
+		else if (mean >= 100.f) mean = 100.f;
+
+		int32_t k = 0;                    // Counter
+		const int32_t max_k = 1000;       // k upper limit
+		float P = (float)::exp(-mean);    // probability
+		float sum = P;                    // cumulant
+		if (sum >= x) return 0;           // done already
+		for (k = 1; k < max_k; ++k)
+		{                                 // Loop over all k:s
+			P *= mean / (float)k;         // Calc next prob
+			sum += P;                     // Increase cumulant
+			if (sum >= x) break;          // Leave loop
+		}
+
+		return k;                         // return random number
+	}
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 217 - 0
include/EAStdC/EAScanf.h

@@ -0,0 +1,217 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// This module implements the following functions.
+//   int Cscanf(ReadFunction pReadFunction, void* pContext, const char_t* pFormat, ...);
+//   int Fscanf(FILE* pFile, const char_t* pFormat, ...);
+//   int Scanf(const char_t* pFormat, ...);
+//   int Sscanf(char_t* pDestination, const char_t* pFormat, ...);
+//
+//   int Vcscanf(ReadFunction pReadFunction, void* pContext, const char_t* pFormat, va_list arguments);
+//   int Vfscanf(FILE* pFile, const char_t* pFormat, va_list arguments);
+//   int Vscanf(const char_t* pFormat, va_list arguments);
+//   int Vsscanf(char_t* pDestination, const char_t* pFormat, va_list arguments);
+//
+// Limitations
+// As of this writing (September 2007), the %[] modifier supports only single-byte
+// characters in the 8 bit version and supports only the first 256 characters in 
+// the 16/32 bit versions. If you need support for additional characters, consult the 
+// maintainer of this package and you should be able to get it within 48 hours.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASCANF_H
+#define EASTDC_EASCANF_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/stdioEA.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/////////////////////////////////////////////////////////////////////////////
+	// ReadAction
+	//
+	enum ReadAction
+	{
+		kReadActionBegin,           // Called upon first entering scanf. The value param will be 1 for UTF8 and 2 for UCS2. This allows the ReadFunction to take any setup actions.
+		kReadActionEnd,             // Called upon just exiting scanf. This allows the ReadFunction to take any cleanup actions. The value param is unused.
+		kReadActionRead,            // Read and return a single UCS2 Unicode character, very much like the fgetc function. Return -1 upon error or EOF; GetLastError will be used to distinguish between the two. The read function may need to convert the data source to Unicode if the data source is not UCS-encoded. The value param is unused.
+		kReadActionUnread,          // Push back the given UCS2 Unicode value. Return -1 upon error, 0 on success. The read function may need to convert the data source from Unicode if the data source is not UCS-encoded. The value param is unused.
+		kReadActionGetAtEnd,        // Return 1 if at end of data, 0 if not. The value param is unused.
+		kReadActionGetLastError     // Return the last file read error value. Zero means no error. The value param is unused.
+	};
+
+
+	/////////////////////////////////////////////////////////////////////////////
+	// kReadError
+	//
+	const int kReadError = -1;
+
+
+	/////////////////////////////////////////////////////////////////////////////
+	// ReadFunction8
+	//
+	// This a multi-purpose callback function that's provided by the user for 
+	// the scanf function and similar data reading functions. It is called with
+	// one of the ReadAction enumerations, and the expected behaviour depends 
+	// on the enumeration.
+	//
+	// The meaning of the value parameter depends on the ReadAction. See ReadAction
+	// for documentation of each case.
+	// 
+	// The pContext parameter is the value the user originally provided to 
+	// scanf or similar data reading function.
+	//
+	// The function is expected to convert the actual source data into individual
+	// Unicode code points during kReadActionRead. For ASCII text there is no 
+	// conversion involved, but for multi-byte text the ReadFunction will need to 
+	// convert any such text to UCS Unicode.
+	//
+	// Returns the number of chars read on success. Returns 0 when at end of buffer.
+	// Upon error, returns -1 (kReadError, same as EOF).
+	//
+	// The scanf functions will not use kReadActionUnread multiple times in
+	// succession; only at most unread action will be outstanding. Due to the 
+	// specification for scanf, there is no practical way to avoid the requirement 
+	// of being able to unread characters.
+	//
+	// See the source code to the scanf function in order to see some examples
+	// of ReadFunction implementations.
+	//
+	// UTF8 multi-byte characters should be returned as their unsigned value.
+	// Thus even though char8_t is signed, all characters are read and written
+	// as if they are uint8_t. The only time a negative value should be used is
+	// in the case of a -1 return value. This situation exists because it 
+	// exists as such with the C Standard Library, for better or worse.
+	//
+	typedef int (*ReadFunction8)(ReadAction readAction, int value, void* pContext);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// ReadFunction16
+	//
+	// This function is currently identical to ReadFunction8 with the exception
+	// that kReadActionBegin will specify char16_t characters instead of char8_t. 
+	//
+	typedef int (*ReadFunction16)(ReadAction readAction, int value, void* pContext);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// ReadFunction32
+	//
+	// This function is currently identical to ReadFunction8 with the exception
+	// that kReadActionBegin will specify char32_t characters instead of char8_t. 
+	//
+	typedef int (*ReadFunction32)(ReadAction readAction, int value, void* pContext);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// ReadFunctionW
+	//
+	// This function is currently identical to ReadFunction8 with the exception
+	// that kReadActionBegin will specify wchar_t characters instead of char8_t. 
+	//
+	typedef int (*ReadFunctionW)(ReadAction readAction, int value, void* pContext);
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	// EAScanf configuration parameters
+	//
+	#ifndef EASCANF_FIELD_MAX               // Defines the maximum supported length of a field, 
+		#define EASCANF_FIELD_MAX 1024      // except string fields, which have no size limit.
+	#endif                                  // This value relates to the size of buffers used in the stack space.
+
+	#ifndef EASCANF_MS_STYLE_S_FORMAT       // Microsoft uses a non-standard interpretation of the %s field type. 
+		#define EASCANF_MS_STYLE_S_FORMAT 1 // For wsprintf MSVC interprets %s as a wchar_t string and %S as a char string.
+	#endif                                  // You can make your code portable by using %hs and %ls to force the type.
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// Scanf
+	///
+	EASTDC_API int Cscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, ...);
+	EASTDC_API int Fscanf(FILE* pFile, const char8_t* pFormat, ...);
+	EASTDC_API int Scanf(const char8_t* pFormat, ...);
+	EASTDC_API int Sscanf(const char8_t*  pTextBuffer, const char8_t* pFormat, ...);
+
+	EASTDC_API int Cscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, ...);
+	EASTDC_API int Fscanf(FILE* pFile, const char16_t* pFormat, ...);
+	EASTDC_API int Scanf(const char16_t* pFormat, ...);
+	EASTDC_API int Sscanf(const char16_t* pTextBuffer, const char16_t* pFormat, ...);
+
+	EASTDC_API int Cscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, ...);
+	EASTDC_API int Fscanf(FILE* pFile, const char32_t* pFormat, ...);
+	EASTDC_API int Scanf(const char32_t* pFormat, ...);
+	EASTDC_API int Sscanf(const char32_t* pTextBuffer, const char32_t* pFormat, ...);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		EASTDC_API int Cscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, ...);
+		EASTDC_API int Fscanf(FILE* pFile, const wchar_t* pFormat, ...);
+		EASTDC_API int Scanf(const wchar_t* pFormat, ...);
+		EASTDC_API int Sscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, ...);
+	#endif
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// Vscanf
+	///
+	EASTDC_API int Vcscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, va_list arguments);
+	EASTDC_API int Vfscanf(FILE* pFile, const char8_t* pFormat, va_list arguments);
+	EASTDC_API int Vscanf(const char8_t* pFormat, va_list arguments);
+	EASTDC_API int Vsscanf(const char8_t* pTextBuffer, const char8_t* pFormat, va_list arguments);
+
+	EASTDC_API int Vcscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments);
+	EASTDC_API int Vfscanf(FILE* pFile, const char16_t* pFormat, va_list arguments);
+	EASTDC_API int Vscanf(const char16_t* pFormat, va_list arguments);
+	EASTDC_API int Vsscanf(const char16_t* pTextBuffer, const char16_t* pFormat, va_list arguments);
+
+	EASTDC_API int Vcscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, va_list arguments);
+	EASTDC_API int Vfscanf(FILE* pFile, const char32_t* pFormat, va_list arguments);
+	EASTDC_API int Vscanf(const char32_t* pFormat, va_list arguments);
+	EASTDC_API int Vsscanf(const char32_t* pTextBuffer, const char32_t* pFormat, va_list arguments);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		EASTDC_API int Vcscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, va_list arguments);
+		EASTDC_API int Vfscanf(FILE* pFile, const wchar_t* pFormat, va_list arguments);
+		EASTDC_API int Vscanf(const wchar_t* pFormat, va_list arguments);
+		EASTDC_API int Vsscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, va_list arguments);
+	#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 198 - 0
include/EAStdC/EASingleton.h

@@ -0,0 +1,198 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_EASINGLETON_H
+#define EASTDC_EASINGLETON_H
+
+#include <EAStdC/internal/Config.h>
+#include <EAAssert/eaassert.h>
+
+
+#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4127) // conditional expression is constant
+#endif
+
+
+/// The standard Electronic Arts namespace.
+namespace EA
+{
+namespace StdC
+{
+	/// \class Singleton
+	///
+	/// Singleton adds singleton semantics to derived classes.  It provides
+	/// singleton-style instance accessors and will assert if more than one
+	/// instance of the derived class is constructed.
+	///
+	/// \code
+	/// class UniqueWidget : public EA::StdC::Singleton<UniqueWidget> { };
+	/// UniqueWidget *pWidget = UniqueWidget::GetInstance();
+	/// \endcode
+	///
+	/// \param  T           The classname from which to create the singleton.
+	/// \param  kId         Multiple unique singleton instances of class \a T
+	///                     can be created if they are given unique \a kId
+	///                     numbers.
+	///
+	template <typename T, unsigned int kId = 0>
+	class Singleton
+	{
+	public:
+		typedef T value_type;
+		typedef Singleton<T, kId> this_type;
+
+		/// Return the pointer to the singleton instance.
+		static T* GetInstance()
+		{
+			return static_cast<T*>(spInstance);
+		}
+
+	protected:
+		/// Constructor
+		/// The singleton instance is assigned at construction time.
+		Singleton()
+		{
+			EA_ASSERT_FORMATTED(!spInstance, ("Singleton instance (%p) has already been created", static_cast<T*>(spInstance)));
+			spInstance = this;
+		}
+
+		/// Destructor
+		/// This destructor is intentionally not marked 'virtual'.  We don't
+		/// want to force virtual-ness on our derived class.
+		~Singleton()
+		{
+			spInstance = NULL;
+		}
+
+	private:
+		/// Private (disabled) copy constructor
+		Singleton(const this_type&);
+
+		/// Private (disabled) assignment operator
+		Singleton & operator=(const this_type&);
+
+		/// Static pointer to this singleton's instance.
+		static this_type* spInstance;
+	};
+
+
+	/// \class  SingletonAdapter
+	///
+	/// SingletonAdapter adds singleton semantics to an existing class by
+	/// extending its public interface.  This is useful for creating
+	/// singletons from existing (and potentially externally maintained)
+	/// classes without modifiying the original code directly.
+	///
+	/// To use this class, derive a new class with a unique name from the base
+	/// class (since the static instance pointer will be unique only for this
+	/// class name).
+	///
+	/// \code
+	/// class WidgetSingleton : public EA::StdC::SingletonAdapter<Widget> { };
+	/// \endcode
+	///
+	/// If implicit creation was requested, the singleton's instance will be
+	/// created the first time it is accessed.  Otherwise, CreateInstance() or
+	/// SetInstance() must be called.
+	///
+	/// The singleton's instance can be destructed and freed using Destroy().
+	///
+	/// SingletonAdapter<> is not thread-safe.
+	///
+	/// If you're going to be using \a T exclusively through the
+	/// SingletonAdapter interface, you should consider making its constructor
+	/// and destructor protected members.  That will ensure that instances of
+	/// \a T can only be created by the SingletonAdapter layer.
+	///
+	/// \param  T           The classname from which to create the singleton.
+	/// \param  bImplicitCreation   If implicit creation is requested, the
+	///                     singleton instance will be created on the first
+	///                     attempt to access it.
+	/// \param  kId         Multiple unique singleton instances of class \a T
+	///                     can be created if they are given unique \a kId
+	///                     numbers.
+	///
+	template <typename T, bool bImplicitCreation = false, unsigned int kId = 0>
+	class SingletonAdapter : public T
+	{
+	public:
+		typedef T value_type;
+		typedef SingletonAdapter<T, bImplicitCreation, kId> this_type;
+
+		/// Return the pointer to the singleton instance.
+		/// If \a bImplicitCreation was requested, an instance will be created
+		/// if one does not already exist.
+		static T* GetInstance()
+		{
+			if (bImplicitCreation && !spInstance)
+				return CreateInstance(EASTDC_ALLOC_PREFIX "SingletonAdapter");
+
+			return static_cast<T*>(spInstance);
+		}
+
+		/// This allows you to manually set the instance, which is useful
+		/// if you want to allocate the memory for it yourself. 
+		/// \return A pointer to the previous instance.
+		static T* SetInstance(T* pInstance)
+		{
+			T* const pPrevious = spInstance;
+			spInstance = pInstance;
+			return pPrevious;
+		}
+
+		/// Create this singleton's instance.  If the instance has already
+		/// been created, a pointer to it will simply be returned.
+		/// \return A pointer to the singleton's instance.
+		static T* CreateInstance(const char* pName)
+		{
+			if (!spInstance)
+				spInstance = EASTDC_NEW(pName) SingletonAdapter;
+
+			return spInstance;
+		}
+
+		/// Destroy this singleton's instance.
+		static void DestroyInstance()
+		{
+			if (spInstance)
+			{
+				EASTDC_DELETE spInstance;
+				spInstance = NULL;
+			}
+		}
+
+	protected:
+		/// Constructor
+		SingletonAdapter() { }
+
+		/// Destructor
+		~SingletonAdapter() { }
+
+	private:
+		/// Private (disabled) copy constructor
+		SingletonAdapter(const this_type&);
+
+		/// Private (disabled) assignment operator
+		SingletonAdapter& operator=(const this_type&);
+
+		/// Static pointer to this singleton's instance.
+		static this_type* spInstance;
+	};
+
+} // namespace StdC
+} // namespace EA
+
+
+// Initialize the singletons' static instance pointers to NULL.
+template <typename T, unsigned int kId> EA::StdC::Singleton<T, kId> *EA::StdC::Singleton<T, kId>::spInstance = NULL;
+template <typename T, bool bImplicitCreation, unsigned int kId> EA::StdC::SingletonAdapter<T, bImplicitCreation, kId> *EA::StdC::SingletonAdapter<T, bImplicitCreation, kId>::spInstance = NULL;
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+#endif // Header include guard

+ 326 - 0
include/EAStdC/EASprintf.h

@@ -0,0 +1,326 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// This module implements the functions listed below. 
+//
+// Variable argument versions:
+//   int Cprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, ...);
+//   int Fprintf(FILE* pFile, const char_t* pFormat, ...);
+//   int Printf(cconst char_t* pFormat, ...);
+//   int Sprintf(char_t* pDestination, const char_t* pFormat, ...);
+//   int Snprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+//   int Dprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+//   int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...);
+//
+// Vararg versions:
+//   int Vcprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, va_list arguments);
+//   int Vfprintf(FILE* pFile, const char_t* pFormat, va_list arguments);
+//   int Vprintf(const char_t* pFormat, va_list arguments);
+//   int Vsprintf(char_t* pDestination, const char_t* pFormat, va_list arguments);
+//   int Vsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, va_list arguments);
+//   int Vscprintf(const char* pFormat, va_list arguments);
+//   int Vdprintf(const char* pFormat, va_list arguments);
+//   int StringVcprintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, va_list arguments)
+//
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASPRINTF_H
+#define EASTDC_EASPRINTF_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAScanf.h>
+#include <EAStdC/internal/stdioEA.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	////////////////////////////////////////////////////////////////////////////
+	// WriteFunctionState
+	//
+	// This is used to help the WriteFunction know what state its being called
+	// in. Our sprintf family of functions work by repeatedly calling the 
+	// WriteFunction until all of the string is complete. But due to platform
+	// quirks and limitations, some platforms require the WriteFunction to know
+	// if this is the last write, because they use it to complete a write that
+	// needs to do the complete string all at once. 
+	//
+	enum WriteFunctionState
+	{
+		kWFSBegin,          /// Called once before any data is written, including if there will be no data written.
+		kWFSIntermediate,   /// Called zero or more times, with partial data. UTF8 sequences will always be whole and not split between calls.
+		kWFSEnd             /// Called once after any data is written, including if there was no data written.
+	};
+
+	/////////////////////////////////////////////////////////////////////////////
+	// WriteFunction8
+	//
+	// The input string for WriteFunction8 is assumed to be UTF8 or ASCII-encoded.
+	// Returns number of chars actually written to the destination.
+	// Returns -1 upon error.
+	//
+	// UTF8 sequences will always be whole and not split between calls.
+	// The input pData is *not* guaranteed to be 0-terminated and won't be so for 
+	// some cases, as it's impractical to make it always be so due to the fact 
+	// that users can print truncated read-only string data memory. However, you are
+	// guaranteed to be able to read the character at pData[nCount], as it will 
+	// always be valid readable memory. This may be useful to do because you can
+	// write an optimized pathway for the case that pData is in fact 0-terminated,
+	// which will often be the case.
+	//
+	typedef int (*WriteFunction8)(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// WriteFunction16
+	//
+	// The input string is UCS2 encoded.
+	// Otherwise this is the same as WriteFunction8.
+	//
+	typedef int (*WriteFunction16)(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// WriteFunction32
+	//
+	// The input string is UCS4 encoded.
+	// Otherwise this is the same as WriteFunction8.
+	//
+	typedef int (*WriteFunction32)(const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
+
+
+	/////////////////////////////////////////////////////////////////////////////
+	// WriteFunctionW
+	//
+	// The input string is wchar_t, with wchar_t being UCS2 or UCS4-encoded, depending on its size.
+	// Otherwise this is the same as WriteFunction8.
+	//
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		typedef int (*WriteFunctionW)(const wchar_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
+	#endif
+
+
+	/////////////////////////////////////////////////////////////////////////////
+	// WriteFunctionString
+	//
+	// This allows the implementation of a Sprintf that writes directly into 
+	// a C++ string class instance. This approach may be better because otherwise
+	// you'd have to do an approach where you call Sprintf once to find the length,
+	// resize your string, then call Sprintf again with the string address.
+	// See StringSprintf below for a generic implementation of direct string writing.
+	//
+	template <typename String>
+	int WriteFunctionString(const typename String::value_type* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState /*wfs*/)
+	{
+		String* pString = static_cast<String*>(pContext);
+		pString->append(pData, (uint32_t)nCount);
+		return (int)nCount;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// Vprintf
+	///
+	/// Note that vsnprintf was not added to the C standard until C99.
+	/// Here we follow the C99 standard and have the return value of vsnprintf 
+	/// return the number of required characters to complete the formatted string.
+	/// This is more useful than the way some libraries implement vsnprintf by 
+	/// returning -1 if there isn't enough space. The return value is -1 if there 
+	/// was an "encoding error" or the implementation was unable to return a 
+	/// value > n upon lack of sufficient space as per the C99 standard.
+	///
+	/// Official specification:
+	/// The vsnprintf function is equivalent to snprintf, with the variable 
+	/// argument list replaced by arguments, which shall have been initialized 
+	/// by the va_start macro (and possibly subsequent va_arg calls). 
+	/// The vsnprintf function does not invoke the va_end macro. If copying 
+	/// takes place between objects that overlap, the behavior is undefined.
+	///
+	/// The vsnprintf function returns the number of characters that would 
+	/// have been written had n been sufficiently large, not counting the 
+	/// terminating null character, or a neg ative value if an encoding error 
+	/// occurred. Thus, the null-terminated output has been completely written 
+	/// if and only if the returned value is nonnegative and less than n.
+	///
+	/// The vscprintf function returns the number of chars that are needed for 
+	/// a printf operation. It writes nothing to any destination.
+	///
+	/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
+	///
+	/// Description:
+	///     Vcprintf:  Print to a user-supplied WriteFunction. Can form the foundation for other printfs.
+	///     Vfprintf:  Print to a specific FILE, same as the C vfprintf function. Acts like Vdprintf for platforms where stdout doesn't exist (e.g. consoles) when the FILE is stdout.
+	///     Vprintf:   Print to the stdout FILE, same as the C vfprintf function. Acts like Vdprintf for platforms where stdout doesn't exist (e.g. consoles).
+	///     Vsprintf:  Print to a string, with no capacity specified, same as the C vsprintf function.
+	///     Vsnprintf: Print to a string, with capacity specified, same as the C vsnprintf function.
+	///     Vdprintf:  Print to a debug output destination (e.g. OutputDebugString on Microsoft platforms).
+	///
+	EASTDC_API int Vcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vscprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vdprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+
+	EASTDC_API int Vcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
+
+	EASTDC_API int Vcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		EASTDC_API int Vcprintf(WriteFunctionW pWriteFunctionW, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vsprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vsnprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vscprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+	#endif
+
+	// StringVcprintf
+	// Writes directly into a C++ string object. e.g. StringVcprintf(strObject, "%d", arguments);
+	// Will be faster than eastl::string::printf() as long as the supplied string has a capacity that
+	// doesn't need to keep increasing while being written into.
+	template <typename String>
+	int StringVcprintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, va_list arguments)
+	{
+		return Vcprintf(WriteFunctionString<String>, &s, pFormat, arguments);
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// Printf
+	///
+	/// The printf family implements the printf family of functions strictly to 
+	/// the C99 standard. As of this writing, all C99 functionality is supported
+	/// except the unusual %a field type, which is treated as %g for the time being.
+	/// Additionally, extra functionality such as the following field types and 
+	/// modifiers are supported:
+	///     b      Binary output field type (joins d, i, x, o, etc.). Example: printf("%b", 255) prints "11111111"
+	///     I8     8 bit integer field modifier. Example: printf("%I8d", 0xff) prints "-1"
+	///     I16    16 bit integer field modifier. Example: printf("%I16d", 0xffff) prints "-1"
+	///     I32    32 bit integer field modifier. Example: printf("%I32d", 0xffffffff) prints "-1"
+	///     I64    64 bit integer field modifier. Example: printf("%I64d", 0xffffffffffffffff) prints "-1"
+	///     I128   128 bit integer field modifier. Example: printf("%I128d", 0xffffffffffffffffffffffffffffffff) prints "-1"
+	///     '      Display a thousands separator. Example: printf("%'I16u", 0xffff); prints 65,535
+	///
+	/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
+	///
+	/// Description:
+	///     Cprintf:  Print to a user-supplied WriteFunction. Can form the foundation for other printfs.
+	///     Fprintf:  Print to a specific FILE, same as the C fprintf function. Acts like Dprintf for platforms where stdout doesn't exist (e.g. consoles) when the FILE is stdout.
+	///     Printf:   Print to the stdout FILE, same as the C fprintf function. Acts like Dprintf for platforms where stdout doesn't exist (e.g. consoles).
+	///     Sprintf:  Print to a string, with no capacity specified, same as the C sprintf function.
+	///     Snprintf: Print to a string, with capacity specified, same as the C snprintf function.
+	///     Dprintf:  Print to a debug output destination (e.g. OutputDebugString on Microsoft platforms).
+	///
+	EASTDC_API int Cprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Printf(const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Sprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Snprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Dprintf(const char8_t* EA_RESTRICT pFormat, ...);
+
+	EASTDC_API int Cprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Printf(const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Sprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Snprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...);
+
+	EASTDC_API int Cprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Printf(const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Sprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Snprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		EASTDC_API int Cprintf(WriteFunctionW pWriteFunction, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, ...);
+		EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, ...);
+		EASTDC_API int Printf(const wchar_t* EA_RESTRICT pFormat, ...);
+		EASTDC_API int Sprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, ...);
+		EASTDC_API int Snprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, ...);
+	#endif
+
+	// StringPrintf
+	// Writes directly into a C++ string object. e.g. StringPrintf(strObject, "%d", 37);
+	// Will be faster than eastl::string::printf() as long as the supplied string has a capacity that
+	// doesn't need to keep increasing while being written into.
+	template <typename String> 
+	int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+		int result = Vcprintf(WriteFunctionString<String>, &s, pFormat, arguments);
+		va_end(arguments);
+		return result;
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// Deprecated functionality
+	///////////////////////////////////////////////////////////////////////////
+
+	// There was an update which caused the WriteFunction to have a new parameter (WriteFunctionState),
+	// but there may be old user code which uses the original WriteFunction which didn't take this
+	// parameter. So we implement support for the old WriteFunction type for the time being.
+	typedef int (*WriteFunction8Old) (const char8_t*  EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8);
+	typedef int (*WriteFunction16Old)(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16);
+
+	EASTDC_API int Cprintf(WriteFunction8Old   pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Cprintf(WriteFunction16Old  pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int Vcprintf(WriteFunction8Old  pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int Vcprintf(WriteFunction16Old pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+
+	#if EASTDC_VSNPRINTF8_ENABLED
+		EASTDC_API int Vsnprintf8(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vsnprintf16(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+		EASTDC_API int Vsnprintf32(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+		#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+			EASTDC_API int VsnprintfW(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
+		#endif
+	#endif
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 125 - 0
include/EAStdC/EASprintfOrdered.h

@@ -0,0 +1,125 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// Ordered printf
+//
+// The module provides ordered versions of the printf family of functions:
+//   int OCprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, ...);
+//   int OFprintf(FILE* pFile, const char_t* pFormat, ...);
+//   int OPrintf(cconst char_t* pFormat, ...);
+//   int OSprintf(char_t* pDestination, const char_t* pFormat, ...);
+//   int OSnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+//
+//   int OVcprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, va_list arguments);
+//   int OVfprintf(FILE* pFile, const char_t* pFormat, va_list arguments);
+//   int OVprintf(const char_t* pFormat, va_list arguments);
+//   int OVsprintf(char_t* pDestination, const char_t* pFormat, va_list arguments);
+//   int OVsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, va_list arguments);
+//   int OVscprintf(const char* pFormat, va_list arguments);
+//
+// Ordered printf is like printf except it works on "ordered" printf specifiers.
+// This means that instead of using "%4d %f" we give an order to the arguments via 
+// an index and colon, as with "%1:4d %2:f". The point, however, is that you can 
+// reorder the indexes, as with "%2:f %1:4d". This is particularly useful for 
+// formatted string localization, as different locales use subjects and verbs
+// in different orders.
+//
+// User indexes can start at either 0 or 1. Oprintf detects which is in use as 
+// it goes. So the following have identical effect:
+//     OPrintf("%1:s" %0:f", 3.0, "hello");
+//     OPrintf("%2:s" %1:f", 3.0, "hello");
+//
+// User indexes must be contiguous and the behaviour of Oprintf is undefined
+// if the ordering is non-contiguous. There are debug checks to validate 
+// contiguity, so debug tests should catch mistakes. The following is an 
+// example of non-contiguous ordering, as it is missing a "3:" format:
+//     OPrintf("%1:d" %0:d %3:d", 17, 18, 19, 20);
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASPRINTFORDERED_H
+#define EASTDC_EASPRINTFORDERED_H
+
+
+#include <EAStdC/EASprintf.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// Printf
+	///
+	/// See EASprintf.h for documentation on the Printf family.
+	/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
+	///
+	EASTDC_API int OCprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OPrintf(const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, ...);
+
+	EASTDC_API int OCprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OPrintf(const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...);
+
+	EASTDC_API int OCprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OPrintf(const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...);
+	EASTDC_API int OSnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...);
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// OVprintf
+	///
+	/// See EASprintf.h for documentation on the Vprintf family.
+	/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
+	///
+	EASTDC_API int OVcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVscprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+
+	EASTDC_API int OVcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
+
+	EASTDC_API int OVcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+	EASTDC_API int OVscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 51 - 0
include/EAStdC/EAStdC.h

@@ -0,0 +1,51 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+
+
+#ifndef EASTDC_EASTDC_H
+#define EASTDC_EASTDC_H
+
+
+namespace EA
+{
+	namespace StdC
+	{
+		/// Does any startup initialization that EAStdC might need. Usually this
+		/// means allocating structures that can't otherwise be automatically 
+		/// auto-initialized (e.g. due to thread safety problems).
+		/// Memory may be allocated via EASTDC_NEW within this function. 
+		EASTDC_API void Init();
+
+		/// Undoes any initialization that Init did.
+		/// Memory may be freed via EASTDC_DELETE within this function.
+		EASTDC_API void Shutdown();
+
+
+
+		/// SetAssertionsEnabled
+		/// If enabled then debug builds execute EA_ASSERT and EA_FAIL statements
+		/// for serious failures that are otherwise silent. For example, the C strtoul
+		/// function silently fails by default according the the C99 language standard.
+		///
+		/// The assertions this applies to are assertions that check user parameters 
+		/// and thus detect user bugs, in particular those that could otherwise go 
+		/// silently undected. This doesn't apply to assertions that are internal
+		/// sanity checks. 
+		///
+		/// By default assertions are disabled (for compatibility with C99 behavior), 
+		/// but they also require EA_ASSERT to be enabled for the given build.
+		EASTDC_API void SetAssertionsEnabled(bool enabled);
+
+		/// GetAssertionsEnabled
+		/// Returns whether assertions are enabled, as described in SetAssertionsEnabled.
+		EASTDC_API bool GetAssertionsEnabled();
+	}
+}
+
+#endif // Header include guard
+
+

+ 866 - 0
include/EAStdC/EAStopwatch.h

@@ -0,0 +1,866 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Implements a stopwatch-style timer. This is useful for both benchmarking
+// and for implementing runtime timing.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASTOPWATCH_H
+#define EASTDC_EASTOPWATCH_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASTDC_STOPWATCH_USE_GETTIMEOFDAY / EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+//
+// Defined as 0 or 1.
+// Identifies if we are using clock_gettime or gettimeofday for GetCPUCycle.
+//
+#if defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
+	#include <time.h>
+
+	// If using Dinkumware's standard library instead of GlibC and similar, then use clock_gettime (which is theoretically better).
+	// To try to do: Use clock_gettime whenever possible in preference over gettimeofday.
+	#if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC))
+		#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY  0
+		#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 1
+	#else
+		#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY  1
+		#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 0
+	#endif
+#else
+	#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY  0
+	#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 0
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+/// EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()
+///
+/// Use this macro in a module within a Win32 DLL or EXE to disable CPU
+/// calibration on startup, which takes about half a second. Doing this causes
+/// GetCPUFrequency() and CPU cycle based stopwatches to return placeholder
+/// timing, although GetCPUCycle() and stopwatch cycle based measurements are
+/// unaffected.
+///
+/// The initializer declared by this macro must be called before the default
+/// stopwatch initializer executes. In VC++, the easiest way to do this is to
+/// declare #pragma init_seg(compiler) before the auto-initializer. For this
+/// reason, it is best to place this initializer in its own .cpp file.
+///
+/// Example usage:
+///     #if defined(_MSC_VER)
+///         #pragma warning(disable: 4074)
+///         #pragma init_seg(compiler)
+///     #endif
+/// 
+///     EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()
+///
+///     int main(int argc, char** argv)
+///     {
+///         . . .
+///     }
+///
+#ifndef EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION
+	// To consider: provide a means for the user to #define the CPU frequency to use 
+	// before using this macro. Otherwise the user has to accept the default (0) or 
+	// would have to manually call EAStdCStopwatchDisableCPUCalibration instead of
+	// using this macro.
+
+	#if defined(__GNUC__)
+		#define EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()                                  \
+			EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency = 0);\
+																							\
+			namespace EA                                                                    \
+			{                                                                               \
+				namespace StdC                                                              \
+				{                                                                           \
+					void AutoStopwatchDisableCPUCalibration() __attribute__((constructor)); \
+					void AutoStopwatchDisableCPUCalibration()                               \
+					{                                                                       \
+						EAStdCStopwatchDisableCPUCalibration(0);                            \
+					}                                                                       \
+				}                                                                           \
+			}
+	#else
+		#define EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()                                  \
+			EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency = 0);\
+																							\
+			namespace EA                                                                    \
+			{                                                                               \
+				namespace StdC                                                              \
+				{                                                                           \
+					struct AutoStopwatchDisableCPUCalibration                               \
+					{                                                                       \
+						AutoStopwatchDisableCPUCalibration()                                \
+							{ EAStdCStopwatchDisableCPUCalibration(0); }                    \
+					} gAutoStopwatchDisableCPUCalibration;                                  \
+				}                                                                           \
+			}
+	#endif
+
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+	//////////////////////////////////////////////////////////////////////////////
+	/// Stopwatch 
+	///
+	/// The Stopwatch class acts very much like a hand-held stopwatch. You can start 
+	/// it, stop it, start it again, reset it, and get the elapsed time. Elapsed time 
+	/// acts just like a stopwatch -- if the stopwatch is running, elapsed time is the 
+	/// current stopwatch time; if the stopwatch is stopped, elapsed time is the 
+	/// time up till the stop.
+	///
+	/// Important things to know about Stopwatch:
+	///     - There is a distinction between stopwatch cycles and CPU cycles. While it
+	///        may be the case that the stopwatch uses a CPU cycle counter as its basis,
+	///        this also may not be the case. In fact, using a CPU cycles counter 
+	///        as the basis for a stopwatch is often a dangerous thing to do because 
+	///        processors these days will sometimes switch frequencies on the fly.
+	///
+	///     - You won't get very accurate timings if you use a millisecond stopwatch
+	///        repeatedly to time tiny sections of code that take only nanoseconds.
+	///
+	///     - You can start and stop a stopwatch at various times and it will do the 
+	///        right thing, which is sum up the times during which it was running.
+	///
+	///     - Timing CPU cycles (clock ticks) accurately can be hard if you are
+	///        trying to time very small pieces of code.  
+	///
+	///     - You don't have to stop a stopwatch that is running; it doesn't take 
+	///        up CPU time to be running. It is not an error to not stop a stopwatch.
+	///
+	///     - You don't have to worry about multi-processing issues, even when 
+	///        measuring clock ticks. If you start a stopwatch while your thread is 
+	///        running on one CPU, you can stop it while running on a different CPU.
+	///
+	///     - You can call GetElapsedTime while the stopwatch is running and it will
+	///        act as you expect.
+	///
+	///     - The construction and destruction instances of Stopwatch is fast; don't
+	///        worry about it. Similarly, a Stopwatch instance is small in size.
+	///
+	/// Example usage:
+	///     Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+	///     stopwatch.Start();
+	///     DoSomethingThatYouWantToMeasure();
+	///     printf("Time to do something: %u.\n", stopwatch.GetElapsedTime());
+	///
+	/// Example usage:
+	///     Stopwatch stopwatch(Stopwatch::kUnitsCycles);
+	///     stopwatch.Start();
+	///     DoSomethingSmall();
+	///     printf("Time to do something small: %u.\n", stopwatch.GetElapsedTime());
+	///     stopwatch.Restart();
+	///     DoSomethingElseSmall();
+	///     printf("Time to do something else small: %d.\n", stopwatch.GetElapsedTime());
+	///
+	/// Example usage:
+	///     Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+	///     stopwatch.Start();
+	///     stopwatch.SetElapsedTime(100);
+	///     printf("This should print out 100 (or maybe 101): %u.\n", stopwatch.GetElapsedTime());
+	///
+	/// Don't do this!:
+	/// It seems that quite often people unfamiliar with a C++ stopwatch tend to revert away from
+	/// using the stopwatch as it was designed and try to do timings manually, like shown below.
+	/// The code below is more complicated and is less precise, as the high internal resolution 
+	/// of the stopwatch is lost.
+	///     Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
+	///     stopwatch.Start();
+	///     uint64_t timeStart = stopwatch.GetElapsedTime();
+	///     DoSomething();
+	///     uint64_t timeElapsed = stopwatch.GetElapsedTime() - time;
+	///
+	/// Todo: Change all floats to doubles due to precision issues, especially when the Stopwatch
+	/// is improperly used. Probably retain as float for PS2 platform.
+	///
+	class EASTDC_API Stopwatch
+	{
+	public:
+		/// Units
+		/// Defines common timing units plus a user-definable set of units.
+		enum Units
+		{
+			kUnitsCycles       =    0,  /// Stopwatch clock ticks. May or may not match CPU clock ticks 1:1, depending on your hardware and operating system. Some CPUs' low level cycle count instruction counts every 16 cycles instead of every cycle. 
+			kUnitsCPUCycles    =    1,  /// CPU clock ticks (or similar equivalent for the platform). Not recommended for use in shipping softare as many systems alter their CPU frequencies at runtime.
+			kUnitsNanoseconds  =    2,  /// For a 1GHz processor, 1 nanosecond is the same as 1 clock tick.
+			kUnitsMicroseconds =    3,  /// For a 1GHz processor, 1 microsecond is the same as 1,000 clock ticks.
+			kUnitsMilliseconds =    4,  /// For a 1GHz processor, 1 millisecond is the same as 1,000,000 clock ticks.
+			kUnitsSeconds      =    5,  /// For a 1GHz processor, 1 second is the same as 1,000,000,000 clock ticks.
+			kUnitsMinutes      =    6,  /// For a 1GHz processor, 1 minute is the same as 60,000,000,000 clock ticks.
+			kUnitsUserDefined  = 1000   /// User defined units, such as animation frames, video vertical retrace, etc.
+		};
+
+	public:
+		/// Stopwatch
+		/// Constructor for Stopwatch, allows user to specify units. 
+		/// If units are kUnitsUserDefined,  you'll need to either subclass 
+		/// Stopwatch and implement GetUserDefinedStopwatchCycle or call 
+		/// SetUserDefinedUnitsToStopwatchCyclesRatio in order to allow it 
+		/// to know how to convert units.
+		explicit Stopwatch(int nUnits = kUnitsCycles, bool bStartImmediately = false);
+
+		/// Stopwatch
+		/// Copy constructor.
+		Stopwatch(const Stopwatch& stopwatch);
+
+		/// ~Stopwatch
+		/// Destructor.
+	   ~Stopwatch();
+
+		/// operator=
+		/// Assignment operator.
+		Stopwatch& operator=(const Stopwatch& stopwatch);
+
+		/// GetUnits
+		/// Gets the current units. Returns one of enum Units or kUnitsUserDefined+.
+		int GetUnits() const;
+
+		/// SetUnits
+		/// Sets the current units. One of enum Units or kUnitsUserDefined+.
+		void SetUnits(int nUnits);
+
+		/// Start
+		/// Starts the stopwatch. Continues where it was last stopped. 
+		/// Does nothing if the stopwatch is already started.
+		void Start();
+
+		/// Stop
+		/// Stops the stopwatch it it was running and retaines the elasped time.
+		void Stop();
+
+		/// Reset
+		/// Stops the stopwatch if it was running and clears the elapsed time.
+		void Reset();
+
+		/// Restart
+		/// Clears the elapsed time and starts the stopwatch if it was not 
+		/// already running. Has the same effect as Reset(), Start().
+		void Restart();
+
+		/// IsRunning
+		/// Returns true if the stopwatch is running.
+		bool IsRunning() const;
+
+		/// GetElapsedTime
+		/// Gets the elapsed time, which properly takes into account any 
+		/// intervening stops and starts. Works properly whether the stopwatch 
+		/// is running or not.
+		uint64_t GetElapsedTime() const;
+
+		/// SetElapsedTime
+		/// Allows you to set the elapsed time. Erases whatever is current. 
+		/// Works properly whether the stopwatch is running or not.
+		void SetElapsedTime(uint64_t nElapsedTime);
+
+		/// GetElapsedTimeFloat
+		/// Float version, which is useful for counting fractions of 
+		/// seconds or possibly milliseconds.
+		float GetElapsedTimeFloat() const;
+
+		/// SetElapsedTimeFloat
+		/// Allows you to set the elapsed time. Erases whatever is current. 
+		/// Works properly whether the stopwatch is running or not.
+		void SetElapsedTimeFloat(float fElapsedTime);
+
+		/// SetCyclesPerUnit
+		/// Allows the user to manually override the frequency of the 
+		/// timer. 
+		void SetCyclesPerUnit(float fCyclesPerUnit);
+
+		/// GetCyclesPerUnit
+		/// Returns the value number of cycles per unit. If the user 
+		/// set a manual value via SetCyclesPerUnit, this function returns 
+		/// that value.
+		float GetCyclesPerUnit() const;
+
+		/// GetStopwatchCycle
+		/// Gets the current stopwatch cycle on the current machine.
+		/// Note that a stopwatch cycle may or may not be the same thing
+		/// as a CPU cycle. We provide the distinction between stopwatch
+		/// cycles and CPU cycles in order to accommodate  platforms (e.g.
+		/// desktop platforms) in which CPU cycle counting is unreliable.
+		static uint64_t GetStopwatchCycle();
+
+		/// GetStopwatchFrequency
+		/// Note that the stopwatch frequency may or may not be the same thing
+		/// as the CPU frequency. We provide the distinction between stopwatch
+		/// cycles and CPU cycles in order to accommodate platforms (e.g.
+		/// desktop platforms) in which CPU cycle counting is unreliable.
+		static uint64_t GetStopwatchFrequency();
+
+		/// GetUnitsPerStopwatchCycle
+		/// Returns the number of specified units per cycle.  
+		/// If the unit is seconds, the return value would be the frequency of 
+		/// the stopwatch timer and thus be the same value as returned by
+		/// GetStopwatchFrequency().
+		///
+		/// Example usage:
+		///     const float    fFrequencyInverse = Stopwatch::GetUnitsPerStopwatchCycle(Stopwatch::kUnitsSeconds);
+		///     const uint64_t nStartCycle = Stopwatch::GetStopwatchCycle();
+		///     <Do something>
+		///     const uint64_t nElapsedTime = (Stopwatch::GetStopwatchCycle() - nStartStopwatchCycle) * fFrequencyInverse;
+		///
+		static float GetUnitsPerStopwatchCycle(Units units);
+
+		/// GetCPUCycle
+		/// Gets the current CPU-based timer cycle on the current processor 
+		/// (if in a multiprocessor system). Note that this doesn't necessarily
+		/// get the actual machine CPU clock cycle; rather it returns the 
+		/// CPU-based timer cycle. One some platforms the CPU-based timer is
+		/// a 1:1 relation to the CPU clock, while on others it is some multiple
+		/// of it.
+		/// Note that on some systems you can't rely on kUnitsCycles being consistent 
+		/// at runtime, especially on x86 PCs with their multiple desynchronized CPUs 
+		/// and variable runtime clock speed.
+		static uint64_t GetCPUCycle();
+
+		/// GetCPUFrequency
+		/// Gets the frequency of the CPU-based timer. Note that this doesn't 
+		/// necessarily get the actual machine CPU clock frequency; rather it returns  
+		/// the CPU-based timer frequency. One some platforms the CPU-based timer is
+		/// a 1:1 relation to the CPU clock, while on others it is some multiple of it.
+		static uint64_t GetCPUFrequency(); 
+
+		/// GetUnitsPerCPUCycle
+		/// Returns the number of CPU cycles per the given unit.  
+		/// If the unit is seconds, the return value would be the frequency 
+		/// of the CPU-based timer.
+		///
+		/// Example usage:
+		///     const float    fFrequencyInverse = Stopwatch::GetUnitsPerCPUCycle(Stopwatch::kUnitsSeconds);
+		///     const uint64_t nStartCycle = Stopwatch::GetCPUCycle();
+		///     <Do something>
+		///     const uint64_t nElapsedTime = (Stopwatch::GetCPUCycle() - nStartStopwatchCycle) * fFrequencyInverse;
+		///
+		static float GetUnitsPerCPUCycle(Units units);
+
+	protected:
+		uint64_t    mnStartTime;                            /// Start time; always in cycles.
+		uint64_t    mnTotalElapsedTime;                     /// Elapsed time; always in cycles.
+		int         mnUnits;                                /// Stopwatch units. One of enum Units or kUnitsUserDefined+.
+		float       mfStopwatchCyclesToUnitsCoefficient;    /// Coefficient is defined seconds per cycle (assuming you want to measure seconds). This is the inverse of the frequency, done this way for speed. Time passed = cycle count * coefficient.                 
+
+	}; // class Stopwatch
+
+
+
+	//////////////////////////////////////////////////////////////////////////////
+	/// LimitStopwatch
+	///
+	/// Implements a stopwatch whose purpose is to tell whether a given amount of 
+	/// time has passed. This has the same effect as using a Stopwatch and checking
+	/// that its elapsed time >= its start time plus delta time. However, it is more
+	/// efficient to have a LimitStopwatch because it can precalculate the end 
+	/// condition and its IsTimeUp function merely compares the current tick with
+	/// the end tick and thus no multiplication, division or other calculations need occur.
+	///
+	/// Example usage:
+	///     LimitStopwatch limitStopwatch(Stopwatch::kUnitsMilliseconds, 1000, true);
+	///     while(!limitStopwatch.IsTimeUp())
+	///         printf("waiting\n");
+	///
+	/// Example usage of re-using a LimitStopwatch:
+	///     LimitStopwatch limitStopwatch(Stopwatch::kUnitsSeconds);
+	///     limitStopwatch.SetTimeLimit(10, true);
+	///     while(!limitStopwatch.IsTimeUp() 
+	///         { }
+	///     limitStopwatch.SetTimeLimit(10, true);
+	///     while(!limitStopwatch.IsTimeUp()
+	///         { }
+	///
+	class EASTDC_API LimitStopwatch : public Stopwatch
+	{
+	public:
+		/// LimitStopwatch
+		/// Constructor
+		LimitStopwatch(int nUnits = kUnitsCycles, uint64_t nLimit = 0, bool bStartImmediately = false);
+
+		/// SetTimeLimit
+		/// Sets the time limit and lets you start the stopwatch at the same time.
+		void SetTimeLimit(uint64_t nLimit, bool bStartImmediately = false); 
+
+		/// IsTimeUp
+		/// Returns true if the limit has been reached. Highly efficient.
+		bool IsTimeUp() const;
+
+		/// GetTimeRemaining
+		/// More expensive than IsTimeUp, as it returns a value.
+		int64_t GetTimeRemaining() const;
+
+		/// GetTimeRemainingFloat
+		/// More expensive than IsTimeUp, as it returns a value.
+		float GetTimeRemainingFloat() const;
+
+	protected:
+		uint64_t mnEndTime;     /// The precomputed end time used by limit timing functions.
+
+	}; // class LimitStopwatch
+
+} // namespace StdC
+
+} // namespace EA
+
+
+
+#if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PROCESSOR_ARM) || defined(EA_PLATFORM_WINDOWS_PHONE))
+	#include <windows.h>
+
+	inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		LARGE_INTEGER perfCounter;
+		QueryPerformanceCounter(&perfCounter);
+		return static_cast<uint64_t>(perfCounter.QuadPart);
+	}
+
+
+// x86-64/VC++
+// EA_PROCESSOR_X86 is the processor for standard Windows PCs.
+#elif defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC)
+	#pragma warning(push, 0)
+	#include <math.h>       // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
+	#if (_MSC_VER >= 1500)  // if VS2008 or later... Earlier versions of intrin.h are acknowledged as broken by Microsoft.
+		#include <intrin.h>
+	#else
+		extern "C" unsigned __int64 __rdtsc(void);
+		#pragma intrinsic(__rdtsc)
+	#endif
+	#pragma warning(pop)
+
+	#define EASTDC_CPU_FREQ_CALCULATED
+
+	__forceinline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		return __rdtsc();
+	}
+
+
+// x86/VC++
+// EA_PROCESSOR_X86 is the processor for standard Windows PCs.
+#elif defined(EA_PROCESSOR_X86) && defined(EA_ASM_STYLE_INTEL)
+	#define EASTDC_CPU_FREQ_CALCULATED
+
+	#if defined(EA_COMPILER_MSVC)
+		#if(EA_COMPILER_VERSION >= 1300)  // VC7.0 or later
+			__forceinline
+		#else
+			__declspec(naked) inline // A problem with __declsped(naked) under VC6 and 
+		#endif                       // earlier is that it makes a function non-inlinable.
+	#else
+		inline
+	#endif
+
+	uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		// Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements and 
+		// need this serialization to prevent instructions from effectively executing after rdtsc, then use asm cpuid or use rdtscp (newer CPUs or XBox One platform).
+		#if !defined(EA_COMPILER_MSVC) || (EA_COMPILER_VERSION >= 1200)
+			__asm rdtsc // Read the CPU time stamp counter into edx:eax.
+		#else
+			__asm __emit 0x0f // 0x0f31 -> rdtsc for older compilers.
+			__asm __emit 0x31
+		#endif
+		#if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION <= 1200)
+			__asm ret // Problem: need to deal with stdcall vs. cdecl calling convention.
+		#endif
+	}
+
+
+
+// Apple OSs
+#elif defined(EA_PLATFORM_APPLE)
+	#include <mach/mach_time.h>
+	inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+	   return mach_absolute_time();
+	}
+
+
+#elif defined(EA_PLATFORM_SONY)
+	// According to Sony, there is a possible ~100 clock tick discrepancy between cores.
+	// Thus GetCPUCycle values could go "back in time" if called on two cores, one very soon
+	// after the other. https://ps4.scedev.net/forums/thread/18192/
+
+	// sceKernelGetProcessTimeCounter is a userland function in a library and thus occurs
+	// a small amount of overhead.  Our measurements show the overhead is ~6 cycles
+	// compared to the previously implementation which utilized rdtsc directly. 
+
+	// Previous implementation for reference:
+	//
+	// inline
+	// uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	// {
+	//     // Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements  
+	//     // and need this serialization to prevent instructions from effectively executing after rdtsc then use rdtscp.
+	//     // Note that as of this writing clang doesn't support __builtin_ia32_rdtsc().
+	//     #if EA_COMPILER_HAS_BUILTIN(__builtin_ia32_rdtsc)
+	//         return __builtin_ia32_rdtsc();
+	//     #else
+	//         uint32_t edxHigh32;
+	//         uint64_t raxLow32;
+
+	//         asm volatile("rdtsc" : "=a" (raxLow32), "=d" (edxHigh32)); // rdtsc clears the high 32 bits of rax and so we can use result directly here.
+	//         return ((uint64_t)edxHigh32 << 32) | raxLow32;
+	//     #endif
+	// }
+
+	#include <kernel.h>
+	inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		return sceKernelGetProcessTimeCounter();
+	}
+
+
+// x86,x86-64/GNUC or Clang
+#elif (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG))
+	#define EASTDC_CPU_FREQ_CALCULATED
+
+	inline
+	uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		// Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements and 
+		// need this serialization to prevent instructions from effectively executing after rdtsc, then use asm cpuid or use rdtscp (newer CPUs).
+		#if EA_COMPILER_HAS_BUILTIN(__builtin_ia32_rdtsc)
+			return __builtin_ia32_rdtsc();
+		#else
+			uint32_t eaxLow32, edxHigh32;
+			uint64_t result;
+
+			asm volatile("rdtsc" : "=a" (eaxLow32), "=d" (edxHigh32));
+			result = ((uint64_t)edxHigh32 << 32) | ((uint64_t)eaxLow32);
+
+			return result;
+		#endif
+	}
+
+// PowerPC64 and GCC
+#elif defined(EA_PROCESSOR_POWERPC) && (EA_PLATFORM_WORD_SIZE == 8) && defined(EA_COMPILER_GNUC)
+	#define EASTDC_CPU_FREQ_CALCULATED
+
+	// This is a generic GCC version for 64 bit PowerPC machines.
+	inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		uint64_t nTimeBase;
+		__asm__ __volatile__ ("mftb %0" : "=r" (nTimeBase));
+		return nTimeBase;
+	}
+
+// PowerPC32 and GCC
+#elif defined(EA_PROCESSOR_POWERPC) && (EA_PLATFORM_WORD_SIZE == 4) && defined(EA_COMPILER_GNUC)
+	#define EASTDC_CPU_FREQ_CALCULATED
+
+	// This is a generic GCC version for 32 bit PowerPC machines.
+	// We need to loop because we are reading two registers and the high one might change.
+	inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+	{
+		uint32_t high1, high2, low;
+
+		__asm__ __volatile__ 
+		(
+			"1: mftbu %0\n"
+			   "mftb  %1\n"
+			   "mftbu %2\n"
+			   "cmpw  %3,%4\n"
+			   "bne-  1b\n"
+			 : "=r" (high1), "=r" (low), "=r" (high2)
+			 : "0" (high1), "2" (high2)
+		);
+		
+		return (uint64_t(high1) << 32) | low;
+	}
+#elif defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
+	#include <time.h>
+
+	#if   EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+		// http://linux.die.net/man/3/clock_gettime
+		#include <errno.h>
+
+		inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+		{
+			timespec ts;
+			int      result = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+			if(result == EINVAL 
+				)
+				result = clock_gettime(CLOCK_REALTIME, &ts);
+
+			const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
+			return nNanoseconds;
+		}
+
+	#else // EASTDC_STOPWATCH_USE_GETTIMEOFDAY
+		#include <sys/time.h>
+		#include <unistd.h>
+
+		inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
+		{
+			struct timeval tv;
+			gettimeofday(&tv, NULL);
+			const uint64_t nMicroseconds = (uint64_t)tv.tv_usec + ((uint64_t)tv.tv_sec * 1000000);
+			return nMicroseconds;
+		}
+	#endif
+
+#else
+
+	#error EA::StdC::Stopwatch::GetCPUCycle not implemented.
+
+#endif
+
+
+
+
+
+
+#if defined(EA_PLATFORM_MICROSOFT) && !EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+	// PC computers (x86 and otherwise) have the property whereby they change 
+	// CPU frequencies on the fly. As such, you cannot safely use GetCPUCycle
+	// on these computers to tell you how much time has passed. This is not the
+	// case with XBOX because it is specifically designed not to do this.
+	// You can disable usage of QueryPerformanceCounter below by defining
+	// EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE as 1.
+	// hardcode prototype here so we don't pull in <windows.h>
+	extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(_Out_ union _LARGE_INTEGER *lpPerformanceCount);
+
+	inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+	{
+		 uint64_t nReturnValue;
+		 QueryPerformanceCounter((union _LARGE_INTEGER*)&nReturnValue);
+		 return nReturnValue;
+	}
+
+
+// Apple OSs
+#elif defined(__APPLE__)
+   #include <mach/mach_time.h>
+   inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+   {
+	  return mach_absolute_time();
+   }
+
+
+#elif defined(EA_PLATFORM_SONY)
+	// See the discussion above in GetCPUCycle for EA_PLATFORM_SONY.
+	// Previous implementation for reference:
+	//
+	// inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+	// {
+	//     uint32_t eaxLow32, edxHigh32;
+	//     uint64_t result;
+	//
+	//     asm volatile("rdtsc" : "=a" (eaxLow32), "=d" (edxHigh32));
+	//     result = ((uint64_t)edxHigh32 << 32) | ((uint64_t)eaxLow32);
+	//
+	//     return result;
+	// }
+
+	#include <kernel.h>
+	inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+	{
+		return sceKernelGetProcessTimeCounter();
+	}
+
+
+#elif defined(EA_PLATFORM_POSIX)
+	#include <time.h>
+
+	#if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+		// http://linux.die.net/man/3/clock_gettime
+		#include <errno.h>
+
+		inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+		{
+			timespec ts;
+			int      result = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+			if(result == EINVAL 
+				)
+				result = clock_gettime(CLOCK_REALTIME, &ts);
+
+			const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
+			return nNanoseconds;
+		}
+
+	#elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
+		#include <sys/time.h>
+		#include <unistd.h>
+
+		inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+		{
+			struct timeval tv;
+			gettimeofday(&tv, NULL);
+			const uint64_t nMicroseconds = (uint64_t)tv.tv_usec + ((uint64_t)tv.tv_sec * 1000000);
+			return nMicroseconds;
+		}
+	#else
+		inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+		{
+			return GetCPUCycle();
+		}
+	#endif
+
+#else
+	// Other supported processors have fixed-frequency CPUs and thus can 
+	// directly use the GetCPUCycle functionality for maximum precision
+	// and speed.
+	inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
+	{
+		return GetCPUCycle();
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inline functions
+//
+// We choose to make these functions inline because they are so small that 
+// making them inline would likely have no effect on code size but would 
+// significantly improve speed and possibly instruction cache friendliness.
+// Also, the PS2 compiler is picky about ordering of inline functions, so
+// the StopWatch class inlines are placed after the GetCPUCycle functions.
+///////////////////////////////////////////////////////////////////////////////
+
+inline
+EA::StdC::Stopwatch::Stopwatch(const Stopwatch& stopwatch)
+{
+	mnUnits = stopwatch.mnUnits;
+	mnStartTime = stopwatch.mnStartTime;
+	mnTotalElapsedTime = stopwatch.mnTotalElapsedTime;
+	mfStopwatchCyclesToUnitsCoefficient = stopwatch.mfStopwatchCyclesToUnitsCoefficient;
+}
+
+
+inline
+EA::StdC::Stopwatch::~Stopwatch()
+{
+	// Empty
+}
+
+
+inline
+EA::StdC::Stopwatch& EA::StdC::Stopwatch::operator =(const Stopwatch& stopwatch)
+{
+	mnUnits = stopwatch.mnUnits;
+	mnStartTime = stopwatch.mnStartTime;
+	mnTotalElapsedTime = stopwatch.mnTotalElapsedTime;
+	mfStopwatchCyclesToUnitsCoefficient = stopwatch.mfStopwatchCyclesToUnitsCoefficient;
+	return *this;
+}
+
+
+inline
+int EA::StdC::Stopwatch::GetUnits() const
+{
+	return mnUnits;
+}
+
+
+inline
+void EA::StdC::Stopwatch::Start()
+{
+	if(!mnStartTime) // If not already started...
+	{
+		if(mnUnits == kUnitsCPUCycles)
+			mnStartTime = GetCPUCycle();
+		else
+			mnStartTime = GetStopwatchCycle();
+
+	}
+}
+
+
+inline
+void EA::StdC::Stopwatch::Reset()
+{
+	mnStartTime = 0;
+	mnTotalElapsedTime = 0;
+}
+
+
+inline
+void EA::StdC::Stopwatch::Restart()
+{
+	mnStartTime = 0;
+	mnTotalElapsedTime = 0;
+	Start();
+}
+
+
+inline
+bool EA::StdC::Stopwatch::IsRunning() const
+{
+	return (mnStartTime != 0);
+}
+
+
+inline
+void EA::StdC::Stopwatch::SetCyclesPerUnit(float fCyclesPerUnit)
+{
+	mfStopwatchCyclesToUnitsCoefficient = fCyclesPerUnit;
+}
+
+
+inline
+float EA::StdC::Stopwatch::GetCyclesPerUnit() const
+{
+	return mfStopwatchCyclesToUnitsCoefficient;
+}
+
+
+
+
+inline
+EA::StdC::LimitStopwatch::LimitStopwatch(int nUnits, uint64_t nLimit, bool bStartImmediately) 
+	: Stopwatch(nUnits, false)
+{
+	SetTimeLimit(nLimit, bStartImmediately);
+}
+
+
+inline
+bool EA::StdC::LimitStopwatch::IsTimeUp() const
+{
+	const uint64_t nCurrentTime = GetStopwatchCycle();
+	return (int64_t)(mnEndTime - nCurrentTime) < 0; // We do this instead of a straight compare to deal with possible integer wraparound issues.
+}
+			
+			
+inline
+int64_t EA::StdC::LimitStopwatch::GetTimeRemaining() const
+{
+	const uint64_t nCurrentTime = GetStopwatchCycle();
+	const int64_t  nTimeRemaining = (int64_t)((int64_t)(mnEndTime - nCurrentTime) * mfStopwatchCyclesToUnitsCoefficient);
+	return nTimeRemaining;
+}
+
+
+
+#endif // header guard
+
+
+
+
+
+
+
+
+
+
+

+ 2804 - 0
include/EAStdC/EAString.h

@@ -0,0 +1,2804 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This module implements the following functions.
+// Some of these are direct analogues of the Standard C Library, whereas others
+// are extension functions that have uses not provided by the Standard C Library.
+//
+//    char_t*  Strcat(char_t* pDestination, const char_t* pSource);
+//    char_t*  Strncat(char_t* pDestination, const char_t* pSource, size_t n);
+//    size_t   Strlcat(char_t* pDestination, const char_t* pSource, size_t n);
+//    char_t*  Strcpy(char_t* pDestination, const char_t* pSource);
+//    char_t*  Strncpy(char_t* pDestination, const char_t* pSource, size_t n);
+//    size_t   Strlcpy(char_t* pDestination, const char_t* pSource, size_t n);
+//    int      Strlcpy(char16_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+//    int      Strlcpy(char8_t*  pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+//    size_t   Strlen(const char_t* pString);
+//    size_t   StrlenUTF8Decoded(const char8_t* pString);
+//    size_t   StrlenUTF8Encoded(const char16_t* pString);
+//    char_t*  Strend(const char_t* pString);
+//    size_t   Strxfrm(char_t* pDest, const char_t* pSource, size_t n);
+//    char_t*  Strdup(const char_t* pString);
+//
+//    char_t*  Strlwr(char_t* pString);
+//    char_t*  Strupr(char_t* pString);
+//    char_t*  Strmix(char_t* pDestination, const char_t* pSource, const char_t* pDelimiters);
+//    char_t*  Strchr(const char_t* pString, char_t c);
+//    char_t*  Strnchr(const char_t* pString, char_t c, size_t n);
+//    size_t   Strcspn(const char_t* pString1, const char_t* pString2);
+//    char_t*  Strpbrk(const char_t* pString1, const char_t* pString2);
+//    char_t*  Strrchr(const char_t* pString, char_t c);
+//    size_t   Strspn(const char_t* pString, const char_t* pSubString);
+//    char_t*  Strstr(const char_t* pString, const char_t* pSubString);
+//    char_t*  Stristr(const char_t* pString, const char_t* pSubString);
+//    bool     Strstart(const char_t* pString, const char_t* pPrefix);
+//    bool     Stristart(const char_t* pString, const char_t* pPrefix);
+//    bool     Strend(const char_t* pString, const char_t* pSuffix, size_t stringLength = kSizeTypeUnset);
+//    bool     Striend(const char_t* pString, const char_t* pSuffix, size_t stringLength = kSizeTypeUnset);
+//    char_t*  Strtok(char_t* pString, const char_t*  pTokens, char_t** pContext);
+//    char_t*  Strset(char_t* pString, char_t c);
+//    char_t*  Strnset(char_t* pString, char_t c, size_t n);
+//    char_t*  Strrev(char_t* pString);
+//    char_t*  Strstrip(char_t* pString)
+//    int      Strcmp(const char_t* pString1, const char_t* pString2);
+//    int      Strncmp(const char_t* pString1, const char_t* pString2, size_t n);
+//    int      Stricmp(const char_t*  pString1, const char_t* pString2);
+//    int      Strnicmp(const char_t* pString1, const char_t* pString2, size_t n);
+//    int      StrcmpAlnum(const char_t* pString1, const char_t* pString2);
+//    int      Strcoll(const char_t*  pString1, const char_t* pString2);
+//    int      Strncoll(const char_t* pString1, const char_t* pString2, size_t n);
+//    int      Stricoll(const char_t* pString1, const char_t* pString2);
+//    int      Strnicoll(const char_t* pString1, const char_t* pString1, size_t n);
+//
+//    template <typename Source, typename Dest>
+//    inline bool ConvertString(const Source& s, Dest& d);
+//
+//    template <typename Source, typename Dest>
+//    inline Dest ConvertString(const Source& s);
+//
+//    char_t*  EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char_t* buffer);
+//    char_t*  FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char_t* buffer);
+//
+//    char_t*  I32toa(int32_t nValue, char_t* pResult, int nBase);
+//    char_t*  U32toa(uint32_t nValue, char_t* pResult, int nBase);
+//    char_t*  I64toa(int64_t nValue, char_t* pBuffer, int nBase);
+//    char_t*  U64toa(uint64_t nValue, char_t* pBuffer, int nBase);
+//    double   Strtod(const char_t* pString, char_t** ppStringEnd);
+//    double   StrtodEnglish(const char_t* pString, char_t** ppStringEnd);
+//    int64_t  StrtoI64(const char_t* pString, char_t** ppStringEnd, int nBase);
+//    uint64_t StrtoU64(const char_t* pString, char_t** ppStringEnd, int nBase);
+//    int32_t  StrtoI32(const char_t* pString, char_t** ppStringEnd, int nBase);
+//    uint32_t StrtoU32(const char_t* pString, char_t** ppStringEnd, int nBase);
+//    int32_t  AtoI32(const char_t* pString);
+//    uint32_t AtoU32(const char_t* pString);
+//    int64_t  AtoI64(const char_t* pString);
+//    uint64_t AtoU64(const char_t* pString);
+//    double   Atof(const char_t* pString);
+//    double   AtofEnglish(const char_t* pString);
+//
+//    char_t*  Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+//    char_t*  FtoaEnglish(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+//
+//    size_t   ReduceFloatString(char_t* pString, size_t length);
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASTRING_H
+#define EASTDC_EASTRING_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+#include <stdlib.h>
+#include <wchar.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+// EA_WCHAR_UNIQUE is defined by recent versions of EABase. If it's not defined then 
+// we just define it to 0 here.
+#ifndef EA_WCHAR_UNIQUE
+	#define EA_WCHAR_UNIQUE 0
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+/// kSizeTypeUnset
+///
+/// Used to specify that a size_t length is not specified, and should be 
+/// determined by the function (e.g. via Strlen).
+///
+static const size_t kSizeTypeUnset = (size_t)~0;
+
+
+/// Strlen
+///
+/// Returns the length of pString, not including the terminating 0 char.
+/// This function acts the same as strlen.
+///
+EASTDC_API size_t Strlen(const char8_t*  pString);
+EASTDC_API size_t Strlen(const char16_t* pString);
+EASTDC_API size_t Strlen(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t Strlen(const wchar_t* pString);
+#endif
+
+/// StrlenUTF8Decoded
+///
+/// Returns the Unicode character length of pString, not including the terminating 0 char.
+/// For ASCII text, this function returns the same as Strlen. For text with UTF8 multi-byte
+/// sequences, this will return a value less than Strlen.
+/// This function assumes that pString represents a valid UT8-encoded string.
+///
+/// Note: this function is a duplicate of the EATextUtil.h UTF8Length(const char16_t*) function and is deprecated in favor of the EATextUtil.h version.
+///
+EASTDC_API size_t StrlenUTF8Decoded(const char8_t* pString);
+
+
+/// StrlenUTF8Encoded
+///
+/// Returns number of characters that would be in a UTF8-encoded equivalent string, 
+/// not including the terminating 0 char. For ASCII text, this function returns 
+/// the same as Strlen. For text with Unicode values > 0x7f, this function 
+/// will return a value greater that Strlen.
+///
+/// Note: this function is a duplicate of the EATextUtil.h UTF8Length(const char8_t*) function  and is deprecated in favor of the EATextUtil.h version.
+///
+EASTDC_API size_t StrlenUTF8Encoded(const char16_t* pString);
+EASTDC_API size_t StrlenUTF8Encoded(const char32_t* pString);
+
+
+/// Strend
+///
+/// Returns the end of the string, which is the same thing as (pStr + Strlen(pStr)). 
+///
+EASTDC_API char8_t*  Strend(const char8_t*  pString);
+EASTDC_API char16_t* Strend(const char16_t* pString);
+EASTDC_API char32_t* Strend(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strend(const wchar_t* pString);
+#endif
+
+/// Strcpy
+///
+/// Copies pSource to pDestination with a terminating 0 char.
+/// Returns pDestination.
+/// Considering using Strlcpy as a safe alternative to Strcat.
+/// This function acts the same as strcpy.
+///
+EASTDC_API char8_t*  Strcpy(char8_t*  pDestination, const char8_t*  pSource);
+EASTDC_API char16_t* Strcpy(char16_t* pDestination, const char16_t* pSource);
+EASTDC_API char32_t* Strcpy(char32_t* pDestination, const char32_t* pSource);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strcpy(wchar_t* pDestination, const wchar_t* pSource);
+#endif
+
+
+/// Strncpy
+///
+/// Copies the first n characters of pSource to pDestination. If the end of the 
+/// pSource string is found before n characters have been copied, pDestination 
+/// is padded with trailing 0 chars until a sum of n chars have been written to it.
+/// Note that n denotes the maximum number of characters copied from the source.
+/// 
+/// pDestination will be 0-terminated only if the length of pSource is less than n.
+/// This characteristic is a common source of software bugs, and should be taken
+/// into account as per the example code here. 
+/// Considering using Strlcpy as a safe alternative to Strncpy.
+/// This function acts the same as strncpy.
+///
+/// Example safe usage:
+///     char buffer[32];
+///     Strncpy(buffer, pSomeString, 32);
+///     buffer[31] = 0;
+///
+EASTDC_API char8_t*  Strncpy(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+EASTDC_API char16_t* Strncpy(char16_t* pDestination, const char16_t* pSource, size_t n);
+EASTDC_API char32_t* Strncpy(char32_t* pDestination, const char32_t* pSource, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strncpy(wchar_t* pDestination, const wchar_t* pSource, size_t n);
+#endif
+
+
+/// StringnCopy
+///
+/// This function is identical to the rwstdc package StringnCopy function and is 
+/// provided for compatibility with it. This equivalence extends to any unintended
+/// bugs that may be in the StringnCopy function.
+/// Users are advised to use Strlcat instead of Strncat or StringnCat.
+///
+EASTDC_API char8_t*  StringnCopy(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+EASTDC_API char16_t* StringnCopy(char16_t* pDestination, const char16_t* pSource, size_t n);
+EASTDC_API char32_t* StringnCopy(char32_t* pDestination, const char32_t* pSource, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* StringnCopy(wchar_t* pDestination, const wchar_t* pSource, size_t n);
+#endif
+
+/// Strlcpy
+///
+/// The strlcpy and strlcat functions copy and concatenate strings
+/// respectively. They are designed to be safer, more consistent, 
+/// and less error prone replacements for strncpy and strncat. Unlike those
+/// functions, strlcpy and strlcat take the full capacity of the buffer (not
+/// just the length) and guarantee to 0-terminate the result (as long as
+/// nDestCapacity is larger than 0 or, in the case of strlcat, as long as there is
+/// at least one byte free in dst). Note that you should include a byte for
+/// the 0 in the input nDestCapacity. Also note that strlcpy and strlcat only operate 
+/// on true C strings. This means that for strlcpy pSource must be 0-terminated
+/// and for strlcat both pSource and pDest must be 0-terminated.
+/// 
+/// The strlcpy function copies up to (nDestCapacity - 1) characters from the 
+/// 0-terminated string pSource to pDest, 0-terminating the result.
+/// 
+/// The strlcat function appends the 0-terminated string src to the end
+/// of dst. It will append at most (nDestCapacity - strlen(dst) - 1) characters, 
+/// 0-terminating the result.
+///
+/// Users must be careful because Strlcpy and Strlcat may copy less than the required
+/// characters. Unless you check the Strlcat return value, you could be trading one
+/// kind of error (buffer overflow) with another (truncated string). In some cases
+/// the truncated string may be significant to the point of creating security errors.
+///
+/// Return value
+/// The strlcpy and strlcat functions return the strlen of the string they 
+/// tried to create. For strlcpy that means the strlen of pSource.
+/// For strlcat that means the initial strlen of pDest plus the strlen of pSource. 
+/// While this may seem somewhat confusing, it makes truncation detection simple.
+/// 
+/// Note however, that if Strlcat traverses nDestSize characters without finding
+/// a 0, the length of the string is considered to be nDestCapacity and the destination
+/// string will not be 0-terminated (since there was no space for the 0). This keeps 
+/// strlcat from running off the end of a string. In practice this should not happen 
+/// (as it means that either size is incorrect or that dst is not a proper C string). 
+/// The check exists to prevent potential security problems in incorrect code.
+///
+/// The versions that implement 8 <->16 or 8<->32 conversions assume the 8 bit text
+/// is UTF8 and they convert to and from UCS2 and UCS4 encodings respectivly. 
+///
+/// Example usage:
+///  The following code fragment illustrates the simple case:
+///      char *s, *p, buffer[BUFFERSIZE];
+///      Strlcpy(buffer, s, EAArrayCount(buffer));
+///      Strlcat(buffer, p, EAArrayCount(buffer));
+///      
+///  To detect truncation, perhaps while building a path, the following might be used:
+///      char *dir, *file, path[_MAX_PATH];
+///      if(Strlcpy(path, dir, EAArrayCount(path)) >= EAArrayCount(path))
+///          goto toolong;
+///      if(Strlcat(path, file, EAArrayCount(path)) >= EAArrayCount(path))
+///          goto toolong;
+/// 
+EASTDC_API size_t Strlcpy(char8_t*  pDestination, const char8_t*  pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcpy(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity);
+
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t Strlcpy(wchar_t* pDestination, const wchar_t* pSource, size_t nDestCapacity);
+#endif
+
+/// The following versions of Strlcpy do UTF8 to UTF16 conversions while copying.
+/// These can thus be considered as encoding conversion functions.
+/// Returns the required Strlen of pDestination.
+/// Returns -1 if the source is a malformed or illegal UTF8 string and thus that 
+/// the conversion cannot be completed.
+/// If nSourceLength is kSizeTypeUnset then the source is copied until a 0 byte is encountered.
+/// The destination needs to have nSourceLength + 1 bytes of capacity. Recall that 
+/// nSourceLength is the strlen of the source and thus doesn't include the source's
+/// own trailing 0 byte.
+///
+EASTDC_API int Strlcpy(char16_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+EASTDC_API int Strlcpy(char8_t*  pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+
+EASTDC_API int Strlcpy(char32_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+EASTDC_API int Strlcpy(char8_t*  pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+
+EASTDC_API int Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+EASTDC_API int Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+	EASTDC_API int Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+	EASTDC_API int Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+	EASTDC_API int Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+	EASTDC_API int Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+	EASTDC_API int Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = kSizeTypeUnset);
+#endif
+
+// To consider: Enable this for completeness with the above:
+//EASTDC_API int Strlcpy(char8_t*  pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength); // nSourceLength = kSizeTypeUnset
+//EASTDC_API int Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength); // nSourceLength = kSizeTypeUnset. We can't define the default parameter because if we did then Strlcpy would conflict with the other version of Strlcpy.
+//EASTDC_API int Strlcpy(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength); // nSourceLength = kSizeTypeUnset. We can't define the default parameter because if we did then Strlcpy would conflict with the other version of Strlcpy.
+
+/// The following versions of Strlcpy supports partial conversion of a string.  Unlike the other Strlcpy,
+/// pDest must not be nullptr.  The number of code units consumed is returned in nSourceUsed.  The number 
+/// of code units written is returned in nDestUsed.
+///
+/// If there is space available in the destination, then a null code unit is added to the end of the destination.
+///
+/// If the return value is true, then:
+///
+///		a) If nSourceUsed == nSourceLength, then the entire source buffer was copied.
+///		b) If nSourceUsed < nSourceLength, then there wasn't enough room in the destination
+///		   buffer and the routine should be called again.
+///
+/// If the return value if false, then there was an error encoding or decoding a code point. 
+/// pSource + nSourceUsed will point to the start of the code point that had issues.
+///
+/// kSizeTypeUnset can be specified as nSourceLength.  Regardless of the setting for nSourceLength,
+/// the string copy will stop after a null is found.  In that case, nSourceUsed will have the
+/// value of nSourceLength.
+///
+EASTDC_API bool Strlcpy(char8_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+EASTDC_API bool Strlcpy(char8_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+EASTDC_API bool Strlcpy(char16_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+EASTDC_API bool Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+EASTDC_API bool Strlcpy(char32_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+EASTDC_API bool Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API bool Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+	EASTDC_API bool Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+	EASTDC_API bool Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+	EASTDC_API bool Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+	EASTDC_API bool Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+	EASTDC_API bool Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed);
+#endif
+
+/// Strlcpy
+///
+/// Implements a generic STL or EASTL 8 <--> 16 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::string8  s8("hello world");
+///    eastl::string16 s16;
+///    EA::StdC::Strlcpy(s16, s8);
+///
+namespace Internal
+{
+	// We don't have visibility to EASTL for its type traits, and std <type_traits> isn't necessarily available.
+	template <typename T> struct is_pointer                    { static const bool value = false; };
+	template <typename T> struct is_pointer<T*>                { static const bool value = true;  };
+	template <typename T> struct is_pointer<T* const>          { static const bool value = true;  };
+	template <typename T> struct is_pointer<T* volatile>       { static const bool value = true;  };
+	template <typename T> struct is_pointer<T* const volatile> { static const bool value = true;  };
+
+	template<typename T>           struct is_array       { static const bool value = false; };
+	template<typename T>           struct is_array<T[]>  { static const bool value = true; };
+	template<typename T, size_t N> struct is_array<T[N]> { static const bool value = true; };
+
+
+
+	template <typename Dest, typename Source, bool isPointer>
+	struct Strlcpy1Class;
+
+	template <typename Dest, typename Source>
+	struct Strlcpy1Class<Dest, Source, false>
+	{
+		static bool Strlcpy1Impl(Dest& d, const Source& s)
+		{
+			const int requiredStrlen = EA::StdC::Strlcpy(&d[0], s.data(), 0, s.length());
+
+			if(requiredStrlen >= 0)
+			{
+				d.resize((typename Dest::size_type)requiredStrlen);
+				EA::StdC::Strlcpy(&d[0], s.data(), d.length() + 1, s.length());
+
+				return true;
+			}
+
+			d.clear();
+			return false;
+		}
+	};
+
+	template <typename T> // Specialization for the case that source and dest are equal.
+	struct Strlcpy1Class<T, T, false>
+	{
+		static bool Strlcpy1Impl(T& d, const T& s)
+			{ d = s; return true; }
+	};
+
+	template <typename Dest, typename Source> // Specialization for the case that Source is a pointer and not a class
+	struct Strlcpy1Class<Dest, Source, true>
+	{
+		static bool Strlcpy1Impl(Dest& d, const Source& pSource)
+		{
+			const size_t sourceLength   = EA::StdC::Strlen(pSource);
+			const int    requiredStrlen = EA::StdC::Strlcpy(&d[0], pSource, 0, sourceLength);
+
+			if(requiredStrlen >= 0)
+			{
+				d.resize((typename Dest::size_type)requiredStrlen);
+				EA::StdC::Strlcpy(&d[0], pSource, d.length() + 1, sourceLength);
+
+				return true;
+			}
+
+			d.clear();
+			return false;
+		}
+	};
+}
+
+template <typename Dest, typename Source>
+inline bool Strlcpy(Dest& d, const Source& s)
+{
+	return Internal::Strlcpy1Class<Dest, Source, Internal::is_pointer<Source>::value || Internal::is_array<Source>::value>::Strlcpy1Impl(d, s);
+}
+
+
+/// Strlcpy
+///
+/// Implements a generic STL/EASTL 8 <--> 16 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::string16 s16;
+///    EA::StdC::Strlcpy(s16, "hello world");
+///
+namespace Internal
+{
+	template <typename Dest, typename DestCharType, typename Source>
+	struct Strlcpy2Class
+	{
+		static bool Strlcpy2Impl(Dest& d, const Source* pSource, size_t sourceLength)
+		{
+			const int requiredStrlen = EA::StdC::Strlcpy(&d[0], pSource, 0, sourceLength);
+
+			if(requiredStrlen >= 0)
+			{
+				d.resize((typename Dest::size_type)requiredStrlen);
+				EA::StdC::Strlcpy(&d[0], pSource, d.length() + 1, sourceLength);
+
+				return true;
+			}
+
+			d.clear();
+			return false;
+		}
+	};
+
+	template <typename Dest, typename CharType> // Specialization for the case that source and dest are equal.
+	struct Strlcpy2Class<Dest, CharType, CharType>
+	{
+		static bool Strlcpy2Impl(Dest& d, const CharType* pSource, size_t sourceLength)
+			{ d.assign(pSource, (typename Dest::size_type)sourceLength); return true; }
+	};
+}
+
+template <typename Dest, typename Source>
+inline bool Strlcpy(Dest& d, const Source* pSource, size_t sourceLength = kSizeTypeUnset)
+{
+	if(sourceLength == kSizeTypeUnset)
+		sourceLength = Strlen(pSource);
+
+	return Internal::Strlcpy2Class<Dest, typename Dest::value_type, Source>::Strlcpy2Impl(d, pSource, sourceLength);
+}
+
+
+
+/// Strlcpy
+///
+/// Implements a generic STL/EASTL 8 <--> 16 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::string8  s8("hello world");
+///    eastl::string16 s16;
+///
+///    s16 = EA::StdC::Strlcpy<eastl::string16, eastl::string8>(s8);   // 8  -> 16
+///    s8  = EA::StdC::Strlcpy<eastl::string8,  eastl::string16>(s16); // 16 ->  8
+///    s16 = EA::StdC::Strlcpy<eastl::string16, eastl::string16>(s16); // 16 -> 16  (merely returns the source)
+///    s8  = EA::StdC::Strlcpy<eastl::string8,  eastl::string8>(s8);   //  8 ->  8  (merely returns the source)
+///
+namespace Internal
+{
+	template <class Dest, class Source>
+	struct Strlcpy3Class
+	{
+		static Dest Strlcpy3Impl(const Source& s)
+		{
+			Dest d;
+
+			const int requiredStrlen = EA::StdC::Strlcpy(&d[0], s.data(), 0, s.length());
+
+			if(requiredStrlen >= 0)
+			{
+				d.resize((typename Dest::size_type)requiredStrlen);
+				EA::StdC::Strlcpy(&d[0], s.data(), d.length() + 1, s.length());
+			}
+
+			return d;
+		}
+	};
+
+	template <class T> // Specialization for the case that source and dest are equal.
+	struct Strlcpy3Class<T, T>
+	{
+		static T Strlcpy3Impl(const T& s)
+			{ return s; }
+	};
+}
+
+template <typename Dest, typename Source>
+inline Dest Strlcpy(const Source& s)
+{
+	return Internal::Strlcpy3Class<Dest, Source>::Strlcpy3Impl(s);
+}
+
+
+/// Strlcpy
+///
+/// Implements a generic STL/EASTL 8 <--> 16 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::string16 s16 = Strlcpy<eastl::string16, char8_t>("hello world");
+///
+namespace Internal
+{
+	template <typename Dest, typename DestCharType, typename Source>
+	struct Strlcpy4Class
+	{
+		static Dest Strlcpy4Impl(const Source* pSource, size_t sourceLength)
+		{
+			Dest d;
+
+			const int requiredStrlen = EA::StdC::Strlcpy(&d[0], pSource, 0, sourceLength);
+
+			if(requiredStrlen >= 0)
+			{
+				d.resize((typename Dest::size_type)requiredStrlen);
+				EA::StdC::Strlcpy(&d[0], pSource, d.length() + 1, sourceLength);
+			}
+
+			return d;
+		}
+	};
+
+	template <typename Dest, typename CharType> // Specialization for the case that source and dest are equal.
+	struct Strlcpy4Class<Dest, CharType, CharType>
+	{
+		static Dest Strlcpy4Impl(const CharType* pSource, size_t sourceLength)
+			{ return Dest(pSource, (typename Dest::size_type)sourceLength); }
+	};
+}
+
+template <typename Dest, typename Source>
+inline Dest Strlcpy(const Source* pSource, size_t sourceLength = kSizeTypeUnset)
+{
+	if(sourceLength == kSizeTypeUnset)
+		sourceLength = Strlen(pSource);
+
+	return Internal::Strlcpy4Class<Dest, typename Dest::value_type, Source>::Strlcpy4Impl(pSource, sourceLength);
+}
+
+
+
+/// Strcat
+///
+/// Appends pSource to the end of the string at pDestination.
+/// Returns pDestination.
+/// The terminating 0 char of destination is overwritten by the first 
+/// character of source, and a new null-character is appended at the 
+/// new end of the destination. The required capacity of the destination
+/// is (Strlen(pSource) + Strlen(pDestination) + 1).
+/// Considering using Strlcat as a safer alternative to Strcat.
+///
+EASTDC_API char8_t*  Strcat(char8_t*  pDestination, const char8_t*  pSource);
+EASTDC_API char16_t* Strcat(char16_t* pDestination, const char16_t* pSource);
+EASTDC_API char32_t* Strcat(char32_t* pDestination, const char32_t* pSource);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strcat(wchar_t* pDestination, const wchar_t* pSource);
+#endif
+
+
+/// Strncat
+///
+/// Appends the first n chars from pSource to pDestination and 0-terminates pDestination. 
+/// If the Strlen of pSource is less than n, only characters preceding the 0 char are copied.
+/// Considering using Strlcat as a safer alternative to Strncat.
+/// This function acts the same as strncat
+///
+EASTDC_API char8_t*  Strncat(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+EASTDC_API char16_t* Strncat(char16_t* pDestination, const char16_t* pSource, size_t n);
+EASTDC_API char32_t* Strncat(char32_t* pDestination, const char32_t* pSource, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strncat(wchar_t* pDestination, const wchar_t* pSource, size_t n);
+#endif
+
+
+/// StringnCat
+///
+/// This function is identical to the rwstdc package StringnCat function and is 
+/// provided for compatibility with it. This equivalence extends to any unintended
+/// bugs that may be in the StringnCopy function.
+/// Users are advised to use Strlcat instead of Strncat or StringnCat.
+///
+EASTDC_API char8_t*  StringnCat(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+EASTDC_API char16_t* StringnCat(char16_t* pDestination, const char16_t* pSource, size_t n);
+EASTDC_API char32_t* StringnCat(char32_t* pDestination, const char32_t* pSource, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* StringnCat(wchar_t* pDestination, const wchar_t* pSource, size_t n);
+#endif
+
+
+/// Strlcat
+///
+/// The strlcpy and strlcat functions copy and concatenate strings
+/// respectively. They are designed to be safer, more consistent, 
+/// and less error prone replacements for strncpy and strncat. Unlike those
+/// functions, strlcpy and strlcat take the full capacity of the buffer (not
+/// just the length) and guarantee to 0-terminate the result (as long as
+/// nDestCapacity is larger than 0 or, in the case of strlcat, as long as there is
+/// at least one byte free in dst). Note that you should include a byte for
+/// the 0 in the input nDestCapacity. Also note that strlcpy and strlcat only operate 
+/// on true C strings. This means that for strlcpy pSource must be 0-terminated
+/// and for strlcat both pSource and pDest must be 0-terminated.
+/// 
+/// The strlcpy function copies up to (nDestCapacity - 1) characters from the 
+/// 0-terminated string pSource to pDest, 0-terminating the result.
+/// 
+/// The strlcat function appends the 0-terminated string src to the end
+/// of dst. It will append at most (nDestCapacity - strlen(dst) - 1) characters, 
+/// 0-terminating the result.
+///
+/// Users must be careful because Strlcpy and Strlcat may copy less than the required
+/// characters. Unless you check the Strlcat return value, you could be trading one
+/// kind of error (buffer overflow) with another (truncated string). In some cases
+/// the truncated string may be significant to the point of creating security errors.
+///
+/// Return value
+/// The strlcpy and strlcat functions return the strlen of the string they 
+/// tried to create. For strlcpy that means the strlen of pSource.
+/// For strlcat that means the initial strlen of pDest plus the strlen of pSource. 
+/// While this may seem somewhat confusing, it makes truncation detection simple.
+/// 
+/// Note however, that if strlcat traverses nDestSize characters without finding
+/// a 0, the length of the string is considered to be nDestCapacity and the destination
+/// string will not be 0-terminated (since there was no space for the 0). This keeps 
+/// strlcat from running off the end of a string. In practice this should not happen 
+/// (as it means that either size is incorrect or that dst is not a proper C string). 
+/// The check exists to prevent potential security problems in incorrect code.
+///
+/// The versions that implement 8 <->16 or 8<->32 conversions assume the 8 bit text
+/// is UTF8 and they convert to and from UCS2 and UCS4 encodings respectivly. 
+///
+/// Example usage:
+///  The following code fragment illustrates the simple case:
+///      char *s, *p, buffer[BUFFERSIZE];
+///      Strlcpy(buffer, s, EAArrayCount(buffer));
+///      Strlcat(buffer, p, EAArrayCount(buffer));
+///      
+///  To detect truncation, perhaps while building a path, the following might be used:
+///      char *dir, *file, path[_MAX_PATH];
+///      if(Strlcpy(path, dir, EAArrayCount(path)) >= EAArrayCount(path))
+///          goto toolong;
+///      if(Strlcat(path, file, EAArrayCount(path)) >= EAArrayCount(path))
+///          goto toolong;
+///
+EASTDC_API size_t Strlcat(char8_t*  pDestination, const char8_t*  pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t Strlcat(wchar_t* pDestination, const wchar_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity);
+	EASTDC_API size_t Strlcat(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity);
+#endif
+
+// UTF8 <-> UTF16 <-> UTF32 encoding conversion 
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char8_t*  pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcat(char8_t*  pDestination, const char16_t* pSource, size_t nDestCapacity);
+
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char8_t*  pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcat(char8_t*  pDestination, const char32_t* pSource, size_t nDestCapacity);
+
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char32_t*  pSource, size_t nDestCapacity);
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char16_t*  pSource, size_t nDestCapacity);
+
+
+/// Strlcat
+///
+/// Implements a generic STL/EASTL 8 <--> 16/32 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::wstring sW(L"hello ");
+///    eastl::string8 s8("world");
+///
+///    EA::StdC::Strlcat(sW, s8);  // char8_t -> wchar_t. Becomes L"hello world."
+///
+namespace Internal
+{
+	template <typename Dest, typename Source>
+	struct Strlcat5Class
+	{
+		static bool Strlcat5Impl(Dest& d, const Source& s)
+		{
+			const int additionalLength = EA::StdC::Strlcpy(&d[0], s.data(), 0, s.length());
+
+			if(additionalLength >= 0) // If there wasn't an encoding error in the source string...
+			{
+				const typename Dest::size_type originalLength = d.length();
+				d.resize(originalLength + (typename Dest::size_type)additionalLength);
+				EA::StdC::Strlcpy(&d[originalLength], s.data(), d.length() + 1, s.length());
+
+				return true;
+			}
+
+			return false;
+		}
+	};
+
+	template <typename T> // Specialization for the case that source and dest are equal.
+	struct Strlcat5Class<T, T>
+	{
+		static bool Strlcat5Impl(T& d, const T& s)
+			{ d += s; return true; }
+	};
+}
+
+template <typename Dest, typename Source>
+inline bool Strlcat(Dest& d, const Source& s)
+{
+	return Internal::Strlcat5Class<Dest, Source>::Strlcat5Impl(d, s);
+}
+
+
+/// Strlcat
+///
+/// Implements a generic STL/EASTL 8 <--> 16/32 conversion via Strlcpy.
+///
+/// Example usage:
+///    eastl::wstring sW(L"hello ");
+///    EA::StdC::Strlcat(sW, "world");  // char8_t -> wchar_t. Becomes L"hello world."
+///
+namespace Internal
+{
+	template <typename Dest, typename DestCharType, typename Source>
+	struct Strlcat6Class
+	{
+		static bool Strlcat6Impl(Dest& d, const Source* pSource, size_t sourceLength)
+		{
+			const int additionalLength = EA::StdC::Strlcpy(&d[0], pSource, 0, sourceLength);
+
+			if(additionalLength >= 0) // If there wasn't an encoding error in the source string...
+			{
+				const typename Dest::size_type originalLength = d.length();
+				d.resize(originalLength + (typename Dest::size_type)additionalLength);
+				EA::StdC::Strlcpy(&d[originalLength], pSource, d.length() + 1, sourceLength);
+
+				return true;
+			}
+
+			return false;
+		}
+	};
+
+	template <typename Dest, typename CharType> // Specialization for the case that source and dest are equal.
+	struct Strlcat6Class<Dest, CharType, CharType>
+	{
+		static bool Strlcat6Impl(Dest& d, const CharType* pSource, size_t sourceLength)
+			{ d.append(pSource, (typename Dest::size_type)sourceLength); return true; }
+	};
+}
+
+template <typename Dest, typename Source>
+inline bool Strlcat(Dest& d, const Source* pSource, size_t sourceLength = kSizeTypeUnset)
+{
+	if(sourceLength == kSizeTypeUnset)
+		sourceLength = Strlen(pSource);
+
+	return Internal::Strlcat6Class<Dest, typename Dest::value_type, Source>::Strlcat6Impl(d, pSource, sourceLength);
+}
+
+
+
+/// Strxfrm
+///
+/// Copies the first n characters of pSource to pDest performing the 
+/// appropiate transformations for the current locale settings if needed.
+/// Returns the new Strlen of pDest. 
+/// If pDest is NULL or n is zero, nothing is written to pDest and the return 
+/// value is the required Strlen of pDest.
+/// Since localization is not currently supported by this module, this function
+/// doesn't do any transformation other than simply copy.
+/// This is similar to the strxfrm C function.
+///
+EASTDC_API size_t Strxfrm(char8_t*  pDest, const char8_t*  pSource, size_t n);
+EASTDC_API size_t Strxfrm(char16_t* pDest, const char16_t* pSource, size_t n);
+EASTDC_API size_t Strxfrm(char32_t* pDest, const char32_t* pSource, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t Strxfrm(wchar_t* pDest, const wchar_t* pSource, size_t n);
+#endif
+
+
+/// Strdup
+///
+/// Duplicates a string, returning a pointer allocated by operator new[] and 
+/// which needs to be deallocated by the user with delete[] or by Strdel.
+/// If Strdup resides in a DLL or similar kind of independent library, Strldel is required.
+/// This is similar to the strdup C function.
+///
+EASTDC_API char8_t*  Strdup(const char8_t*  pString);
+EASTDC_API char16_t* Strdup(const char16_t* pString);
+EASTDC_API char32_t* Strdup(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strdup(const wchar_t* pString);
+#endif
+
+
+/// Strdel
+///
+/// Deletes a string returned by Strdup.
+///
+EASTDC_API void Strdel(char8_t*  pString);
+EASTDC_API void Strdel(char16_t* pString);
+EASTDC_API void Strdel(char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API void Strdel(wchar_t* pString);
+#endif
+
+
+/// Strupr
+///
+/// Converts an ASCII string to upper-case. Any characters that are outside
+/// the ASCII set are not left as they are.
+/// If you want Unicode-correct character and string functionality for all 
+/// Unicode characters, use the EATextUnicode module from the EAText package. 
+/// The Unicode module may be split off into a  unique package by the time you read this.
+/// This is similar to the sometimes seen _strupr C function.
+///
+EASTDC_API char8_t*  Strupr(char8_t*  pString);
+EASTDC_API char16_t* Strupr(char16_t* pString);
+EASTDC_API char32_t* Strupr(char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strupr(wchar_t* pString);
+#endif
+
+
+/// Strlwr
+///
+/// Converts an ASCII string to lower-case. Any characters that are outside
+/// the ASCII set are not left as they are.
+/// If you want Unicode-correct character and string functionality for all 
+/// Unicode characters, use the EATextUnicode module from the EAText package. 
+/// The Unicode module may be split off into a  unique package by the time you read this.
+/// This is similar to the sometimes seen _strlwr C function.
+///
+EASTDC_API char8_t*  Strlwr(char8_t*  pString);
+EASTDC_API char16_t* Strlwr(char16_t* pString);
+EASTDC_API char32_t* Strlwr(char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strlwr(wchar_t* pString);
+#endif
+
+
+/// Strmix
+///
+/// Copies the string from source to destination while converting it to mixed 
+/// upper and lower case. The first letter to appear at the beginning of the 
+/// string or after a character that is contained in separatorsPtr is encountered 
+/// is capitalized while the rest is lower-cased. For example, if pSource is 
+/// "aBcDe_fGhI*jKl+m", and if pDelimiters is "_+", pDestination becomes "Abcde_Fghi*jkl+M".
+/// pDelimiters is a string containing characters acting as separators to mark that 
+/// the next character is to be capitalized
+/// Returns pDestination.
+///
+/// This function is currently provided for compatibility with the rwstdc package. 
+///
+EASTDC_API char8_t*  Strmix(char8_t*  pDestination, const char8_t*  pSource, const char8_t*  pDelimiters);
+EASTDC_API char16_t* Strmix(char16_t* pDestination, const char16_t* pSource, const char16_t* pDelimiters);
+EASTDC_API char32_t* Strmix(char32_t* pDestination, const char32_t* pSource, const char32_t* pDelimiters);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strmix(wchar_t* pDestination, const wchar_t* pSource, const wchar_t* pDelimiters);
+#endif
+
+
+/// Strchr
+///
+/// Returns a pointer to the first occurrence of c in pString, or NULL if the c is not found.
+/// The null-terminating character is included as part of the string and can also be searched.
+/// This is similar to the strchr C function.
+///
+EASTDC_API char8_t*  Strchr(const char8_t*  pString, int      c);
+EASTDC_API char16_t* Strchr(const char16_t* pString, char16_t c);
+EASTDC_API char32_t* Strchr(const char32_t* pString, char32_t c);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strchr(const wchar_t* pString, wchar_t c);
+#endif
+
+/// Strnchr
+///
+/// Returns a pointer to the first occurrence of c in pString, or NULL if the c is not found within the first n characters.
+/// The search stops after n characters or at the end of the string, whichever comes first.
+/// The null-terminating character is included as part of the string and can also be searched.
+/// This differs from some implementations that let you search past the null-terminator of pString. If you'd like to do that, use EA::StdC::Memchr
+///  Some other C library strnchr functions have the last two arguments switched relative to this version.
+///
+EASTDC_API char8_t*  Strnchr(const char8_t*  pString, int      c, size_t n);
+EASTDC_API char16_t* Strnchr(const char16_t* pString, char16_t c, size_t n);
+EASTDC_API char32_t* Strnchr(const char32_t* pString, char32_t c, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strnchr(const wchar_t* pString, wchar_t c);
+#endif
+
+
+/// Strcspn
+///
+/// Scans pString1 character by character, returning the number of chars read 
+/// until the first occurrence of any character included in pString2. 
+/// The search includes terminating null-characters, so the function will return 
+/// the length of pString1 if none of the characters included in pString2 is in pString1.
+/// This is similar to the strcspn C function.
+///
+EASTDC_API size_t  Strcspn(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API size_t  Strcspn(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API size_t  Strcspn(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t  Strcspn(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strpbrk
+///
+/// Scans pString1 character by character, returning a pointer to the first 
+/// character that matches with any of the characters in pString2. The search 
+/// does not includes the terminating null characters.
+/// This is similar to the strpbrk C function.
+///
+EASTDC_API char8_t*  Strpbrk(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API char16_t* Strpbrk(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API char32_t* Strpbrk(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strpbrk(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strrchr
+///
+/// Returns the last occurrence of c in pString, or NULL if the c is not found.
+/// The null-terminating character is included as part of the string and can also be searched.
+/// This is similar to the strrchr C function.
+///
+EASTDC_API char8_t*  Strrchr(const char8_t*  pString, int      c);
+EASTDC_API char16_t* Strrchr(const char16_t* pString, char16_t c);
+EASTDC_API char32_t* Strrchr(const char32_t* pString, char32_t c);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strrchr(const wchar_t* pString, wchar_t c);
+#endif
+
+
+/// Strspn
+///
+/// This is similar to the strspn C function.
+///
+EASTDC_API size_t Strspn(const char8_t*  pString, const char8_t*  pSubString);
+EASTDC_API size_t Strspn(const char16_t* pString, const char16_t* pSubString);
+EASTDC_API size_t Strspn(const char32_t* pString, const char32_t* pSubString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t Strspn(const wchar_t* pString, const wchar_t* pSubString);
+#endif
+
+
+/// Strstr
+///
+/// Finds the first occurrence of pSubString within pString, exclusive of the 
+/// terminating null character.
+/// This is similar to the strstr C function.
+/// Note: Like the C version this takes const char parameters but returns
+/// non-const values. In C++ there are two versions: an all const version and 
+/// an all non-const version. The C++ design is preferable, but switching to 
+/// it could break users who expect a non-const return value from the const 
+/// argument version.
+/// 
+EASTDC_API char8_t*  Strstr(const char8_t*  pString, const char8_t*  pSubString);
+EASTDC_API char16_t* Strstr(const char16_t* pString, const char16_t* pSubString);
+EASTDC_API char32_t* Strstr(const char32_t* pString, const char32_t* pSubString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strstr(const wchar_t* pString, const wchar_t* pSubString);
+#endif
+
+
+/// Stristr
+///
+/// This is a case-insensitive version of Strstr.
+/// Scans pString for the first occurrence of pSubString with case-insensitive 
+/// comparison. The search does not include terminating null-characters.
+/// The return value is char16_t* and not const char16_t* because that's how 
+/// standard wcsstr works.
+/// This is similar to the stristr and wcsstr C functions. See the notes for
+/// those functions for some additional info.
+///
+EASTDC_API char8_t*  Stristr(const char8_t*  pString, const char8_t*  pSubString);
+EASTDC_API char16_t* Stristr(const char16_t* pString, const char16_t* pSubString);
+EASTDC_API char32_t* Stristr(const char32_t* pString, const char32_t* pSubString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Stristr(const wchar_t* pString, const wchar_t* pSubString);
+#endif
+
+
+/// Strrstr
+///
+/// Finds the last occurrence of pSubString within pString, exclusive of the 
+/// terminating null character.
+/// This is similar to the strrstr C function. See the notes for
+/// Strstr for some additional info.
+///
+EASTDC_API char8_t*  Strrstr(const char8_t*  pString, const char8_t*  pSubString);
+EASTDC_API char16_t* Strrstr(const char16_t* pString, const char16_t* pSubString);
+EASTDC_API char32_t* Strrstr(const char32_t* pString, const char32_t* pSubString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strrstr(const wchar_t* pString, const wchar_t* pSubString);
+#endif
+
+
+/// Strirstr
+///
+/// Finds the last case insensitive occurrence of pSubString within pString, 
+/// exclusive of the terminating null character.
+/// This is similar to the strirstr C function. See the notes for
+/// Strstr for some additional info.
+///
+EASTDC_API char8_t*  Strirstr(const char8_t*  pString, const char8_t*  pSubString);
+EASTDC_API char16_t* Strirstr(const char16_t* pString, const char16_t* pSubString);
+EASTDC_API char32_t* Strirstr(const char32_t* pString, const char32_t* pSubString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strirstr(const wchar_t* pString, const wchar_t* pSubString);
+#endif
+
+
+/// Strstart
+///
+/// Returns true if pString begins with pPrefix.
+/// Both strings must be non-null, but either can be empty.
+/// An empty pPrefix results in success, regardless of what pString is.
+///
+EASTDC_API bool Strstart(const char8_t* pString, const char8_t* pPrefix);
+EASTDC_API bool Strstart(const char16_t* pString, const char16_t* pPrefix);
+EASTDC_API bool Strstart(const char32_t* pString, const char32_t* pPrefix);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API bool Strstart(const wchar_t* pString, const wchar_t* pPrefix);
+#endif
+
+
+/// Stristart
+///
+/// Returns true if pString begins with pPrefix, compared case-insensitively.
+/// Both strings must be non-null, but either can be empty.
+/// An empty pPrefix results in success, regardless of what pString is.
+/// Supports ASCII case comparisons only.
+///
+EASTDC_API bool Stristart(const char8_t* pString, const char8_t* pPrefix);
+EASTDC_API bool Stristart(const char16_t* pString, const char16_t* pPrefix);
+EASTDC_API bool Stristart(const char32_t* pString, const char32_t* pPrefix);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API bool Stristart(const wchar_t* pString, const wchar_t* pPrefix);
+#endif
+
+
+/// Strend
+///
+/// Returns true if pString's contents ends with pSuffix's contents.
+/// Both strings must be non-null, but either can be empty.
+/// An empty pSuffix results in success, regardless of what pString is.
+/// This Strend differs from Strend(const char_t*) in that this one searches
+/// for a substring as opposed to simply searching for the end of the string.
+/// If stringLength is the default value then it will be calculated, resulting in 
+/// slower execution than otherwise.
+///
+EASTDC_API bool Strend(const char8_t* pString, const char8_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+EASTDC_API bool Strend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+EASTDC_API bool Strend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+
+
+/// Striend
+///
+/// Returns true if pString's contents ends with pSuffix's contents, compared case-insensitively.
+/// Both strings must be non-null, but either can be empty.
+/// An empty pSuffix results in success, regardless of what pString is.
+/// If stringLength is the default value then it will be calculated, resulting in 
+/// slower execution than otherwise.
+/// Supports ASCII case comparisons only.
+///
+EASTDC_API bool Striend(const char8_t* pString, const char8_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+EASTDC_API bool Striend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+EASTDC_API bool Striend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength = kSizeTypeUnset, size_t suffixLength = kSizeTypeUnset);
+
+
+/// Strtok
+///
+/// Parses pString into tokens separated by characters in pDelimiters.
+/// If pString is NULL, the saved pointer in pContext is used as
+/// the next starting point. 
+///
+/// Note that this function is slightly different from strtok in that
+/// it requires a user supplied context pointer. This context pointer
+/// requirement is part of the C99 standard wcstok function and is
+/// there for good reason: strtok is neither thread-safe nor re-entrant. 
+/// The sometimes-seen strtok_r function is provided which rectifies
+/// this problem. Strtok here acts like strtok_r or wcstok.
+///
+/// Note that this function modifies pString in place, as per the C strtok
+/// convention. To avoid this use the Strtok2 function.
+/// 
+/// This is similar to wcstok and to the sometimes seen strtok_r 
+/// function (as opposed to the strtok function).
+///
+/// Detailed specification:
+/// The first call in the sequence has pString as its first argument, and is followed 
+/// by calls with a null pointer as their first argument. The separator string pointed 
+/// to by pDelimiters may be different from call to call. The first call in the 
+/// sequence shall search the string pointed to by pString for the first character code 
+/// that is not contained in the current separator string pointed to by pDelimiters. 
+/// If no such character code is found, then there are no tokens in the string 
+/// pointed to by pString and Strtok shall return a null pointer. If such a 
+/// character code is found, it shall be the start of the first token.
+/// The Strtok function shall then search from there for a character code that is 
+/// contained in the current separator string. If no such character code is found, 
+/// the current token extends to the end of the string pointed to by pString, and 
+/// subsequent searches for a token shall return a null pointer. If such a character 
+/// code is found, it shall be overwritten by a null character, which terminates the 
+/// current token. The Strtok function shall save a pointer to the following character  
+/// code, from which the next search for a token shall start. Each subsequent call, 
+/// with a null pointer as the value of the first argument, shall start searching 
+/// from pContext and behave as described above.
+/// 
+/// Example:
+///     char8_t* pContext = NULL;
+///     char8_t* pString  = "000\t000\n000\t000";
+///     char8_t* pResult;
+///
+///     while((pResult = Strtok(pContext ? NULL : pString, "\t\n", &pContext)) != NULL)
+///         printf("%s\n", pResult);
+///     
+/// Example:
+///    char8_t* p = "-abc-=-def";
+///    char8_t* p1;
+///    char8_t* r;
+///
+///    r = Strtok8(p,    "-",  &p1);    // r = "abc", p1 = "=-def", p = "abc\0=-def"
+///    r = Strtok8(NULL, "-=", &p1);    // r = "def", p1 = NULL,    p = "abc\0=-def"
+///    r = Strtok8(NULL, "=",  &p1);    // r = NULL,  p1 = NULL,    p = "abc\0=-def"
+/// 
+EASTDC_API char8_t*  Strtok(char8_t*  pString, const char8_t*  pDelimiters, char8_t**  pContext);
+EASTDC_API char16_t* Strtok(char16_t* pString, const char16_t* pDelimiters, char16_t** pContext);
+EASTDC_API char32_t* Strtok(char32_t* pString, const char32_t* pDelimiters, char32_t** pContext);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strtok(wchar_t* pString, const wchar_t* pDelimiters, wchar_t** pContext);
+#endif
+
+
+/// Strtok2
+///
+/// Searches through pString while skipping any leading delimiters and returns
+/// the pointer to the first token. The string length (which is possibly zero) 
+/// of the resulting delimited string is returned in pResultLength. 
+/// Returns NULL if pString is empty.
+///
+/// Unlike strtok, this function does not replace the following delimiter with 
+/// a null character and thus leaves pString unmodified.
+///
+/// This function is identical to the rwstdc package GetFirstTokenInString function
+/// when bFirst is true and identical to GetNextTokenInString when bFirst is false. 
+/// This function has the potentially undesirable effect of skipping empty fields 
+/// in the string; you may want to try using the parsing functions in EATextUtil.h 
+/// in order to avoid this.
+/// 
+EASTDC_API const char8_t*  Strtok2(const char8_t*  pString, const char8_t*  pDelimiters, size_t* pResultLength, bool bFirst);
+EASTDC_API const char16_t* Strtok2(const char16_t* pString, const char16_t* pDelimiters, size_t* pResultLength, bool bFirst);
+EASTDC_API const char32_t* Strtok2(const char32_t* pString, const char32_t* pDelimiters, size_t* pResultLength, bool bFirst);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API const wchar_t* Strtok2(const wchar_t* pString, const wchar_t* pDelimiters, size_t* pResultLength, bool bFirst);
+#endif
+
+
+/// Strset
+///
+/// Sets all characters up to but not including the terminating 0 char in 
+/// pString with the value c. The char8_t version uses int instead of char8_t
+/// in order to be compatible with the C strset function.
+/// Returns pString.
+/// This is similar to the sometimes seen _strset C function.
+///
+EASTDC_API char8_t*  Strset(char8_t*  pString, int      c);
+EASTDC_API char16_t* Strset(char16_t* pString, char16_t c);
+EASTDC_API char32_t* Strset(char32_t* pString, char32_t c);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strset(wchar_t* pString, wchar_t c);
+#endif
+
+
+/// Strnset
+///
+/// Sets up to the first n bytes of pString to c. If n is greater than the 
+/// Strlen of pString, the Strlen(pString) is used instead of n. 
+/// This is similar to the sometimes seen _strnset C function.
+///
+EASTDC_API char8_t*  Strnset(char8_t*  pString, int c,      size_t n);
+EASTDC_API char16_t* Strnset(char16_t* pString, char16_t c, size_t n);
+EASTDC_API char32_t* Strnset(char32_t* pString, char32_t c, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strnset(wchar_t* pString, wchar_t c, size_t n);
+#endif
+
+
+/// Strrev
+///
+/// This is similar to the sometimes seen _strrev C function.
+///
+EASTDC_API char8_t*  Strrev(char8_t*  pString);
+EASTDC_API char16_t* Strrev(char16_t* pString);
+EASTDC_API char32_t* Strrev(char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strrev(wchar_t* pString);
+#endif
+
+
+/// Strstrip
+///
+/// Removes leading and trailing space, defined by the Isspace function.
+/// The trailing space is removed by potentially modifying the end position 
+/// of the string via writing '\0'. The leading space isn't removed, but rather
+/// the first non-whitespace char in pString is returned. Thus beware that 
+/// this function may return a pointer >= pString, and you cannot free that 
+/// that pointer instead of pString.
+/// This is similar to the sometimes seen strstrip C function.
+///
+EASTDC_API char8_t*  Strstrip(char8_t*  pString);
+EASTDC_API char16_t* Strstrip(char16_t* pString);
+EASTDC_API char32_t* Strstrip(char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Strstrip(wchar_t* pString);
+#endif
+
+
+/// Strcmp
+///
+/// This is similar to the strcmp C function.
+///
+EASTDC_API int Strcmp(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int Strcmp(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API int Strcmp(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strcmp(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strncmp
+///
+/// This is similar to the strncmp C function.
+///
+EASTDC_API int Strncmp(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+EASTDC_API int Strncmp(const char16_t* pString1, const char16_t* pString2, size_t n);
+EASTDC_API int Strncmp(const char32_t* pString1, const char32_t* pString2, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strncmp(const wchar_t* pString1, const wchar_t* pString2, size_t n);
+#endif
+
+
+/// Stricmp
+///
+/// This is similar to the sometimes seen _stricmp or strcasecmp C function.
+///
+EASTDC_API int Stricmp(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int Stricmp(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API int Stricmp(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Stricmp(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strnicmp
+///
+/// This is similar to the sometimes seen _strnicmp or strncasecmp C function.
+///
+EASTDC_API int Strnicmp(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+EASTDC_API int Strnicmp(const char16_t* pString1, const char16_t* pString2, size_t n);
+EASTDC_API int Strnicmp(const char32_t* pString1, const char32_t* pString2, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strnicmp(const wchar_t* pString1, const wchar_t* pString2, size_t n);
+#endif
+
+
+/// StrcmpAlnum
+///
+/// *** This function is deprecated. ***
+///
+/// This function is fairly crippled and may not be as useful as StrcmpNumeric.
+/// Its sole purpose is for backward compatibility with older EA code.
+///
+/// Implements an alphanumeric string compare. Strings of integral decimal digits
+/// are treated as unsigned integers instead of individual ascii characters. 
+/// Thus a string of "abc100" is greater than a string of "abc20", whereas strcmp 
+/// would say that it is less. Dashes in front of characters are not treated as 
+/// sign characters. Leading zeroes in front of numbers are treated as part of 
+/// the number. Numbers are always treated as base 10.
+///
+/// Example behaviour:
+///    EATEST_VERIFY(StrcmpAlnum("",       "")        == 0);
+///    EATEST_VERIFY(StrcmpAlnum("abc",    "abc")     == 0);
+///    EATEST_VERIFY(StrcmpAlnum("a",      "b")        < 0);
+///    EATEST_VERIFY(StrcmpAlnum("abc",    "abcd")     < 0);
+///    EATEST_VERIFY(StrcmpAlnum("abcd",   "abc")      > 0);
+///    EATEST_VERIFY(StrcmpAlnum("a",      "")         > 0);
+///    EATEST_VERIFY(StrcmpAlnum("",       "1")        < 0);       // Verify numerics evaluate highest.
+///    EATEST_VERIFY(StrcmpAlnum("abc",    "abd")      < 0);
+///    EATEST_VERIFY(StrcmpAlnum("a",      "1")        < 0);
+///    EATEST_VERIFY(StrcmpAlnum("%",      "1")        < 0);       // Verify numerics evaluate highest.
+///    EATEST_VERIFY(StrcmpAlnum("103",    "12")       > 0);       // Verify that numeric compare is occuring.
+///    EATEST_VERIFY(StrcmpAlnum("abc12a", "abc12b")   < 0);
+///    EATEST_VERIFY(StrcmpAlnum("abc123", "abc12a")   > 0);
+///    EATEST_VERIFY(StrcmpAlnum("abc-2",  "abc-1")    > 0);       // Verify that '-' is not treated as a minus sign.
+///    EATEST_VERIFY(StrcmpAlnum("abc1.1", "abc1.02")  < 0);       // Verify that '.' is not treated as a decimal point.
+///    EATEST_VERIFY(StrcmpAlnum("44",     "044")     == 0);       // Verify that digit spans are treated as base 10 numbers.
+///
+EASTDC_API int StrcmpAlnum(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int StrcmpAlnum(const char16_t* pString1, const char16_t* pString2);
+// No 32 bit version because this function is deprecated.
+
+EASTDC_API int StricmpAlnum(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int StricmpAlnum(const char16_t* pString1, const char16_t* pString2);
+// No 32 bit version because this function is deprecated.
+
+
+/// StrcmpNumeric
+///
+/// Compares number strings, including both integer and floating point numbers.
+/// This is different from StrcmpAlnum because StrcmpAlnum just looks at digits
+/// as integers and doesn't understand decimal points or exponents. A primary use
+/// of this function is to save CPU cycles when comparing numeric strings, since
+/// it would be significantly slower to convert strings to floating point values
+/// and then do a floating point compare. Another use of this function is that it
+/// avoids using the FPU and so can handle precisions that might not be available
+/// of the user has the FPU precision set to a lower value. Numbers are always
+/// treated as base-ten, even if they have leading zeroes.
+/// If the string has a specified length and the string has a 0 char prior to that
+/// length, the 0 char is not treated as a string terminator but is treated like
+/// any other control character value.
+/// For efficiency reasons, this code doesn't rigorously check to make sure numbers
+/// are valid.
+/// Virtual number strings such as "NaN" are not treated as numeric NaNs but are
+/// treated as string characters. Normally NaN compared to a non-NaN is already 
+/// unequal. But the situation where this function differs from numeric NaN
+/// comparison is that numeric NaNs always compare against self as non-equal.
+/// That doesn't work well in the world of integer and string logic, as it makes 
+/// it difficult to have sensible hashed containers of such things.
+///
+/// Example behaviour:
+///    EATEST_VERIFY(StrcmpNumeric("1",       "1")      == 0);
+///    EATEST_VERIFY(StrcmpNumeric("12",      "13")      > 0);
+///    EATEST_VERIFY(StrcmpNumeric("-1",      "1")       < 0);
+///    EATEST_VERIFY(StrcmpNumeric("10.01",   "10.02")   < 0);
+///    EATEST_VERIFY(StrcmpNumeric("-1.010",  "-1.001")  < 0);       // -1.010 is more negative than -1.010, and thus is lesser.
+///
+/// Basic alphabetic values are handled as well, like Strcmp.
+///    EATEST_VERIFY(StrcmpNumeric("",       "")        == 0);
+///    EATEST_VERIFY(StrcmpNumeric("abc",    "abc")     == 0);
+///    EATEST_VERIFY(StrcmpNumeric("a",      "b")        < 0);
+///    EATEST_VERIFY(StrcmpNumeric("abc",    "abcd")     < 0);
+///    EATEST_VERIFY(StrcmpNumeric("abcd",   "abc")      > 0);
+///    EATEST_VERIFY(StrcmpNumeric("a",      "")         > 0);
+///    EATEST_VERIFY(StrcmpNumeric("",       "1")        < 0);       // Verify numerics evaluate highest.
+///    EATEST_VERIFY(StrcmpNumeric("abc",    "abd")      < 0);
+///    EATEST_VERIFY(StrcmpNumeric("a",      "1")        < 0);
+///    EATEST_VERIFY(StrcmpNumeric("%",      "1")        < 0);       // Verify numerics evaluate highest.
+///    EATEST_VERIFY(StrcmpNumeric("103",    "12")       > 0);       // Verify that numeric compare is occuring.
+///    EATEST_VERIFY(StrcmpNumeric("abc12a", "abc12b")   < 0);
+///    EATEST_VERIFY(StrcmpNumeric("abc123", "abc12a")   > 0);
+///    EATEST_VERIFY(StrcmpNumeric("abc-2",  "abc-1")    < 0);
+///    EATEST_VERIFY(StrcmpNumeric("abc1.1", "abc1.02")  > 0);
+///    EATEST_VERIFY(StrcmpNumeric("44",     "044")     == 0);
+///
+EASTDC_API int StrcmpNumeric (const char8_t*  pString1, const char8_t*  pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char8_t  decimal = '.', char8_t  thousandsSeparator = ',');
+EASTDC_API int StrcmpNumeric (const char16_t* pString1, const char16_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char16_t decimal = '.', char16_t thousandsSeparator = ',');
+EASTDC_API int StrcmpNumeric (const char32_t* pString1, const char32_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char32_t decimal = '.', char32_t thousandsSeparator = ',');
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int StrcmpNumeric (const wchar_t* pString1, const wchar_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, wchar_t decimal = '.', wchar_t thousandsSeparator = ',');
+#endif
+
+EASTDC_API int StricmpNumeric(const char8_t*  pString1, const char8_t*  pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char8_t  decimal = '.', char8_t  thousandsSeparator = ',');
+EASTDC_API int StricmpNumeric(const char16_t* pString1, const char16_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char16_t decimal = '.', char16_t thousandsSeparator = ',');
+EASTDC_API int StricmpNumeric(const char32_t* pString1, const char32_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, char32_t decimal = '.', char32_t thousandsSeparator = ',');
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int StricmpNumeric(const wchar_t* pString1, const wchar_t* pString2, size_t length1 = kSizeTypeUnset, size_t length2 = kSizeTypeUnset, wchar_t decimal = '.', wchar_t thousandsSeparator = ',');
+#endif
+
+
+/// Strcoll
+///
+/// Compare two strings using the locale LC_COLLATE information.
+/// In the C locale, Strcmp() is used to make the comparison.
+/// However, this function is not localized and always uses the C locale. 
+/// If you want Unicode-correct character and string functionality for all 
+/// Unicode characters, use the EATextUnicode module from the EAText package. 
+/// The Unicode module may be split off into a  unique package by the time you read this.
+/// This is similar to the strcoll C function.
+///
+EASTDC_API int Strcoll(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int Strcoll(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API int Strcoll(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strcoll(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strncoll
+///
+/// If you want Unicode-correct character and string functionality for all 
+/// Unicode characters, use the EATextUnicode module from the EAText package. 
+/// The Unicode module may be split off into a  unique package by the time you read this.
+/// This is similar to the sometimes seen _strncoll C function.
+///
+EASTDC_API int Strncoll(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+EASTDC_API int Strncoll(const char16_t* pString1, const char16_t* pString2, size_t n);
+EASTDC_API int Strncoll(const char32_t* pString1, const char32_t* pString2, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strncoll(const wchar_t* pString1, const wchar_t* pString2, size_t n);
+#endif
+
+
+/// Stricoll
+///
+/// This is similar to the sometimes seen _stricoll C function.
+///
+EASTDC_API int Stricoll(const char8_t*  pString1, const char8_t*  pString2);
+EASTDC_API int Stricoll(const char16_t* pString1, const char16_t* pString2);
+EASTDC_API int Stricoll(const char32_t* pString1, const char32_t* pString2);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Stricoll(const wchar_t* pString1, const wchar_t* pString2);
+#endif
+
+
+/// Strnicoll
+///
+/// If you want Unicode-correct character and string functionality for all 
+/// Unicode characters, use the EATextUnicode module from the EAText package. 
+/// The Unicode module may be split off into a  unique package by the time you read this.
+/// This is similar to the sometimes seen _strnicoll C function.
+///
+EASTDC_API int Strnicoll(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+EASTDC_API int Strnicoll(const char16_t* pString1, const char16_t* pString2, size_t n);
+EASTDC_API int Strnicoll(const char32_t* pString1, const char32_t* pString2, size_t n);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int Strnicoll(const wchar_t* pString1, const wchar_t* pString2, size_t n);
+#endif
+
+
+
+/// EASTDC_NATIVE_FCVT / EASTDC_NATIVE_FCVT_SHORT
+///
+/// EASTDC_NATIVE_FCVT and EASTDC_NATIVE_FCVT_SHORT are defined as 0 or 1.
+/// This is useful for defining if the provided standard library implements
+/// a version of the fcvt and ecvt functions. A standard library version of
+/// fcvt and ecvt may be faster than the one provided here.
+/// EASTDC_NATIVE_FCVT is 1 if the compiler's C library provides ecvt/fcvt/gcvt.
+/// EASTDC_NATIVE_FCVT_SHORT is 1 if the implementation limits the output string
+/// to some internally defined limit as opposed to using the user-specified ndigits 
+/// output string precision. 
+///
+#ifndef EASTDC_NATIVE_FCVT
+	#if defined(EA_PLATFORM_MICROSOFT)
+		#define EASTDC_NATIVE_FCVT       1
+		#define EASTDC_NATIVE_FCVT_SHORT 0
+	#elif defined(EA_COMPILER_GNUC) && (defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_WINDOWS)) && !defined(_BSD_SIZE_T_DEFINED_) && !defined(__APPLE__) // BSD-based (as opposed to glibc-based) standard libraries don't support fcvt. Apple has a fcvt, but it is broken and our unit tests fail when using it.
+		#define EASTDC_NATIVE_FCVT       1
+		#define EASTDC_NATIVE_FCVT_SHORT 1 // GCC's standard library will only write 16 chars, even if you pass a higher ndigits value.
+	#else
+		#define EASTDC_NATIVE_FCVT       0
+		#define EASTDC_NATIVE_FCVT_SHORT 0
+	#endif
+#endif
+
+
+
+/// EcvtBuf
+///
+/// These functions convert a floating point value to a string and decimal point information.
+/// They are most useful for constucting localized formatted numbers. These are similar to 
+/// the ecvt() and fcvt() functions but are thread-safe due to having the user specify the buffer.
+/// The supplied buffer must have at least 350 characters of capacity, which could hold any 
+/// double represented as a decimal string.
+///
+/// nDigitCount Must be > 0, pDecimalPos must be valid, pSign must be valid, pBuffer must be valid.
+///
+const int kEcvtBufMaxSize = 350;
+
+EASTDC_API char8_t*  EcvtBuf(double dValue, int nDigitCount, int* pDecimalPos, int* pSign, char8_t*  pBuffer);
+EASTDC_API char16_t* EcvtBuf(double dValue, int nDigitCount, int* pDecimalPos, int* pSign, char16_t* pBuffer);
+EASTDC_API char32_t* EcvtBuf(double dValue, int nDigitCount, int* pDecimalPos, int* pSign, char32_t* pBuffer);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* EcvtBuf(double dValue, int nDigitCount, int* pDecimalPos, int* pSign, wchar_t* pBuffer);
+#endif
+
+
+/// FcvtBuf
+///
+/// These functions convert a floating point value to a string and decimal point information.
+/// They are most useful for constucting localized formatted numbers. These are similar to 
+/// the ecvt() and fcvt() functions but are thread-safe due to having the user specify the buffer.
+/// The supplied buffer must have at least 350 characters of capacity, which could hold any 
+/// double represented as a decimal string.
+///
+/// nDigitCountAfterDecimal Must be > 0, pDecimalPos must be valid, pSign must be valid, pBuffer must be valid.
+///
+const int kFcvtBufMaxSize = 350;
+
+EASTDC_API char8_t*  FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* pDecimalPos, int* pSign, char8_t*  pBuffer);
+EASTDC_API char16_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* pDecimalPos, int* pSign, char16_t* pBuffer);
+EASTDC_API char32_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* pDecimalPos, int* pSign, char32_t* pBuffer);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* pDecimalPos, int* pSign, wchar_t* pBuffer);
+#endif
+
+
+
+//const int kInt8MinCapacity   =  5;
+//const int kUint8MinCapacity  =  4;
+//const int kInt16MinCapacity  =  7;
+//const int kUint16MinCapacity =  6;
+const int kInt32MinCapacity  = 12;
+const int kUint32MinCapacity = 11;
+const int kInt64MinCapacity  = 21;
+const int kUint64MinCapacity = 21;
+
+
+/// I32toa
+///
+/// Buffer must hold at least kInt32MinCapacity (12) chars.
+///
+EASTDC_API char8_t*  I32toa(int32_t nValue, char8_t*  pBuffer, int nBase);
+EASTDC_API char16_t* I32toa(int32_t nValue, char16_t* pBuffer, int nBase);
+EASTDC_API char32_t* I32toa(int32_t nValue, char32_t* pBuffer, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* I32toa(int32_t nValue, wchar_t* pBuffer, int nBase);
+#endif
+
+
+/// U32toa
+///
+/// Buffer must hold at least kUint32MinCapacity (11) chars.
+///
+EASTDC_API char8_t*  U32toa(uint32_t nValue, char8_t*  pBuffer, int nBase);
+EASTDC_API char16_t* U32toa(uint32_t nValue, char16_t* pBuffer, int nBase);
+EASTDC_API char32_t* U32toa(uint32_t nValue, char32_t* pBuffer, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* U32toa(uint32_t nValue, wchar_t* pBuffer, int nBase);
+#endif
+
+
+/// I64toa
+///
+/// Buffer must hold at least kInt64MinCapacity (21) chars.
+///
+EASTDC_API char8_t*  I64toa(int64_t nValue, char8_t*  pBuffer, int nBase);
+EASTDC_API char16_t* I64toa(int64_t nValue, char16_t* pBuffer, int nBase);
+EASTDC_API char32_t* I64toa(int64_t nValue, char32_t* pBuffer, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* I64toa(int64_t nValue, wchar_t* pBuffer, int nBase);
+#endif
+
+
+/// U64toa
+///
+/// Buffer must hold at least kUint64MinCapacity (21) chars.
+///
+EASTDC_API char8_t*  U64toa(uint64_t nValue, char8_t*  pBuffer, int nBase);
+EASTDC_API char16_t* U64toa(uint64_t nValue, char16_t* pBuffer, int nBase);
+EASTDC_API char32_t* U64toa(uint64_t nValue, char32_t* pBuffer, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* U64toa(uint64_t nValue, wchar_t* pBuffer, int nBase);
+#endif
+
+
+/// Strtod
+///
+/// Converts pString to a floating point value.
+/// The function first discards leading whitespace chars until the first 
+/// non-whitespace char is found. It then takes as many chars as possible 
+/// that are valid in the representation of a floating point literal and 
+/// interprets them as a floating point double. Any remaining chars
+/// are ignored. 
+/// 
+/// A valid floating point value is formed by a succession of:
+///    - An minus sign or optional plus sign.
+///    - A sequence of decimal digits, optionally containing a decimal-point character.
+///    - An optional exponent which consists of an 'e' or 'E' char followed 
+///      by a minus sign or optional plus sign and a sequence of decimal digits.
+///
+/// If successful, the function returns the resulting floating point double.
+/// If no valid conversion could be performed or if the correct value would 
+/// be too small to be representable, a value of zero is returned.
+/// If the correct value is positively or negatively beyond of the range of 
+/// representable values, a positive or negative HUGE_VAL is returned.
+///
+/// If the first sequence of non-whitespace chars in pString does not 
+/// form a valid floating-point number or is empty or only whitespace
+/// no conversion is done and a value of zero is returned.
+///
+/// This function has the same effect as Strtod(pString, NULL);
+///
+EASTDC_API double Strtod(const char8_t*  pString, char8_t**  ppStringEnd);
+EASTDC_API double Strtod(const char16_t* pString, char16_t** ppStringEnd);
+EASTDC_API double Strtod(const char32_t* pString, char32_t** ppStringEnd);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API double Strtod(const wchar_t* pString, wchar_t** ppStringEnd);
+#endif
+
+EASTDC_API float StrtoF32(const char8_t*  pString, char8_t**  ppStringEnd);
+EASTDC_API float StrtoF32(const char16_t* pString, char16_t** ppStringEnd);
+EASTDC_API float StrtoF32(const char32_t* pString, char32_t** ppStringEnd);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API float StrtoF32(const wchar_t* pString, wchar_t** ppStringEnd);
+#endif
+
+/// StrtodEnglish
+///
+/// Implements a version of Strtod that interprets numbers 
+/// as English regardless of what the C runtime library language
+/// setting is. This is useful for being able to interpret numbers
+/// in a constant way with respect to decimal point usage, etc.
+///
+EASTDC_API double StrtodEnglish(const char8_t*  pString, char8_t**  ppStringEnd);
+EASTDC_API double StrtodEnglish(const char16_t* pString, char16_t** ppStringEnd);
+EASTDC_API double StrtodEnglish(const char32_t* pString, char32_t** ppStringEnd);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API double StrtodEnglish(const wchar_t* pString, wchar_t** ppStringEnd);
+#endif
+
+EASTDC_API float  StrtoF32English(const char8_t*  pString, char8_t**  ppStringEnd);
+EASTDC_API float  StrtoF32English(const char16_t* pString, char16_t** ppStringEnd);
+EASTDC_API float  StrtoF32English(const char32_t* pString, char32_t** ppStringEnd);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API float  StrtoF32English(const wchar_t* pString, wchar_t** ppStringEnd);
+#endif
+
+
+/// StrtoI32
+///
+/// It is similar to the C strtol function.
+///
+EASTDC_API int32_t StrtoI32(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+EASTDC_API int32_t StrtoI32(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+EASTDC_API int32_t StrtoI32(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int32_t StrtoI32(const wchar_t* pString, wchar_t** ppStringEnd, int nBase);
+#endif
+
+
+/// StrtoU32
+///
+/// It is similar to the C strtoul function.
+///
+EASTDC_API uint32_t StrtoU32(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+EASTDC_API uint32_t StrtoU32(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+EASTDC_API uint32_t StrtoU32(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API uint32_t StrtoU32(const wchar_t* pString, wchar_t** ppStringEnd, int nBase);
+#endif
+
+
+/// StrtoI64
+///
+/// It is similar to the C strtoll function.
+///
+/// StrtoI64 expects pString to point to a string of the following form:
+///    [whitespace] [{+ | -}] [0 [{ x | X }]] [digits]
+///
+/// A whitespace may consist of space and tab characters, which are ignored; digits 
+/// are one or more decimal digits. The first character that does not fit this form 
+/// stops the scan. If base is between 2 and 36, then it is used as the base of the number. 
+/// If nBase is 0, the initial characters of the string pointed to by pString are used 
+/// to determine the base. If the first character is 0 and the second character 
+/// is not 'x' or 'X', the string is interpreted as an octal integer; otherwise, 
+/// it is interpreted as a decimal number. If the first character is '0' and the 
+/// second character is 'x' or 'X', the string is interpreted as a hexadecimal integer. 
+/// If the first character is '1' through '9', the string is interpreted as a decimal integer. 
+/// The letters 'a' through 'z' (or 'A' through 'Z') are assigned the values 10 
+/// through 35; only letters whose assigned values are less than base are permitted. 
+/// StrtoI64 allows a plus (+) or minus (-) sign prefix; a leading minus sign indicates 
+/// that the return value is negated.
+///
+EASTDC_API int64_t StrtoI64(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+EASTDC_API int64_t StrtoI64(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+EASTDC_API int64_t StrtoI64(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int64_t StrtoI64(const wchar_t* pString, wchar_t** ppStringEnd, int nBase);
+#endif
+
+
+/// StrtoU64
+///
+/// It is similar to the C strtoull function.
+///
+EASTDC_API uint64_t StrtoU64(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+EASTDC_API uint64_t StrtoU64(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+EASTDC_API uint64_t StrtoU64(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API uint64_t StrtoU64(const wchar_t* pString, wchar_t** ppStringEnd, int nBase);
+#endif
+
+
+/// AtoI32
+///
+/// This function has the same effect as StrtoI32(pString, NULL, 10);
+/// It is similar to the C atoll function.
+/// 
+EASTDC_API int32_t AtoI32(const char8_t*  pString);
+EASTDC_API int32_t AtoI32(const char16_t* pString);
+EASTDC_API int32_t AtoI32(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int32_t AtoI32(const wchar_t* pString);
+#endif
+
+
+/// AtoU32
+///
+/// This function has the same effect as StrtoU32(pString, NULL, 10);
+/// It is similar to the C atoul function.
+/// 
+EASTDC_API uint32_t AtoU32(const char8_t*  pString);
+EASTDC_API uint32_t AtoU32(const char16_t* pString);
+EASTDC_API uint32_t AtoU32(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API uint32_t AtoU32(const wchar_t* pString);
+#endif
+
+/// AtoI64
+///
+/// This function has the same effect as StrtoI64(pString, NULL, 10);
+/// It is similar to the C atoll function.
+/// 
+EASTDC_API int64_t AtoI64(const char8_t*  pString);
+EASTDC_API int64_t AtoI64(const char16_t* pString);
+EASTDC_API int64_t AtoI64(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API int64_t AtoI64(const wchar_t* pString);
+#endif
+
+
+/// AtoU64
+///
+/// This function has the same effect as StrtoU64(pString, NULL, 10);
+/// It is similar to the C atoull function.
+/// 
+EASTDC_API uint64_t AtoU64(const char8_t*  pString);
+EASTDC_API uint64_t AtoU64(const char16_t* pString);
+EASTDC_API uint64_t AtoU64(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API uint64_t AtoU64(const wchar_t* pString);
+#endif
+
+
+
+/// Atof
+///
+/// This function has the same effect as Strtod(pString, NULL);
+/// It is similar to the C strtod function.
+/// 
+/// Converts pString to a floating point value.
+/// The function first discards leading whitespace chars until the first 
+/// non-whitespace char is found. It then takes as many chars as possible 
+/// that are valid in the representation of a floating point literal and 
+/// interprets them as a floating point double. Any remaining chars
+/// are ignored. Use the Strtod function if you want to find out where 
+/// the remaining chars begin.
+/// 
+/// A valid floating point value is formed by a succession of:
+///    - An minus sign or optional plus sign.
+///    - A sequence of decimal digits, optionally containing a decimal-point character.
+///    - An optional exponent which consists of an 'e' or 'E' char followed 
+///      by a minus sign or optional plus sign and a sequence of decimal digits.
+///
+/// If successful, the function returns the resulting floating point double.
+/// If no valid conversion could be performed or if the correct value would 
+/// be too small to be representable, a value of zero is returned.
+/// If the correct value is positively or negatively beyond of the range of 
+/// representable values, a positive or negative HUGE_VAL is returned.
+///
+/// If the first sequence of non-whitespace chars in pString does not 
+/// form a valid floating-point number or is empty or only whitespace
+/// no conversion is done and a value of zero is returned.
+///
+/// This function has the same effect as Strtod(pString, NULL);
+///
+EASTDC_API double Atof(const char8_t*  pString);
+EASTDC_API double Atof(const char16_t* pString);
+EASTDC_API double Atof(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API double Atof(const wchar_t* pString);
+#endif
+
+EASTDC_API float  AtoF32(const char8_t*  pString);
+EASTDC_API float  AtoF32(const char16_t* pString);
+EASTDC_API float  AtoF32(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API float  AtoF32(const wchar_t* pString);
+#endif
+
+
+/// AtofEnglish
+///
+/// This function has the same effect as Strtod(pString, NULL);
+/// It is similar to the C strtod function.
+/// 
+/// Implements a version of Atof that interprets numbers as English regardless of 
+/// what the C runtime library language setting is. This is useful for being able to 
+/// interpret numbers in a constant way with respect to decimal point usage, etc.
+/// 
+EASTDC_API double AtofEnglish(const char8_t*  pString);
+EASTDC_API double AtofEnglish(const char16_t* pString);
+EASTDC_API double AtofEnglish(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API double AtofEnglish(const wchar_t* pString);
+#endif
+
+EASTDC_API float  AtoF32English(const char8_t*  pString);
+EASTDC_API float  AtoF32English(const char16_t* pString);
+EASTDC_API float  AtoF32English(const char32_t* pString);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API float  AtoF32English(const wchar_t* pString);
+#endif
+
+
+/// Ftoa
+///
+/// Same as FtoaEnglish.
+/// Similar but not identical to the non-standard but sometimes seen C ftoa function 
+/// except it assumes English language numeric representation (e.g. decimal point 
+/// represented by a period).
+/// 
+/// The nResultCapacity argument is number of characters in the destination, 
+/// including the char that will hold the terminating 0 char.
+///
+/// The precision argument is the number of digits to return after the 
+/// decimal point, if there is one. Trailing zeroes after a decimal point 
+/// aren't added to the string, even if you request them.
+/// 
+/// pResult should generally have at least 32 characters to deal with all 
+/// floating point values.
+///
+EASTDC_API char8_t*  Ftoa(double dValue, char8_t*  pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+EASTDC_API char16_t* Ftoa(double dValue, char16_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+EASTDC_API char32_t* Ftoa(double dValue, char32_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* Ftoa(double dValue, wchar_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+#endif
+
+
+/// FtoaEnglish
+///
+/// Implements a version of ftoa which interprets numbers as English regardless of 
+/// what the C runtime library language setting is. This is useful for being able to 
+/// interpret numbers in a constant way with respect to decimal point usage, etc.
+/// This function will often be significantly faster than the C ftoa function.
+/// 
+/// The nResultCapacity argument is number of characters in the destination, 
+/// including the char that will hold the terminating 0 char.
+///
+/// The precision argument is the number of digits to return after the 
+/// decimal point, if there is one. Trailing zeroes after a decimal point 
+/// aren't added to the string, even if you request them.
+///
+/// Returns pResult if there was enough space in the input buffer to hold the 
+/// resulting string. If the return value is NULL, pResult will hold the
+/// numerical string value built left-to-right before space was exhausted. 
+/// 
+/// pResult should generally have at least 32 characters to deal with all 
+/// floating point values.
+///
+/// Examples:
+///     FtoaEnglish(10000, pResult, 1, 0, false);
+///     returns NULL
+///
+///     FtoaEnglish(10000, pResult, 10, 0, false);
+///     returns pResult and "10000"
+///
+///     FtoaEnglish(10000.003, pResult, 10, 0, false);
+///     returns pResult and "10000"
+///
+///     FtoaEnglish(10000.003, pResult, 6, 10, false);
+///     returns NULL
+///
+///     FtoaEnglish(10000.12345, pResult, 20, 3, false);
+///     returns pResult and "10000.123"
+///
+///     FtoaEnglish(10000.12348, pResult, 20, 8, false);
+///     returns pResult and "10000.12348"
+///
+///     FtoaEnglish(.12345, pResult, 20, 2, false);
+///     returns pResult and "0.12"
+///
+///     FtoaEnglish(.123450000, pResult, 20, 20, false);
+///     returns pResult and "0.12345"
+///
+EASTDC_API char8_t*  FtoaEnglish(double dValue, char8_t*  pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+EASTDC_API char16_t* FtoaEnglish(double dValue, char16_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+EASTDC_API char32_t* FtoaEnglish(double dValue, char32_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API wchar_t* FtoaEnglish(double dValue, wchar_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled);
+#endif
+
+
+/// ReduceFloatString
+///
+/// This function removes unnecessary floating point digits from a string.
+/// It is useful for the creation of minumum size floating point strings for  
+/// the case of reducing memory requirements of floating point representation. 
+///
+/// The input 'nLength' is the length of the string as would be returned by Strlen, 
+/// but it can be less. If nLength is (size_t)-1, then pString must be 0-terminated.
+/// The return value is the new Strlen of the string. 
+///
+/// Given a floating point number string like this:
+///    "3.000000"
+/// There is no reason this can't simply be represented as this:
+///    "3"
+///
+/// This function determines of such a shortening can take place, and if so, 
+/// it modifies the buffer (if needed) and returns the appropriate length. 
+/// There are other conditions that we need to test for, such as numbers like this:
+///    "2.3400"          -> "2.34"
+///    "00000"           -> "0"
+///    ".0000"           -> "0"
+///    "000.0000"        -> "0"
+///    "-0.0000"         -> "-0"
+///    "23.430000e10"    -> "23.43e10"
+///
+/// We also deal with valid numbers like this:
+///    "2.34001"
+///    "0.00001"
+///    "-0.00001"
+///    "23.43e10"
+///    "15e-0"
+///
+EASTDC_API size_t ReduceFloatString(char8_t*  pString, size_t nLength = kSizeTypeUnset);
+EASTDC_API size_t ReduceFloatString(char16_t* pString, size_t nLength = kSizeTypeUnset);
+EASTDC_API size_t ReduceFloatString(char32_t* pString, size_t nLength = kSizeTypeUnset);
+#if EA_WCHAR_UNIQUE
+	EASTDC_API size_t ReduceFloatString(wchar_t* pString, size_t nLength = kSizeTypeUnset);
+#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Inlines
+///////////////////////////////////////////////////////////////////////////////
+
+namespace EA
+{
+namespace StdC
+{
+
+	inline EASTDC_API int32_t AtoI32(const char8_t* pString)
+	{
+		return StrtoI32(pString, NULL, 10);
+	}
+
+	inline EASTDC_API int32_t AtoI32(const char16_t* pString)
+	{
+		return StrtoI32(pString, NULL, 10);
+	}
+
+	inline EASTDC_API int32_t AtoI32(const char32_t* pString)
+	{
+		return StrtoI32(pString, NULL, 10);
+	}
+
+
+
+	inline EASTDC_API uint32_t AtoU32(const char8_t* pString)
+	{
+		return StrtoU32(pString, NULL, 10);
+	}
+
+	inline EASTDC_API uint32_t AtoU32(const char16_t* pString)
+	{
+		return StrtoU32(pString, NULL, 10);
+	}
+
+	inline EASTDC_API uint32_t AtoU32(const char32_t* pString)
+	{
+		return StrtoU32(pString, NULL, 10);
+	}
+
+
+
+	inline EASTDC_API int64_t AtoI64(const char8_t* pString)
+	{
+		return StrtoI64(pString, NULL, 10);
+	}
+
+	inline EASTDC_API int64_t AtoI64(const char16_t* pString)
+	{
+		return StrtoI64(pString, NULL, 10);
+	}
+
+	inline EASTDC_API int64_t AtoI64(const char32_t* pString)
+	{
+		return StrtoI64(pString, NULL, 10);
+	}
+
+
+
+	inline EASTDC_API uint64_t AtoU64(const char8_t*  pString)
+	{
+		return StrtoU64(pString, NULL, 10);
+	}
+
+	inline EASTDC_API uint64_t AtoU64(const char16_t* pString)
+	{
+		return StrtoU64(pString, NULL, 10);
+	}
+
+	inline EASTDC_API uint64_t AtoU64(const char32_t* pString)
+	{
+		return StrtoU64(pString, NULL, 10);
+	}
+
+
+
+	inline EASTDC_API double Strtod(const char8_t* pString, char8_t** ppStringEnd)
+	{
+		return strtod(pString, ppStringEnd); // strtod is well-implemented by most standard libraries.
+	}
+
+	inline EASTDC_API double Strtod(const char16_t* pString, char16_t** ppStringEnd)
+	{
+		#if (EA_WCHAR_SIZE == 2) && defined(EA_HAVE_WCHAR_IMPL)
+			return wcstod(reinterpret_cast<const wchar_t*>(pString), reinterpret_cast<wchar_t**>(reinterpret_cast<void*>(ppStringEnd)));
+		#else
+			// EA_PLATFORM_PLAYSTATION2, __CYGWIN__
+			// These platforms don't have wide string support.
+
+			// EA_PLATFORM_UNIX
+			// Unix defines wchar_t to be 32 bit. We compile under GCC/Unix with the -fshort-wchar argument
+			// which makes wchar_t be 16 bit. But the GCC standard library linked in is still the one with 
+			// 32 bit wchar_t. So we can't call wcstod here as it will treat our char16_t string as a 32 bit string.
+			// It's possible that the user could recompile libc to use 16 bit strings, but that's a pain for most users.
+			char8_t  buffer8[64];
+			char8_t* p    = buffer8;
+			char8_t* pEnd = buffer8 + 63; 
+
+			while(p != pEnd)
+			{   //             '+'                  'z'
+				if((*pString < 0x2b) || (*pString > 0x7a)) // This includes a '\0' check.
+					break;
+				*p++ = (char8_t)(int16_t)(uint16_t)*pString++;
+			}
+			*p = 0;
+
+			double d = strtod(buffer8, &p);
+			if(ppStringEnd)
+				*ppStringEnd = (char16_t*)pString + (uintptr_t)(p - buffer8); // This math should be safe in the presence of multi-byte UTF8 strings because the only chars that can be part of a floating point string are always single bytes.
+
+			return d;
+		#endif
+	}
+
+	inline EASTDC_API double Strtod(const char32_t* pString, char32_t** ppStringEnd)
+	{
+		#if (EA_WCHAR_SIZE == 4) && defined(EA_HAVE_WCHAR_IMPL)
+			return wcstod(reinterpret_cast<const wchar_t*>(pString), reinterpret_cast<wchar_t**>(ppStringEnd));
+		#else
+			// We convert from char32_t to char8_t and convert that.
+			char8_t  buffer8[64]; buffer8[0] = 0;
+			char8_t* p    = buffer8;
+			char8_t* pEnd = buffer8 + 63; 
+
+			while(p != pEnd)
+			{   //             '+'                  'z'
+				if((*pString < 0x2b) || (*pString > 0x7a)) // This includes a '\0' check.
+					break;
+				*p++ = (char8_t)(int32_t)(uint32_t)*pString++;
+			}
+			*p = 0;
+
+			double d = strtod(buffer8, &p);
+			if(ppStringEnd)
+				*ppStringEnd = (char32_t*)pString + (uintptr_t)(p - buffer8); // This math should be safe in the presence of multi-byte UTF8 strings because the only chars that can be part of a floating point string are always single bytes.
+
+			return d;
+		#endif
+	}
+
+
+
+	inline EASTDC_API float StrtoF32(const char8_t* pString, char8_t** ppStringEnd)
+	{
+		return (float)strtod(pString, ppStringEnd); // strtod is well-implemented by most standard libraries.
+	}
+
+	inline EASTDC_API float StrtoF32(const char16_t* pString, char16_t** ppStringEnd)
+	{
+		return (float)Strtod(pString, ppStringEnd);
+	}
+
+
+	inline EASTDC_API float StrtoF32(const char32_t* pString, char32_t** ppStringEnd)
+	{
+		return (float)Strtod(pString, ppStringEnd);
+	}
+
+
+
+	inline EASTDC_API float StrtoF32English(const char8_t*  pString, char8_t** ppStringEnd)
+	{
+		return (float)StrtodEnglish(pString, ppStringEnd);
+	}
+
+	inline EASTDC_API float StrtoF32English(const char16_t* pString, char16_t** ppStringEnd)
+	{
+		return (float)StrtodEnglish(pString, ppStringEnd);
+	}
+
+	inline EASTDC_API float StrtoF32English(const char32_t* pString, char32_t** ppStringEnd)
+	{
+		return (float)StrtodEnglish(pString, ppStringEnd);
+	}
+
+
+
+	inline EASTDC_API double Atof(const char8_t* pString)
+	{
+		return atof(pString); // atof is well-implemented by most standard libraries.
+	}
+
+	inline EASTDC_API double Atof(const char16_t* pString)
+	{
+		return Strtod(pString, NULL);
+	}
+
+	inline EASTDC_API double Atof(const char32_t* pString)
+	{
+		return Strtod(pString, NULL);
+	}
+
+
+
+	inline EASTDC_API float AtoF32(const char8_t* pString)
+	{
+		return (float)atof(pString); // atof is well-implemented by most standard libraries.
+	}
+
+	inline EASTDC_API float AtoF32(const char16_t* pString)
+	{
+		return (float)Strtod(pString, NULL);
+	}
+
+	inline EASTDC_API float AtoF32(const char32_t* pString)
+	{
+		return (float)Strtod(pString, NULL);
+	}
+
+
+
+	inline EASTDC_API double AtofEnglish(const char8_t* pString)
+	{
+		return StrtodEnglish(pString, NULL);
+	}
+
+	inline EASTDC_API double AtofEnglish(const char16_t* pString)
+	{
+		return StrtodEnglish(pString, NULL);
+	}
+
+	inline EASTDC_API double AtofEnglish(const char32_t* pString)
+	{
+		return StrtodEnglish(pString, NULL);
+	}
+
+
+
+	inline EASTDC_API float AtoF32English(const char8_t*  pString)
+	{
+		return (float)StrtodEnglish(pString, NULL);
+	}
+
+	inline EASTDC_API float AtoF32English(const char16_t* pString)
+	{
+		return (float)StrtodEnglish(pString, NULL);
+	}
+
+	inline EASTDC_API float AtoF32English(const char32_t* pString)
+	{
+		return (float)StrtodEnglish(pString, NULL);
+	}
+
+
+
+	inline EASTDC_API char8_t* Ftoa(double dValue, char8_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+	{
+		// An implementation which calls sprintf might be a better idea.
+		return FtoaEnglish(dValue, pResult, nResultCapacity, nPrecision, bExponentEnabled);
+	}
+
+	inline EASTDC_API char16_t* Ftoa(double dValue, char16_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+	{
+		// An implementation which calls sprintf might be a better idea.
+		return FtoaEnglish(dValue, pResult, nResultCapacity, nPrecision, bExponentEnabled);
+	}
+
+	inline EASTDC_API char32_t* Ftoa(double dValue, char32_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+	{
+		// An implementation which calls sprintf might be a better idea.
+		return FtoaEnglish(dValue, pResult, nResultCapacity, nPrecision, bExponentEnabled);
+	}
+
+
+
+#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+
+	#ifdef EASTDC_UNICODE_CHAR_PTR_PTR_CAST
+	#undef EASTDC_UNICODE_CHAR_PTR_PTR_CAST
+	#endif
+
+	#ifdef EASTDC_UNICODE_CONST_CHAR_PTR_CAST
+	#undef EASTDC_UNICODE_CONST_CHAR_PTR_CAST
+	#endif
+
+	#ifdef EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST
+	#undef EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST
+	#endif
+
+	#ifdef EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST
+	#undef EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST
+	#endif
+
+	#ifdef EASTDC_UNICODE_CHAR_PTR_CAST
+	#undef EASTDC_UNICODE_CHAR_PTR_CAST
+	#endif
+
+	#ifdef EASTDC_UNICODE_CHAR_CAST
+	#undef EASTDC_UNICODE_CHAR_CAST
+	#endif
+
+	#if (EA_WCHAR_SIZE == 2)
+		#define EASTDC_UNICODE_CHAR_PTR_PTR_CAST(x)       reinterpret_cast<char16_t **>(reinterpret_cast<void*>(x))
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_CAST(x)     reinterpret_cast<const char16_t *>(x)
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(x) reinterpret_cast<const char16_t **>(x)
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(x) reinterpret_cast<const char16_t *&>(x)
+		#define EASTDC_UNICODE_CHAR_PTR_CAST(x)           reinterpret_cast<char16_t *>(x)
+		#define EASTDC_UNICODE_CHAR_CAST(x)               char16_t(x)
+	#else
+		#define EASTDC_UNICODE_CHAR_PTR_PTR_CAST(x)       reinterpret_cast<char32_t **>(x)
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_CAST(x)     reinterpret_cast<const char32_t *>(x)
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(x) reinterpret_cast<const char32_t **>(x)
+		#define EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(x) reinterpret_cast<const char32_t *&>(x)
+		#define EASTDC_UNICODE_CHAR_PTR_CAST(x)           reinterpret_cast<char32_t *>(x)
+		#define EASTDC_UNICODE_CHAR_CAST(x)               char32_t(x)
+	#endif
+
+
+
+	inline size_t Strlen(const wchar_t* pString)
+	{
+		return Strlen(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+	inline wchar_t* Strend(const wchar_t* pString)
+	{
+		return reinterpret_cast<wchar_t *>(Strend(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString)));
+	}
+
+	inline wchar_t* Strcpy(wchar_t* pDestination, const wchar_t* pSource)
+	{
+		return reinterpret_cast<wchar_t *>(Strcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource)));
+	}
+
+	inline wchar_t* Strncpy(wchar_t* pDestination, const wchar_t* pSource, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(Strncpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), n));
+	}
+
+	inline wchar_t* StringnCopy(wchar_t* pDestination, const wchar_t* pSource, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(StringnCopy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), n));
+	}
+
+	inline size_t Strlcpy(wchar_t* pDestination, const wchar_t* pSource, size_t nDestCapacity)
+	{
+		return Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity);
+	}
+
+	inline int Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		return static_cast<int>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength));
+	}
+
+	inline int Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			return static_cast<int>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity));
+		#else
+			return static_cast<int>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength));
+		#endif
+	}
+
+	inline int Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return static_cast<int>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength));
+		#else
+			EA_UNUSED(nSourceLength);
+			return static_cast<int>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity));
+		#endif
+	}
+	
+	inline int Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		return static_cast<int>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength));
+	}
+
+	inline int Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			return static_cast<int>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity));
+		#else
+			return static_cast<int>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength));
+		#endif
+	}
+
+	inline int Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return static_cast<int>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength));
+		#else
+			EA_UNUSED(nSourceLength);
+			return static_cast<int>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity));
+		#endif
+	}
+
+	inline bool Strlcpy(char8_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		return Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+	}
+
+	inline bool Strlcpy(char16_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			nDestUsed = nSourceUsed = static_cast<size_t>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity));
+			return true;
+		#else
+			return Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+		#endif
+	}
+
+	inline bool Strlcpy(char32_t* pDestination, const wchar_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+		#else
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			nDestUsed = nSourceUsed = static_cast<size_t>(Strlcpy(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity));
+			return true;
+		#endif
+	}
+
+	inline bool Strlcpy(wchar_t* pDestination, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		return Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+	}
+
+	inline bool Strlcpy(wchar_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			nDestUsed = nSourceUsed = static_cast<size_t>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity));
+			return true;
+		#else
+			return Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+		#endif
+	}
+
+	inline bool Strlcpy(wchar_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+		#else
+			EA_UNUSED(nSourceLength); // To do: This pathway is broken in that this version of Strlcpy allows a source length to be specified, whereas this pathway doesn't use it.
+			nDestUsed = nSourceUsed = static_cast<size_t>(Strlcpy(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity));
+			return true;
+		#endif
+	}
+
+	inline wchar_t* Strcat(wchar_t* pDestination, const wchar_t* pSource)
+	{
+		return reinterpret_cast<wchar_t *>(Strcat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource)));
+	}
+
+	inline wchar_t* Strncat(wchar_t* pDestination, const wchar_t* pSource, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(Strncat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), n));
+	}
+
+	inline wchar_t* StringnCat(wchar_t* pDestination, const wchar_t* pSource, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(StringnCat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), n));
+	}
+
+	inline size_t Strlcat(wchar_t* pDestination, const wchar_t* pSource, size_t nDestCapacity)
+	{
+		return Strlcat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity);
+	}
+
+	inline size_t Strlcat(wchar_t* pDestination, const char8_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity);
+	}
+
+	inline size_t Strlcat(wchar_t* pDestination, const char16_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity);
+	}
+
+	inline size_t Strlcat(wchar_t* pDestination, const char32_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), pSource, nDestCapacity);
+	}
+
+	inline size_t Strlcat(char8_t* pDestination, const wchar_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity);
+	}
+
+	inline size_t Strlcat(char16_t* pDestination, const wchar_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity);
+	}
+		
+	inline size_t Strlcat(char32_t* pDestination, const wchar_t*  pSource, size_t nDestCapacity)
+	{
+		return Strlcat(pDestination, EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nDestCapacity);
+	}
+
+	inline size_t Strxfrm(wchar_t* pDest, const wchar_t* pSource, size_t n)
+	{
+		return Strxfrm(EASTDC_UNICODE_CHAR_PTR_CAST(pDest), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), n);
+	}
+
+	inline wchar_t* Strdup(const wchar_t* pString)
+	{
+		return reinterpret_cast<wchar_t *>(Strdup(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString)));
+	}
+
+	inline void Strdel(wchar_t* pString)
+	{
+		Strdel(EASTDC_UNICODE_CHAR_PTR_CAST(pString));
+	}
+
+	inline wchar_t* Strupr(wchar_t* pString)
+	{
+		return reinterpret_cast<wchar_t *>(Strupr(EASTDC_UNICODE_CHAR_PTR_CAST(pString)));
+	}
+
+	inline wchar_t* Strlwr(wchar_t* pString)
+	{
+		return reinterpret_cast<wchar_t *>(Strlwr(EASTDC_UNICODE_CHAR_PTR_CAST(pString)));
+	}
+
+	inline wchar_t* Strmix(wchar_t* pDestination, const wchar_t* pSource, const wchar_t* pDelimiters)
+	{
+		return reinterpret_cast<wchar_t *>(Strmix(EASTDC_UNICODE_CHAR_PTR_CAST(pDestination), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pDelimiters)));
+	}
+
+	inline wchar_t* Strchr(const wchar_t* pString, wchar_t c)
+	{
+		return reinterpret_cast<wchar_t *>(Strchr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_CAST(c)));
+	}
+
+	inline wchar_t* Strnchr(const wchar_t* pString, wchar_t c, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(Strnchr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_CAST(c), n));
+	}
+
+	inline size_t  Strcspn(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return Strcspn(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2));
+	}
+
+	inline wchar_t* Strpbrk(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return reinterpret_cast<wchar_t *>(Strpbrk(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2)));
+	}
+
+	inline wchar_t* Strrchr(const wchar_t* pString, wchar_t c)
+	{
+		return reinterpret_cast<wchar_t *>(Strrchr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_CAST(c)));
+	}
+
+	inline size_t Strspn(const wchar_t* pString, const wchar_t* pSubString)
+	{
+		return Strspn(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSubString));
+	}
+
+	inline wchar_t* Strstr(const wchar_t* pString, const wchar_t* pSubString)
+	{
+		return reinterpret_cast<wchar_t *>(Strstr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSubString)));
+	}
+
+	inline wchar_t* Stristr(const wchar_t* pString, const wchar_t* pSubString)
+	{
+		return reinterpret_cast<wchar_t *>(Stristr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSubString)));
+	}
+
+	inline wchar_t* Strrstr(const wchar_t* pString, const wchar_t* pSubString)
+	{
+		return reinterpret_cast<wchar_t *>(Strrstr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSubString)));
+	}
+
+	inline wchar_t* Strirstr(const wchar_t* pString, const wchar_t* pSubString)
+	{
+		return reinterpret_cast<wchar_t *>(Strirstr(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSubString)));
+	}
+
+	inline bool Strstart(const wchar_t* pString, const wchar_t* pPrefix)
+	{
+		return Strstart(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPrefix));
+	}
+
+	inline bool Stristart(const wchar_t* pString, const wchar_t* pPrefix)
+	{
+		return Stristart(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPrefix));
+	}
+
+	inline bool Strend(const wchar_t* pString, const wchar_t* pSuffix, size_t stringLength = kSizeTypeUnset)
+	{
+		return Strend(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSuffix), stringLength);
+	}
+
+	inline bool Striend(const wchar_t* pString, const wchar_t* pSuffix, size_t stringLength = kSizeTypeUnset)
+	{
+		return Striend(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSuffix), stringLength);
+	}
+
+	inline wchar_t* Strtok(wchar_t* pString, const wchar_t* pDelimiters, wchar_t** pContext)
+	{
+		return reinterpret_cast<wchar_t *>(Strtok(EASTDC_UNICODE_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pDelimiters), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(pContext)));
+	}
+
+	inline const wchar_t* Strtok2(const wchar_t* pString, const wchar_t* pDelimiters, size_t* pResultLength, bool bFirst)
+	{
+		return reinterpret_cast<const wchar_t *>(Strtok2(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pDelimiters), pResultLength, bFirst));
+	}
+
+	inline wchar_t* Strset(wchar_t* pString, wchar_t c)
+	{
+		return reinterpret_cast<wchar_t *>(Strset(EASTDC_UNICODE_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_CAST(c)));
+	}
+
+	inline wchar_t* Strnset(wchar_t* pString, wchar_t c, size_t n)
+	{
+		return reinterpret_cast<wchar_t *>(Strnset(EASTDC_UNICODE_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_CAST(c), n));
+	}
+
+	inline wchar_t* Strrev(wchar_t* pString)
+	{
+		return reinterpret_cast<wchar_t *>(Strrev(EASTDC_UNICODE_CHAR_PTR_CAST(pString)));
+	}
+
+	inline int Strcmp(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return Strcmp(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2));
+	}
+
+	inline int Strncmp(const wchar_t* pString1, const wchar_t* pString2, size_t n)
+	{
+		return Strncmp(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), n);
+	}
+
+	inline int Stricmp(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return Stricmp(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2));
+	}
+
+	inline int Strnicmp(const wchar_t* pString1, const wchar_t* pString2, size_t n)
+	{
+		return Strnicmp(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), n);
+	}
+
+	inline int StrcmpNumeric (const wchar_t* pString1, const wchar_t* pString2, size_t length1, size_t length2, wchar_t decimal, wchar_t thousandsSeparator)
+	{
+		return StrcmpNumeric(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), length1, length2, EASTDC_UNICODE_CHAR_CAST(decimal), EASTDC_UNICODE_CHAR_CAST(thousandsSeparator));
+	}
+
+	inline int StricmpNumeric (const wchar_t* pString1, const wchar_t* pString2, size_t length1, size_t length2, wchar_t decimal, wchar_t thousandsSeparator)
+	{
+		return StricmpNumeric(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), length1, length2, EASTDC_UNICODE_CHAR_CAST(decimal), EASTDC_UNICODE_CHAR_CAST(thousandsSeparator));
+	}
+
+	inline int Strcoll(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return Strcoll(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2));
+	}
+
+	inline int Strncoll(const wchar_t* pString1, const wchar_t* pString2, size_t n)
+	{
+		return Strncoll(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), n);
+	}
+
+	inline int Stricoll(const wchar_t* pString1, const wchar_t* pString2)
+	{
+		return Stricoll(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2));
+	}
+
+	inline int Strnicoll(const wchar_t* pString1, const wchar_t* pString2, size_t n)
+	{
+		return Strnicoll(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString1), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString2), n);
+	}
+
+	inline wchar_t* EcvtBuf(double dValue, int nDigitCount, int* pDecimalPos, int* pSign, wchar_t* pBuffer)
+	{
+		return reinterpret_cast<wchar_t *>(EcvtBuf(dValue, nDigitCount, pDecimalPos, pSign, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer)));
+	}
+
+	inline wchar_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* pDecimalPos, int* pSign, wchar_t* pBuffer)
+	{
+		return reinterpret_cast<wchar_t *>(FcvtBuf(dValue, nDigitCountAfterDecimal, pDecimalPos, pSign, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer)));
+	}
+
+	inline wchar_t* I32toa(int32_t nValue, wchar_t* pBuffer, int nBase)
+	{
+		return reinterpret_cast<wchar_t *>(I32toa(nValue, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer), nBase));
+	}
+
+	inline wchar_t* U32toa(uint32_t nValue, wchar_t* pBuffer, int nBase)
+	{
+		return reinterpret_cast<wchar_t *>(U32toa(nValue, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer), nBase));
+	}
+
+	inline wchar_t* I64toa(int64_t nValue, wchar_t* pBuffer, int nBase)
+	{
+		return reinterpret_cast<wchar_t *>(I64toa(nValue, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer), nBase));
+	}
+
+	inline wchar_t* U64toa(uint64_t nValue, wchar_t* pBuffer, int nBase)
+	{
+		return reinterpret_cast<wchar_t *>(U64toa(nValue, EASTDC_UNICODE_CHAR_PTR_CAST(pBuffer), nBase));
+	}
+
+	inline double Strtod(const wchar_t* pString, wchar_t** ppStringEnd)
+	{
+		return Strtod(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd));
+	}
+
+	inline float StrtoF32(const wchar_t* pString, wchar_t** ppStringEnd)
+	{
+		return StrtoF32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd));
+	}
+
+	inline double StrtodEnglish(const wchar_t* pString, wchar_t** ppStringEnd)
+	{
+		return StrtodEnglish(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd));
+	}
+
+	inline float  StrtoF32English(const wchar_t* pString, wchar_t** ppStringEnd)
+	{
+		return StrtoF32English(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd));
+	}
+
+	inline int32_t StrtoI32(const wchar_t* pString, wchar_t** ppStringEnd, int nBase)
+	{
+		return StrtoI32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd), nBase);
+	}
+
+
+	inline uint32_t StrtoU32(const wchar_t* pString, wchar_t** ppStringEnd, int nBase)
+	{
+		return StrtoU32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd), nBase);
+	}
+
+
+	inline int64_t StrtoI64(const wchar_t* pString, wchar_t** ppStringEnd, int nBase)
+	{
+		return StrtoI64(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd), nBase);
+	}
+
+
+	inline uint64_t StrtoU64(const wchar_t* pString, wchar_t** ppStringEnd, int nBase)
+	{
+		return StrtoU64(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CHAR_PTR_PTR_CAST(ppStringEnd), nBase);
+	}
+
+
+	inline int32_t AtoI32(const wchar_t* pString)
+	{
+		return AtoI32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline uint32_t AtoU32(const wchar_t* pString)
+	{
+		return AtoU32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline int64_t AtoI64(const wchar_t* pString)
+	{
+		return AtoI64(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline uint64_t AtoU64(const wchar_t* pString)
+	{
+		return AtoU64(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline double Atof(const wchar_t* pString)
+	{
+		return Atof(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline float  AtoF32(const wchar_t* pString)
+	{
+		return AtoF32(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline double AtofEnglish(const wchar_t* pString)
+	{
+		return AtofEnglish(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline float  AtoF32English(const wchar_t* pString)
+	{
+		return AtoF32English(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString));
+	}
+
+
+	inline wchar_t* Ftoa(double dValue, wchar_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+	{
+		return reinterpret_cast<wchar_t *>(Ftoa(dValue, EASTDC_UNICODE_CHAR_PTR_CAST(pResult), nResultCapacity, nPrecision, bExponentEnabled));
+	}
+
+
+	inline wchar_t* FtoaEnglish(double dValue, wchar_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+	{
+		return reinterpret_cast<wchar_t *>(FtoaEnglish(dValue, EASTDC_UNICODE_CHAR_PTR_CAST(pResult), nResultCapacity, nPrecision, bExponentEnabled));
+	}
+
+
+	inline size_t ReduceFloatString(wchar_t* pString, size_t nLength)
+	{
+		return ReduceFloatString(EASTDC_UNICODE_CHAR_PTR_CAST(pString), nLength);
+	}
+
+#endif
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// Deprecated functionality
+	///////////////////////////////////////////////////////////////////////////
+
+	template <typename Source, typename Dest>  // ConvertString has been renamed to Strlcpy and the template arguments reversed.
+	inline Dest ConvertString(const Source& s){ return Strlcpy<Dest, Source>(s); }
+
+	template <typename Source, typename Dest> // ConvertString has been renamed to Strlcpy and the template arguments reversed.
+	inline bool ConvertString(const Source& s, Dest& d){ return Strlcpy<Dest, Source>(d, s); }
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+#endif // Header include guard

+ 501 - 0
include/EAStdC/EATextUtil.h

@@ -0,0 +1,501 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EATEXTUTIL_H
+#define EASTDC_EATEXTUTIL_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAString.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	/// kLengthNull
+	/// Defines a value to be used for string conversion functions which means
+	/// that the string length is specified by a wherever the terminating null
+	/// character is. For the copying or converting of strings, the terminating
+	/// null character is also copied to the destination.
+	const size_t kLengthNull = (size_t)-1;
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// UTF8 utilities
+	/// 
+	EASTDC_API bool      UTF8Validate(const char8_t* p, size_t nLength);
+	EASTDC_API char8_t*  UTF8Increment(const char8_t* p, size_t n);
+	EASTDC_API char8_t*  UTF8Decrement(const char8_t* p, size_t n);
+	EASTDC_API size_t    UTF8Length(const char8_t* p);
+	EASTDC_API size_t    UTF8Length(const char16_t* p);
+	EASTDC_API size_t    UTF8Length(const char32_t* p);
+	EASTDC_API size_t    UTF8CharSize(const char8_t* p);
+	EASTDC_API size_t    UTF8CharSize(char16_t c);
+	EASTDC_API size_t    UTF8CharSize(char32_t c);
+	EASTDC_API char16_t  UTF8ReadChar(const char8_t* p, const char8_t** ppEnd = NULL);
+	EASTDC_API char8_t*  UTF8WriteChar(char8_t* p, char16_t c);
+	EASTDC_API char8_t*  UTF8WriteChar(char8_t* p, char32_t c);
+	EASTDC_API size_t    UTF8TrimPartialChar(char8_t* p, size_t nLength);
+	EASTDC_API char8_t*  UTF8ReplaceInvalidChar(const char8_t* pIn, size_t nLength, char8_t* pOut, char8_t replaceWith);
+	inline     bool      UTF8IsSoloByte(char8_t c)   { return ((uint8_t)c < 0x80); }
+	inline     bool      UTF8IsLeadByte(char8_t c)   { return (0xc0 <= (uint8_t)c) && ((uint8_t)c <= 0xef); } // This tests for lead bytes for 2 and 3 byte UTF8 sequences, which map to char16_t code points. If we were to support 4, 5, 6 byte code sequences (char32_t code points), we'd test for <= 0xfd.
+	inline     bool      UTF8IsFollowByte(char8_t c) { return (0x80 <= (uint8_t)c) && ((uint8_t)c <= 0xbf); } // This assumes that the char is part of a valid UTF8 sequence.
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// WildcardMatch
+	/// 
+	/// These functions match source strings to wildcard patterns like those used
+	/// in file specifications. '*' in the pattern means match zero or more 
+	/// consecutive source characters. '?' in the pattern means match exactly one
+	/// source character. Here are some examples:
+	///
+	///      Source            Pattern          Result
+	///      -------------------------------------------
+	///      abcde             *e                true
+	///      abcde             *f                false
+	///      abcde             ???de             true
+	///      abcde             ????g             false
+	///      abcde             *c??              true
+	///      abcde             *e??              false
+	///      abcde             *????             true
+	///      abcde             bcdef             false
+	///      abcde             *?????            true
+	///
+	/// Multiple * and ? characters may be used. Two consecutive * characters are 
+	/// treated as if they were one.
+	///
+	EASTDC_API bool WildcardMatch(const char8_t*  pString, const char8_t*  pPattern, bool bCaseSensitive);
+	EASTDC_API bool WildcardMatch(const char16_t* pString, const char16_t* pPattern, bool bCaseSensitive);
+	EASTDC_API bool WildcardMatch(const char32_t* pString, const char32_t* pPattern, bool bCaseSensitive);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline bool WildcardMatch(const wchar_t* pString, const wchar_t* pPattern, bool bCaseSensitive)
+			{ return WildcardMatch(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPattern), bCaseSensitive); }
+	#endif
+
+
+	////////////////////////////////////////////////////////////////////////////////
+	/// ParseDelimitedText
+	///
+	/// Given a line of text (e.g. like this:)
+	///     342.5, "This is a string", test, "This is a string, with a comma"
+	/// This function parses it into separate fields (e.g. like this:)
+	///     342.5
+	///     This is a string
+	///     test
+	///     This is a string, with a comma
+	/// This function lets you dynamically specify the delimiter. The delimiter
+	/// can be any char (e.g. space, tab, semicolon) except the quote char
+	/// itself, which is reserved for the purpose of grouping. See the source
+	/// code comments for more details. However, in the case of text that is
+	/// UTF8-encoded, you need to make sure the delimiter char is a value
+	/// that is less than 127, so as not to collide with UTF8 encoded chars.
+	///
+	/// The input is a pointer to text and the text length. For ASCII, MBCS, and
+	/// UTF8, this is the number of bytes or chars. For UTF16 (Unicode) it is
+	/// the number of characters. There are two bytes (two chars) per character
+	/// in UTF16. 
+	///
+	/// The input nTextLength can be -1 (kLengthNull) to indicate that the string 
+	/// is null-terminated. 
+	///
+	/// See the other version of ParseDelimitedText below for some additional documentation.
+	///
+	EASTDC_API bool ParseDelimitedText(const char8_t* pText, const char8_t* pTextEnd, char8_t cDelimiter, 
+									   const char8_t*& pToken, const char8_t*& pTokenEnd, const char8_t** ppNewText);
+
+	EASTDC_API bool ParseDelimitedText(const char16_t* pText, const char16_t* pTextEnd, char16_t cDelimiter, 
+									   const char16_t*& pToken, const char16_t*& pTokenEnd, const char16_t** ppNewText);
+
+	EASTDC_API bool ParseDelimitedText(const char32_t* pText, const char32_t* pTextEnd, char32_t cDelimiter, 
+									   const char32_t*& pToken, const char32_t*& pTokenEnd, const char32_t** ppNewText);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline bool ParseDelimitedText(const wchar_t* pText, const wchar_t* pTextEnd, wchar_t cDelimiter, 
+									   const wchar_t*& pToken, const wchar_t*& pTokenEnd, const wchar_t** ppNewText)
+			{ return ParseDelimitedText(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pText), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pTextEnd), EASTDC_UNICODE_CHAR_CAST(cDelimiter), 
+										EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(pToken), EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(pTokenEnd), EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewText)); }
+	#endif
+
+
+
+
+	/// ConvertBinaryDataToASCIIArray
+	///
+	/// These functions convert an array of binary characters into an encoded ASCII
+	/// format that can be later converted back to binary. You might want to do this
+	/// if you are trying to embed binary data into a text file (e.g. .ini file)
+	/// and need a way to encode the binary data as text.
+	/// 
+	/// Example input:
+	///    { 0x12, 0x34, 0x56, 0x78 }
+	/// Resulting output:
+	///    "12345678"
+	///
+	EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char8_t*  pASCIIArray);
+	EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char16_t* pASCIIArray);
+	EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char32_t* pASCIIArray);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, wchar_t* pASCIIArray)
+			{ ConvertBinaryDataToASCIIArray(pBinaryData, nBinaryDataLength, EASTDC_UNICODE_CHAR_PTR_CAST(pASCIIArray)); }
+	#endif
+
+
+	/// ConvertASCIIArrayToBinaryData
+	///
+	/// Takes an ASCII string of text and converts it to binary data. This is the reverse
+	/// of the ConvertBinaryDataToASCIIArray function. If an invalid hexidecimal character
+	/// is encountered, it is replaced with a '0' character.
+	/// Returns true if the input was entirely valid hexadecimal data.
+	/// 
+	/// Example input:
+	///    "12345678"
+	/// Resulting output:
+	///    { 0x12, 0x34, 0x56, 0x78 }
+	///
+	EASTDC_API bool ConvertASCIIArrayToBinaryData(const char8_t*  pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+	EASTDC_API bool ConvertASCIIArrayToBinaryData(const char16_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+	EASTDC_API bool ConvertASCIIArrayToBinaryData(const char32_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline bool ConvertASCIIArrayToBinaryData(const wchar_t*  pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData)
+			{ return ConvertASCIIArrayToBinaryData(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pASCIIArray), nASCIIArrayLength, pBinaryData); }
+	#endif
+
+
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// GetTextLine
+	///
+	/// Given a block of text, this function reads a line of text and moves to the 
+	/// beginning of the next line. The return value is the end of the current line,
+	/// previous to the newline characters. 
+	/// Lines are defined as ending in \n, \r, \r\n, or \n\r sequences.
+	/// If ppNewText is supplied, it holds the start of the new line, which will often
+	/// be different from the return value, as the start of the new line is after any
+	/// newline characters. The length of the current line is pTextEnd - pText.
+	///
+	/// Example usage (assumes that :
+	///     cosnt char  buffer[30] = "line1\nLine2\r\nLine3\rLine4\nLine5"; // strlen(buffer) == 30.
+	///     const char* pLineNext(buffer);
+	///     const char* pLine;
+	///
+	///     do{
+	///         pLine = pLineNext;
+	///         const char* pLineEnd = GetTextLine(pLine, buffer + EAArrayCount(buffer), &pLineNext);
+	///         // Use pLine - pLineEnd
+	///     }while(pLineNext != (buffer + 256));
+	///
+	EASTDC_API const char8_t*  GetTextLine(const char8_t* pText, const char8_t* pTextEnd, const char8_t** ppNewText);
+	EASTDC_API const char16_t* GetTextLine(const char16_t* pText, const char16_t* pTextEnd, const char16_t** ppNewText);
+	EASTDC_API const char32_t* GetTextLine(const char32_t* pText, const char32_t* pTextEnd, const char32_t** ppNewText);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline const wchar_t* GetTextLine(const wchar_t* pText, const wchar_t* pTextEnd, const wchar_t** ppNewText)
+			{ return reinterpret_cast<const wchar_t *>(GetTextLine(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pText), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pTextEnd), EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewText))); }
+	#endif
+
+	
+	/// GetTextLine
+	///
+	/// Retrieves the line in the pLine output argument. Lines are defined as ending in 
+	/// \n, \r, \r\n, or \n\r sequences.
+	/// Returns true if there is any line to retrieve. Returns false only if sSource is empty.
+	/// The retrieved pLine does not have the final newline character(s).
+	/// sSource is modified to have the line removed. This gets increasingly less efficient
+	/// as the length of sSource increases for the case that sSource is a typical STL-like
+	/// linear string. 
+	/// If pLine is NULL then the behavior is the same except no copy is done to pLine.
+	///
+	/// Requires that String support the following STL-like data functions: 
+	///     data(), length(), erase(pos, count), assign(begin, end).
+	///
+	/// Example usage:
+	///     eastl::stringW s(L"line1\nLine2\r\nLine3\rLine4\nLine5");
+	///     eastl::stringW line;
+	/// 
+	///     while(GetTextLine(s, &line))
+	///          printf("%ls\n", line.c_str());
+	///
+	template<typename String>
+	bool GetTextLine(String& sSource, String* pLine);
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// SplitTokenDelimited
+	///
+	/// This function returns tokens that are delimited by a single character --
+	/// repetitions of that character will result in empty tokens returned.
+	/// This is most commonly useful when you want to parse a string of text
+	/// delimited by commas or spaces.
+	/// 
+	/// Returns true whenever it extracts a token. Note however that the extracted
+	/// token may be empty. Note that the return value will be true if the source
+	/// has length and will be false if the source is empty.
+	/// If the input pToken is non-null, the text before the delimiter is copied to it. 
+	///
+	/// Examples (delimiter is comma):
+	///    source      token    new source   return
+	///    -----------------------------------------
+	///    "a,b"       "a"      "b"          true
+	///    " a , b "   " a "    " b "        true
+	///    "ab,b"      "ab"     "b"          true
+	///    ",a,b"      ""       "a,b"        true
+	///    ",b"        ""       "b"          true
+	///    ",,b"       ""       ",b"         true
+	///    ",a,"       ""       "a,"         true
+	///    "a,"        "a"      ""           true
+	///    ","         ""       ""           true
+	///    ", "        ""       " "          true
+	///    "a"         "a"      ""           true
+	///    " "         " "      ""           true
+	///    ""          ""       ""           false
+	///    NULL        ""       NULL         false
+	///
+	///  Example usage:
+	///    const char16_t* pString = EA_CHAR16("a, b, c, d");
+	///    char16_t pToken[16];
+	///    
+	///    while(SplitTokenDelimited(pString, kLengthNull, ',', pToken, 16, &pString))
+	///         printf("%s\n", pToken);
+	///    
+	EASTDC_API bool SplitTokenDelimited(const char8_t*  pSource, size_t nSourceLength, char8_t  cDelimiter, char8_t*  pToken, size_t nTokenLength, const char8_t**  ppNewSource = NULL);
+	EASTDC_API bool SplitTokenDelimited(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+	EASTDC_API bool SplitTokenDelimited(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline const wchar_t* SplitTokenDelimited(const wchar_t* pSource, size_t nSourceLength, wchar_t cDelimiter, wchar_t* pToken, size_t nTokenLength, const wchar_t** ppNewSource = NULL)
+			{ return reinterpret_cast<const wchar_t*>(SplitTokenDelimited(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nSourceLength, EASTDC_UNICODE_CHAR_CAST(cDelimiter), EASTDC_UNICODE_CHAR_PTR_CAST(pToken), nTokenLength, EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewSource))); }
+	#endif
+
+	template<typename String, typename Char>
+	bool SplitTokenDelimited(String& sSource, Char cDelimiter, String* pToken);
+
+
+	///////////////////////////////////////////////////////////////////////////////
+	/// SplitTokenSeparated
+	///
+	/// This function returns tokens that are separated by one or more instances
+	/// of a character. Returns true whenever it extracts a token. 
+	///
+	/// Examples (delimiter is space):
+	///   source    token    new source   return
+	///   ---------------------------------------
+	///    "a"       "a"      ""           true
+	///    "a b"     "a"      "b"          true
+	///    "a  b"    "a"      "b"          true
+	///    " a b"    "a"      "b"          true
+	///    " a b "   "a"      "b "         true
+	///    " a "     "a"      ""           true
+	///    " a  "    "a"      ""           true
+	///    ""        ""       ""           false
+	///    " "       ""       ""           false
+	///    NULL      ""       NULL         false
+	///
+	EASTDC_API bool SplitTokenSeparated(const char8_t*  pSource, size_t nSourceLength, char8_t  cDelimiter, char8_t*  pToken, size_t nTokenLength, const char8_t**  ppNewSource = NULL);
+	EASTDC_API bool SplitTokenSeparated(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+	EASTDC_API bool SplitTokenSeparated(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		inline const wchar_t* SplitTokenSeparated(const wchar_t* pSource, size_t nSourceLength, wchar_t cDelimiter, wchar_t* pToken, size_t nTokenLength, const wchar_t** ppNewSource = NULL)
+			{ return reinterpret_cast<const wchar_t*>(SplitTokenSeparated(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nSourceLength, EASTDC_UNICODE_CHAR_CAST(cDelimiter), EASTDC_UNICODE_CHAR_PTR_CAST(pToken), nTokenLength, EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewSource))); }
+	#endif
+
+	template<typename String, typename Char>
+	bool SplitTokenSeparated(String& sSource, Char c, String* pToken);
+
+
+	////////////////////////////////////////////////////////////////////////////////
+	/// Boyer-Moore string search
+	///
+	/// This is the "turbo" implementation defined at http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140.
+	/// Boyer-Moore is a very fast string search compared to most others, including
+	/// those in the STL. However, you need to be searching a string of at least 100
+	/// chars and have a search pattern of at least 3 characters for the speed to show,
+	/// as Boyer-Moore has a startup precalculation that costs some cycles. 
+	/// This startup precalculation is proportional to the size of your search pattern
+	/// and the size of the alphabet in use. Thus, doing Boyer-Moore searches on the 
+	/// entire Unicode alphabet is going to incur a fairly expensive precalculation cost.
+	///
+	/// patternBuffer1 is a user-supplied buffer and must be at least as long as the search pattern.
+	/// patternBuffer2 is a user-supplied buffer and must be at least as long as the search pattern.
+	/// alphabetBuffer is a user-supplied buffer and must be at least as long as the highest character value used in the searched string and search pattern.
+	///
+	EASTDC_API int BoyerMooreSearch(const char* pPattern, int nPatternLength, const char* pSearchString, int nSearchStringLength, 
+									int* pPatternBuffer1, int* pPatternBuffer2, int* pAlphabetBuffer, int nAlphabetBufferSize);
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+namespace EA
+{
+	namespace StdC
+	{
+
+		template<typename String>
+		bool GetTextLine(String& sSource, String* pLine)
+		{
+			typedef typename String::value_type Char;
+
+			bool        bReturnValue = false;
+			const Char* pText    = sSource.data();
+			const Char* pTextEnd = pText + sSource.length();
+
+			if(pText < pTextEnd)
+			{
+				// Read until the first \r or \n. 
+				while((pText < pTextEnd) && (*pText != '\r') && (*pText != '\n'))
+					++pText;
+
+				if(pLine)
+					pLine->assign(sSource.data(), pText);
+
+				// Find the end of the line, which will be 0, 1, or 2 chars past pText.
+				const Char* pTextCurrent = pText;
+
+				if(pTextCurrent < pTextEnd)
+				{
+					// Read past a second newline character, e.g. as part of a /r/n sequence.
+					if((++pTextCurrent < pTextEnd) && (*pTextCurrent ^ *pText) == ('\r' ^ '\n'))
+						++pTextCurrent;
+				}
+
+				sSource.erase(0, pTextCurrent - sSource.data());
+
+				bReturnValue = true;
+			}
+
+			return bReturnValue;
+		}
+
+
+		template<typename String, typename Char>
+		bool SplitTokenDelimited(String& sSource, Char cDelimiter, String* pToken)
+		{
+			if(pToken)
+				pToken->clear();
+
+			if(!sSource.empty())
+			{
+				// find the first occurence of the delimiter
+				const size_t nIndex = sSource.find(cDelimiter);
+
+				if(nIndex == String::npos) // If we didn't find the delimiter...
+				{
+					if(pToken) // If we have the token
+						pToken->swap(sSource);  // swap it with the source string ( it will clear the source )
+					else
+						sSource.erase(); // simply clear the source
+				}
+				else
+				{
+					if(pToken) // If we found a delimiter - move the string before it to the token, if present
+						pToken->assign(sSource, 0, (typename String::size_type)nIndex);
+
+					sSource.erase(0, (typename String::size_type)nIndex + 1); // remove the token + delimiter from the source
+				}
+
+				return true; // there are tokens remaining
+			}
+
+			return false;
+		}
+
+		template<typename String, typename Char>
+		bool SplitTokenSeparated(String& sSource, Char c, String* pToken)
+		{
+			// loop until we are done
+			for(;;)
+			{
+				// look for first occurance of the separator
+				const size_t nIndex1 = sSource.find(c);
+
+				// didn't find any
+				if(nIndex1 == String::npos)
+				{
+					// no text available
+					if(sSource.empty())
+					{
+						// clear token
+						if(pToken)
+							pToken->erase();
+
+						// no token found - return false
+						return false;
+					}
+					else
+					{
+						// no separators, but we are not empty - fill the token and clear the source
+						if(pToken)
+						{
+							// clear it
+							pToken->erase();
+							
+							// swap it with the source string ( it will clear the source )
+							pToken->swap(sSource);
+						}
+						else
+						{
+							// simply clear the source
+							sSource.erase();
+						}
+
+						// token found - return true
+						return true;
+					}
+				}
+
+				// find first ( or second ) occurence of a non-separator character
+				const size_t nIndex2 = sSource.find_first_not_of(c, (typename String::size_type)nIndex1);
+
+				// if we found a non-empty string in front of separators, extract it and exit
+				if(nIndex1 > 0)
+				{
+					// add it to the token
+					if(pToken)
+						pToken->assign(sSource, 0, (typename String::size_type)nIndex1);
+
+					// remove all we found so far from the source
+					sSource.erase(0, (typename String::size_type)nIndex2);
+
+					// token found - return true
+					return true;
+				}
+
+				// Remove initial separator and loop back to try again
+				sSource.erase(0, (typename String::size_type)nIndex2);
+			}
+
+		   // return false;   // This should never get executed, but some compilers might not be smart enough to realize it.
+		}
+	}
+}
+
+
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+

+ 544 - 0
include/EAStdC/Int128_t.h

@@ -0,0 +1,544 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines the following:
+//     Integer data types int128_t and uint128_t
+//     INT128_MIN, INT128_MAX, UINT128_MIN, UINT128_MAX
+//     INT128_C, UINT128_C
+//
+// Issues:
+//     * Automatic float and double conversion to int128_t is only partially supported.
+//     * Some problems exist with respect to different compiler's view of what types 
+//       like 'long' and 'long long' are. Some see these as 32 and 64 bits respectively
+//       and some don't.
+//
+// Note that GCC defines the __int128_t and __uint128_t data types when building
+// for 64 bit platforms. These types are binary-compatible with the int128_t and
+// uint128_t types defined here.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_INT128_H
+#define EASTDC_INT128_H
+
+
+#include <EAStdC/internal/Config.h>
+#include <EABase/eabase.h>
+
+EA_DISABLE_GCC_WARNING(-Wtype-limits)
+#include <wchar.h>
+EA_RESTORE_GCC_WARNING()
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_INT128_USE_INT64
+//
+#ifndef EA_INT128_USE_INT64
+	#if (EA_PLATFORM_WORD_SIZE >= 8)
+		#define EA_INT128_USE_INT64 1
+	#else
+		#define EA_INT128_USE_INT64 0
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// INT128_INT_TYPE / INT128_UINT_TYPE
+//
+// int32_t may be equivalent to int or it may be equivalent to long, depending 
+// on the compiler / platform. In order for integer expressions involving 
+// int128_t to work for all cases, we need to provide support for whichever 
+// type is not handled by int32_t.
+
+#if   defined(EA_PLATFORM_MICROSOFT)                // Microsoft always defines long as 32 bits, and int32_t as int.
+	#define INT128_INT_TYPE  long
+	#define INT128_UINT_TYPE unsigned long
+#elif(EA_PLATFORM_PTR_SIZE == 4)                    // Other compilers define long as 32 or 64 bits, and int32_t as int and int64_t as long or long long.
+	#define INT128_INT_TYPE  long
+	#define INT128_UINT_TYPE unsigned long
+#elif defined(__have_long64)                        // Some Standard C libraries use this to indicate the int64_t type.
+	#define INT128_INT_TYPE  long long
+	#define INT128_UINT_TYPE unsigned long long
+#elif defined(__have_longlong64)                    // Some Standard C libraries use this to indicate the int64_t type.
+	#define INT128_INT_TYPE  long
+	#define INT128_UINT_TYPE unsigned long
+#elif defined(EA_PLATFORM_APPLE)                    // Apple always sets int64_t as long long on 64 bit platforms.
+	#define INT128_INT_TYPE  long
+	#define INT128_UINT_TYPE unsigned long
+#elif defined(EA_PLATFORM_UNIX)                     // Unix always sets int64_t as long on 64 bit platforms.
+	#define INT128_INT_TYPE  long long
+	#define INT128_UINT_TYPE unsigned long long
+#elif defined(__WORDSIZE) && (__WORDSIZE == 64)     // When __WORDSIZE is 64 bit, the compiler sets int64_t to be long. Except on Apple platforms.
+	#define INT128_INT_TYPE  long long
+	#define INT128_UINT_TYPE unsigned long long
+#else
+	// To do: we need to detect whether the compiler/stdlib is defining int64_t as long or long long.
+	// In the case that it's using long, we use long long here. In the case that it's using long long, we use long here.
+#endif
+
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+//
+class int128_t_base;
+class int128_t;
+class uint128_t;
+
+
+/// \class int128_t_base
+/// \brief Base class upon which int128_t and uint128_t build.
+///
+class EASTDC_API int128_t_base
+{
+public:
+	// Constructors / destructors
+	int128_t_base();
+	int128_t_base(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
+	int128_t_base(uint64_t nPart0, uint64_t nPart1);
+	int128_t_base(uint8_t value);
+	int128_t_base(uint16_t value);
+	int128_t_base(uint32_t value);
+  #if defined(INT128_UINT_TYPE)
+	int128_t_base(INT128_UINT_TYPE value);
+  #endif
+	int128_t_base(uint64_t value);
+	int128_t_base(const int128_t_base& value);
+
+	// To do: Add an int128_t_base(unsigned int) constructor just for Metrowerks,
+	// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
+
+	// Assignment operator
+	int128_t_base& operator=(const int128_t_base& value);
+
+	// Math operators
+	static void operatorPlus (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+	static void operatorMinus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+	static void operatorMul  (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+
+	// Shift operators
+	static void operatorShiftRight(const int128_t_base& value, int nShift, int128_t_base& result);
+	static void operatorShiftLeft (const int128_t_base& value, int nShift, int128_t_base& result);
+
+	// Unary arithmetic/logic operators
+	bool operator!() const;
+
+	// Logical operators
+	static void operatorXOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+	static void operatorOR (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+	static void operatorAND(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
+
+	// Operators to convert back to basic types
+	// We do not provide casting operators (e.g. operator float()) because doing so would
+	// cause the compiler to complian about multiple conversion choices while doing freeform
+	// math. That's standard C++ behaviour and the conventional solution is to not provide
+	// implicit casting operators but to provide functions such as AsFloat() to allow the 
+	// user to do explicit casts.
+	bool     AsBool()   const;
+	uint8_t  AsUint8()  const;
+	uint16_t AsUint16() const;
+	uint32_t AsUint32() const;
+	uint64_t AsUint64() const;
+
+	// Misc. Functions
+	// The index values below start with zero, and zero means the lowest part.
+	// For example, calling SetPartUint32(3, 0x00000000) zeros the high 32 bits of the int128.
+	int      GetBit(int nIndex) const;
+	void     SetBit(int nIndex, int value);
+	uint8_t  GetPartUint8 (int nIndex) const;
+	uint16_t GetPartUint16(int nIndex) const;
+	uint32_t GetPartUint32(int nIndex) const;
+	uint64_t GetPartUint64(int nIndex) const;
+	void     SetPartUint8 (int nIndex, uint8_t  value);
+	void     SetPartUint16(int nIndex, uint16_t value);
+	void     SetPartUint32(int nIndex, uint32_t value);
+	void     SetPartUint64(int nIndex, uint64_t value);
+
+	bool     IsZero() const;
+	void     SetZero();
+	void     TwosComplement();
+	void     InverseTwosComplement();
+
+	enum LeadingZeroes
+	{
+		kLZDefault,     // Do the default for the base. By default there are leading zeroes only with base 16.
+		kLZEnable,
+		kLZDisable
+	};
+
+	enum Prefix
+	{
+		kPrefixDefault, // Do the default for the base. By default there is a prefix only with base 16.
+		kPrefixEnable,
+		kPrefixDisable
+	};
+
+protected:
+	void DoubleToUint128(double value);
+
+protected:
+	#if EA_INT128_USE_INT64
+		#ifdef EA_SYSTEM_BIG_ENDIAN
+			uint64_t mPart1;  // Most significant byte.
+			uint64_t mPart0;  // Least significant byte.
+		#else
+			uint64_t mPart0;  // Most significant byte.
+			uint64_t mPart1;  // Least significant byte.
+		#endif
+	#else
+		#ifdef EA_SYSTEM_BIG_ENDIAN
+			uint32_t mPart3;  // Most significant byte.
+			uint32_t mPart2;
+			uint32_t mPart1;
+			uint32_t mPart0;  // Least significant byte.
+		#else
+			uint32_t mPart0;  // Most significant byte.
+			uint32_t mPart1;
+			uint32_t mPart2;
+			uint32_t mPart3;  // Least significant byte.
+		#endif
+	#endif
+};
+
+
+/// \class int128_t
+/// \brief Implements signed 128 bit integer.
+///
+class EASTDC_API int128_t : public int128_t_base
+{
+public:
+	// Constructors / destructors
+	int128_t();
+	int128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
+	int128_t(uint64_t nPart0, uint64_t nPart1);
+
+	int128_t(int8_t value);
+	int128_t(uint8_t value);
+
+	int128_t(int16_t value);
+	int128_t(uint16_t value);
+
+	int128_t(int32_t value);
+	int128_t(uint32_t value);
+
+  #if defined(INT128_INT_TYPE)
+	int128_t(INT128_INT_TYPE value);
+	int128_t(INT128_UINT_TYPE value);
+  #endif
+
+	int128_t(int64_t value);
+	int128_t(uint64_t value);
+
+	int128_t(const int128_t& value);
+	//int128_t(const uint128_t& value); // Not defined because doing so would make the compiler unable to decide how to choose binary functions involving int128/uint128.
+
+	int128_t(const float value);
+	int128_t(const double value);
+
+	int128_t(const char* pValue, int nBase = 10);
+	int128_t(const wchar_t* pValue, int nBase = 10);
+
+	// To do: Add an int128_t(int) constructor just for Metrowerks,
+	// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
+
+	// Assignment operator
+	int128_t& operator=(const int128_t_base& value);
+
+	// Unary arithmetic/logic operators
+	int128_t  operator-() const;
+	int128_t& operator++();
+	int128_t& operator--();
+	int128_t  operator++(int);
+	int128_t  operator--(int);
+	int128_t  operator~() const;
+	int128_t  operator+() const;
+
+	// Math operators
+	friend EASTDC_API int128_t operator+(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator-(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator*(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator/(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator%(const int128_t& value1, const int128_t& value2);
+
+	int128_t& operator+=(const int128_t& value);
+	int128_t& operator-=(const int128_t& value);
+	int128_t& operator*=(const int128_t& value);
+	int128_t& operator/=(const int128_t& value);
+	int128_t& operator%=(const int128_t& value);
+
+	// Shift operators
+	int128_t  operator>> (int nShift) const;
+	int128_t  operator<< (int nShift) const;
+	int128_t& operator>>=(int nShift);
+	int128_t& operator<<=(int nShift);
+
+	// Logical operators
+	friend EASTDC_API int128_t operator^(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator|(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API int128_t operator&(const int128_t& value1, const int128_t& value2);
+
+	int128_t& operator^= (const int128_t& value);
+	int128_t& operator|= (const int128_t& value);
+	int128_t& operator&= (const int128_t& value);
+
+	// Equality operators
+	friend EASTDC_API int  compare   (const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator==(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator!=(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator> (const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator>=(const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator< (const int128_t& value1, const int128_t& value2);
+	friend EASTDC_API bool operator<=(const int128_t& value1, const int128_t& value2);
+
+	// Operators to convert back to basic types
+	// We do not provide casting operators (e.g. operator float()) because doing so would
+	// cause the compiler to complian about multiple conversion choices while doing freeform
+	// math. That's standard C++ behaviour and the conventional solution is to not provide
+	// implicit casting operators but to provide functions such as AsFloat() to allow the 
+	// user to do explicit casts.
+	int8_t  AsInt8()   const;
+	int16_t AsInt16()  const;
+	int32_t AsInt32()  const;
+	int64_t AsInt64()  const;
+	float   AsFloat()  const;
+	double  AsDouble() const;
+
+	// Misc. Functions
+	void    Negate();
+	bool    IsNegative() const;    // Returns true for value <  0
+	bool    IsPositive() const;    // Returns true for value >= 0
+	void    Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const;
+
+	// String conversion functions
+	static int128_t StrToInt128(const char*    pValue, char**    ppEnd, int base);
+	static int128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base);
+
+	void     Int128ToStr(char*    pValue, char**    ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix prefix = kPrefixDefault) const;
+	void     Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix pPrefix = kPrefixDefault) const;
+};
+
+
+
+/// \class uint128_t
+/// \brief Implements unsigned 128 bit integer.
+///
+class EASTDC_API uint128_t : public int128_t_base
+{
+public:
+	// Constructors / destructors
+	uint128_t();
+	uint128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
+	uint128_t(uint64_t nPart0, uint64_t nPart1);
+
+	uint128_t(int8_t value);
+	uint128_t(uint8_t value);
+
+	uint128_t(int16_t value);
+	uint128_t(uint16_t value);
+
+	uint128_t(int32_t value);
+	uint128_t(uint32_t value);
+
+  #if defined(INT128_INT_TYPE)
+	uint128_t(INT128_INT_TYPE value);
+	uint128_t(INT128_UINT_TYPE value);
+  #endif
+
+	uint128_t(int64_t value);
+	uint128_t(uint64_t value);
+
+	uint128_t(const int128_t& value);
+	uint128_t(const uint128_t& value);
+
+	uint128_t(const float value);
+	uint128_t(const double value);
+
+	uint128_t(const char* pValue, int nBase = 10);
+	uint128_t(const wchar_t* pValue, int nBase = 10);
+
+	// To do: Add a uint128_t(unsigned int) constructor just for Metrowerks,
+	// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
+
+	// Assignment operator
+	uint128_t& operator=(const int128_t_base& value);
+
+	// Unary arithmetic/logic operators
+	uint128_t  operator-() const;
+	uint128_t& operator++();
+	uint128_t& operator--();
+	uint128_t  operator++(int);
+	uint128_t  operator--(int);
+	uint128_t  operator~() const;
+	uint128_t  operator+() const;
+
+	// Math operators
+	friend EASTDC_API uint128_t operator+(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator-(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator*(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator/(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator%(const uint128_t& value1, const uint128_t& value2);
+
+	uint128_t& operator+=(const uint128_t& value);
+	uint128_t& operator-=(const uint128_t& value);
+	uint128_t& operator*=(const uint128_t& value);
+	uint128_t& operator/=(const uint128_t& value);
+	uint128_t& operator%=(const uint128_t& value);
+
+	// Shift operators
+	uint128_t  operator>> (int nShift) const;
+	uint128_t  operator<< (int nShift) const;
+	uint128_t& operator>>=(int nShift);
+	uint128_t& operator<<=(int nShift);
+
+	// Logical operators
+	friend EASTDC_API uint128_t operator^(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator|(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API uint128_t operator&(const uint128_t& value1, const uint128_t& value2);
+
+	uint128_t& operator^= (const uint128_t& value);
+	uint128_t& operator|= (const uint128_t& value);
+	uint128_t& operator&= (const uint128_t& value);
+
+	// Equality operators
+	friend EASTDC_API int  compare   (const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator==(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator!=(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator> (const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator>=(const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator< (const uint128_t& value1, const uint128_t& value2);
+	friend EASTDC_API bool operator<=(const uint128_t& value1, const uint128_t& value2);
+
+	// Operators to convert back to basic types
+	// We do not provide casting operators (e.g. operator float()) because doing so would
+	// cause the compiler to complian about multiple conversion choices while doing freeform
+	// math. That's standard C++ behaviour and the conventional solution is to not provide
+	// implicit casting operators but to provide functions such as AsFloat() to allow the 
+	// user to do explicit casts.
+	int8_t  AsInt8()   const;
+	int16_t AsInt16()  const;
+	int32_t AsInt32()  const;
+	int64_t AsInt64()  const;
+	float   AsFloat()  const;
+	double  AsDouble() const;
+
+	// Misc. Functions
+	void    Negate();
+	bool    IsNegative() const;    // Returns true for value <  0
+	bool    IsPositive() const;    // Returns true for value >= 0
+	void    Modulus(const uint128_t& divisor, uint128_t& quotient, uint128_t& remainder) const;
+
+	// String conversion functions
+	static uint128_t StrToInt128(const char*    pValue, char**    ppEnd, int base);
+	static uint128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base);
+
+	void      Int128ToStr(char*    pValue, char**    ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix prefix = kPrefixDefault) const;
+	void      Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix pPrefix = kPrefixDefault) const;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// binary operators
+//
+// Operators involving two operands are made as independent functions instead
+// of member functions because if they were member functions then an expression
+// involving both operands would always have to be written in a specific order.
+//
+
+
+///////////////////////////////////////////////////////////////////////////////
+// int128_t
+//
+EASTDC_API int128_t operator+(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator-(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator*(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator/(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator%(const int128_t& value1, const int128_t& value2);
+
+EASTDC_API int128_t operator^(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator|(const int128_t& value1, const int128_t& value2);
+EASTDC_API int128_t operator&(const int128_t& value1, const int128_t& value2);
+
+EASTDC_API int      compare   (const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator==(const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator!=(const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator> (const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator>=(const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator< (const int128_t& value1, const int128_t& value2);
+EASTDC_API bool     operator<=(const int128_t& value1, const int128_t& value2);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// uint128_t
+//
+EASTDC_API uint128_t operator+(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator-(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator*(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator/(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator%(const uint128_t& value1, const uint128_t& value2);
+
+EASTDC_API uint128_t operator^(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator|(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API uint128_t operator&(const uint128_t& value1, const uint128_t& value2);
+
+EASTDC_API int       compare   (const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator==(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator!=(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator> (const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator>=(const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator< (const uint128_t& value1, const uint128_t& value2);
+EASTDC_API bool      operator<=(const uint128_t& value1, const uint128_t& value2);
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Min / Max
+//
+// The C99 language standard defines macros for sized types, such as INT32_MAX.
+// Usually such macros are defined like this:
+//     #define INT32_MAX 2147483647
+// We cannot do this with int128, so instead we define a const variable that
+// can be linked to which has the desirable property.
+//
+extern EASTDC_API const int128_t  EASTDC_INT128_MIN;
+extern EASTDC_API const int128_t  EASTDC_INT128_MAX;
+
+extern EASTDC_API const uint128_t EASTDC_UINT128_MIN;
+extern EASTDC_API const uint128_t EASTDC_UINT128_MAX;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// INT128_C / UINT128_C
+//
+// The C99 language defines macros for portably defining constants of 
+// sized numeric types. For example, there might be:
+//     #define UINT64_C(x) x##ULL
+// Since our int128 data type is not a built-in type, we can't define a
+// UINT128_C macro as something that pastes ULLL at the end of the digits.
+// Instead we define it to create a temporary that is constructed from a 
+// string of the digits. This will work in most cases that suffix pasting
+// would work.
+//
+#define EASTDC_INT128_C(x)  EA::StdC::int128_t(#x)
+#define EASTDC_UINT128_C(x) EA::StdC::uint128_t(#x)
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+
+

+ 261 - 0
include/EAStdC/Win32/EAMathHelpWin32.inl

@@ -0,0 +1,261 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32 implementation of fast, specialized scalar math primitives
+///////////////////////////////////////////////////////////////////////////////
+
+
+// EAMathHelp.h (or possibly the build file) would have set none or one of the 
+// following (usually none). If none were defined then we auto-detect.
+#if !defined(EAMATHHELP_MODE_SSE)     && \
+	!defined(EAMATHHELP_MODE_X86ASM)  && \
+	!defined(EAMATHHELP_MODE_REFERENCE)
+
+	#if !defined(EAMATHHELP_MODE_SSE) && defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC)
+		#define EAMATHHELP_MODE_SSE 1
+	#elif !defined(EAMATHHELP_MODE_X86ASM) && defined(EA_PROCESSOR_X86) && defined(EA_ASM_STYLE_INTEL)
+		#define EAMATHHELP_MODE_X86ASM 1
+	#else
+		#define EAMATHHELP_MODE_REFERENCE 1
+	#endif
+
+#endif
+
+#if EAMATHHELP_MODE_SSE
+	#pragma warning(push, 0)
+	#include <xmmintrin.h>
+	#pragma warning(pop)
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	#if EAMATHHELP_MODE_SSE
+
+		inline uint32_t RoundToUint32(float32_t fValue) {
+			// We accomplish unsigned 32-bit conversion here by wrapping the
+			// value to [-2^31, 2^31] before doing a signed 32-bit conversion.
+			// This is necessary because SSE cannot do unsigned 32-bit itof
+			// conversion and cannot store 64-bit values like x87 can.
+
+			const __m128 fValue128 = _mm_set_ss(fValue);
+			return (uint32_t)_mm_cvtss_si32(
+								_mm_add_ss(fValue128,
+									_mm_and_ps(
+										_mm_cmpgt_ss(
+											fValue128,
+											_mm_set_ss(2147483648.0f)
+										),
+										_mm_set_ss(-4294967296.0f)
+									)
+								)
+							);
+		}
+
+		inline int32_t RoundToInt32(float32_t fValue) {
+			return _mm_cvtss_si32(_mm_set_ss(fValue));
+		}
+
+		inline int32_t FloorToInt32(float32_t fValue) {
+			__m128 fValue128 = _mm_set_ss(fValue);
+			int32_t iValue = _mm_cvtss_si32(fValue128);
+			int32_t correction = _mm_cvtss_si32(            // correction = iValue > fValue128 ? -1.0f : 0.0f
+									_mm_and_ps(
+										_mm_cmplt_ss(
+											fValue128,
+											_mm_cvtsi32_ss(_mm_setzero_ps(), iValue)
+										),
+										_mm_set_ss(-1.0f)
+									)
+								);
+
+			return iValue + correction;
+		}
+
+		inline int32_t CeilToInt32(float32_t fValue) {
+			__m128 fValue128 = _mm_set_ss(fValue);
+			int32_t iValue = _mm_cvtss_si32(fValue128);
+			int32_t correction = _mm_cvtss_si32(            // correction = iValue < fValue128 ? +1.0f : 0.0f
+									_mm_and_ps(
+										_mm_cmplt_ss(
+											_mm_cvtsi32_ss(_mm_setzero_ps(), iValue),
+											fValue128
+										),
+										_mm_set_ss(+1.0f)
+									)
+								);
+
+			return iValue + correction;
+		}
+
+		// This shouldn't be necessary with /arch:SSE, but if VC7.1 is doing calcs
+		// in x87 -- which it will sometimes do if the SSE ops or data types are
+		// too awkward for the expression -- it will resort to good old slow-as-@&$
+		// fldcw + fistp.
+		inline int32_t TruncateToInt32(float32_t fValue) {
+			#if (defined(_MSC_VER) && (_MSC_VER < 1500)) || !EA_SSE2  // If using VC++ < VS2008 or if SSE2+ is not available...
+				return _mm_cvttss_si32(_mm_set_ss(fValue));
+			#else
+				return (int32_t)fValue;
+			#endif
+		}
+
+		// This function is deprecated, as it's not very useful any more.
+		inline int32_t FastRoundToInt23(float32_t fValue) {
+			return _mm_cvtss_si32(_mm_set_ss(fValue));
+		}
+
+		inline uint8_t UnitFloatToUint8(float fValue) {
+			return (uint8_t)_mm_cvtss_si32(_mm_set_ss(fValue * 255.0f));
+		}
+
+		inline uint8_t ClampUnitFloatToUint8(float fValue) {
+			return (uint8_t)_mm_cvtss_si32(
+								_mm_max_ss(
+									_mm_min_ss(
+										_mm_set_ss(fValue * 255.0f),
+										_mm_set_ss(255.0f)
+									),
+									_mm_set_ss(0.0f)
+								)
+							);
+		}
+
+	#elif EAMATHHELP_MODE_X86ASM
+
+		inline uint32_t RoundToUint32(float32_t fValue) {
+			EA_PREFIX_ALIGN(8) int64_t v;
+			__asm {
+				fld fValue
+				fistp v
+			}
+			return (uint32_t)v;
+		}
+
+		inline int32_t RoundToInt32(float32_t fValue) {
+			int32_t iv;
+			__asm {
+				fld fValue
+				fistp iv
+			}
+			return iv;
+		}
+
+		inline int32_t FloorToInt32(float32_t fValue) {
+			int32_t iv, correct;
+
+			__asm {
+				fld     fValue              ;load fp value
+				fist    iv                  ;store as integer (rounded to nearest even)
+				fild    iv                  ;reload rounded value
+				fsub                        ;compute v - round(v)
+				fstp    correct             ;store v - round(v)
+				cmp     correct, 80000001h  ;set carry if (v >= round(v))   (watch for -0!)
+				mov     eax, iv             ;load rounded value
+				adc     eax, -1             ;subtract 1 if v < round(v)
+			}
+		}
+
+		inline int32_t CeilToInt32(float32_t fValue) {
+			int32_t iv, correct;
+
+			__asm {
+				fld     fValue              ;load fp value
+				fist    iv                  ;store as integer (rounded to nearest even)
+				fild    iv                  ;reload rounded value
+				fsubr                       ;compute round(v) - v
+				fstp    correct             ;store round(v) - v
+				cmp     correct, 80000001h  ;set carry if (round(v) >= v)   (watch for -0!)
+				mov     eax, iv             ;load rounded value
+				sbb     eax, -1             ;add 1 if round(v) < v
+			}
+		}
+
+		// This function is not much faster than VC7.1's improved _ftol()
+		// and is actually slower than fldcw on a Pentium 4, but is likely
+		// to be faster on a PIII, which does not cache fpucw changes.
+		inline int32_t TruncateToInt32(float32_t fValue) {
+			int32_t iv, correct;
+
+			__asm {
+				fld     fValue              ;load fp value
+				fabs                        ;compute |value|
+				fist    iv                  ;store |value| as integer using round-to-nearest-even
+				mov     eax, fValue         ;load fp value as integer
+				cdq                         ;extract sign bit
+				fild    iv                  ;load rounded fp value
+				fsub                        ;compute |v| - round(|v|)
+				fstp    correct             ;store |v| - round(|v|)
+				mov     eax, iv             ;load rounded value
+				cmp     correct, 80000001h  ;set carry if (v >= round(|v|))   (watch for -0!)
+				adc     eax, -1             ;add 1 if |v| < round(|v|)
+				xor     eax, edx            ;compute ~round(|v|) if v<0
+				sub     eax, edx            ;compute ~round(|v|)+1 == -round(|v|) if v<0
+			}
+		}
+
+
+		// The FastRoundToInt23() and UnitFloatToUint8() functions are somewhat fast
+		// by themselves, but what really makes them shine is the integer addition
+		// that can easily be folded into other additions done on the value.
+		// In particular, a table lookup on the result is likely to have the bias
+		// subtracted incorporated into the address displacement for free on x86.
+
+		// This function is deprecated, as it's not very useful any more.
+		inline int32_t FastRoundToInt23(float32_t fValue) {
+			const union {
+				float32_t   f;
+				int32_t i;
+			} converter = { fValue + kFToIBiasF32 };
+
+			return converter.i - kFToIBiasS32;
+		}
+
+		inline uint8_t UnitFloatToUint8(float fValue) {
+			const union {
+				float32_t   f;
+				int32_t i;
+			} converter = { fValue * (255.0f/256.0f) + kFToI8BiasF32 };
+
+			return (uint8_t)(int8_t)(converter.i - kFToI8BiasS32);
+		}
+
+
+		inline uint8_t ClampUnitFloatToUint8(float fValue) {
+			const union {
+				float32_t   f;
+				int32_t     i;
+			} converter = { kFToI8BiasF32 + fValue * (255.0f/256.0f) };
+
+			const int32_t i  = converter.i - kFToI8BiasS32;
+
+			// ~(i>>31)    == (i<0  ) ? 0 : 0xFFFFFFFF    (low clamp)
+			// (255-i)>>31 == (i>255) ? 0xFFFFFFFF : 0    (high clamp)
+
+			return uint8_t((i & ~(i>>31)) | ((255-i)>>31));
+		}
+
+	#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 722 - 0
include/EAStdC/internal/Config.h

@@ -0,0 +1,722 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_INTERNAL_CONFIG_H
+#define EASTDC_INTERNAL_CONFIG_H
+
+
+#include <EABase/eabase.h>
+#include <stddef.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_VERSION
+//
+// We more or less follow the conventional EA packaging approach to versioning 
+// here. A primary distinction here is that minor versions are defined as two
+// digit entities (e.g. .03") instead of minimal digit entities ".3"). The logic
+// here is that the value is a counter and not a floating point fraction.
+// Note that the major version doesn't have leading zeros.
+//
+// Example version strings:
+//      "0.91.00"   // Major version 0, minor version 91, patch version 0. 
+//      "1.00.00"   // Major version 1, minor and patch version 0.
+//      "3.10.02"   // Major version 3, minor version 10, patch version 02.
+//     "12.03.01"   // Major version 12, minor version 03, patch version 
+//
+// Example usage:
+//     printf("EASTDC version: %s", EASTDC_VERSION);
+//     printf("EASTDC version: %d.%d.%d", EASTDC_VERSION_N / 10000 % 100, EASTDC_VERSION_N / 100 % 100, EASTDC_VERSION_N % 100);
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_VERSION
+	#define EASTDC_VERSION   "1.26.02"
+	#define EASTDC_VERSION_N  12602
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_XBDM_ENABLED
+//
+// Defined as 0 or 1, with 1 being the default for debug builds.
+// This controls whether xbdm library usage is enabled on XBox 360. This library
+// allows for runtime debug functionality. But shipping applications are not
+// allowed to use xbdm. 
+//
+#if !defined(EA_XBDM_ENABLED)
+	#if defined(EA_DEBUG)
+		#define EA_XBDM_ENABLED 1
+	#else
+		#define EA_XBDM_ENABLED 0
+	#endif
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_SCEDBG_ENABLED
+//
+// Defined as 0 or 1, with 1 being the default for debug builds.
+// This controls whether sceDbg library usage is enabled on Sony platforms. This library
+// allows for runtime debug functionality. But shipping applications are not
+// allowed to use sceDbg. You can define EA_SCEDBG_ENABLED=1 in your nant build
+// properties to enable EA_SCEDBG_ENABLED in any build; the .build file for
+// this package will pick it up and define it for the compile of this package.
+//
+#if !defined(EA_SCEDBG_ENABLED)
+	#if defined(EA_DEBUG)
+		#define EA_SCEDBG_ENABLED 1
+	#else
+		#define EA_SCEDBG_ENABLED 0
+	#endif
+#endif
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASTDC_PRINTF_DEBUG_ENABLED
+// 
+// Defined as 0 or 1. Enabled by default in debug builds for platforms which
+// don't support stdout.
+// Has the effect of causing writes to stdout to be redirected to debug output
+// (same as Dprintf) on platforms where stdout is a no-op (e.g. consoles).
+//
+#if !defined(EASTDC_PRINTF_DEBUG_ENABLED)
+	#if defined(EA_PLATFORM_CONSOLE) || defined(EA_PLATFORM_MOBILE)
+		#define EASTDC_PRINTF_DEBUG_ENABLED 1
+	#else
+		#define EASTDC_PRINTF_DEBUG_ENABLED 0
+	#endif
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASTDC_OUTPUTDEBUGSTRING_ENABLED
+// 
+// Defined as 0 or 1. Enabled of the platform supports OutputDebugString and
+// if it's allowed to in the current build. Consider that Microsoft disallows
+// using OutputDebugString in published store applications.
+//
+#if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PLATFORM_DESKTOP) || EA_XBDM_ENABLED)
+	#define EASTDC_OUTPUTDEBUGSTRING_ENABLED 1
+#else
+	#define EASTDC_OUTPUTDEBUGSTRING_ENABLED 0
+#endif
+	
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_DLL
+//
+// Defined as 0 or 1. The default is dependent on the definition of EA_DLL.
+// If EA_DLL is defined, then EASTDC_DLL is 1, else EASTDC_DLL is 0.
+// EA_DLL is a define that controls DLL builds within the EAConfig build system. 
+// EASTDC_DLL controls whether EASTDC is built and used as a DLL. 
+// Normally you wouldn't do such a thing, but there are use cases for such
+// a thing, particularly in the case of embedding C++ into C# applications.
+//
+#ifndef EASTDC_DLL
+	#if defined(EA_DLL)
+		#define EASTDC_DLL 1
+	#else
+		#define EASTDC_DLL 0
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_API
+//
+// This is used to label functions as DLL exports under Microsoft platforms.
+// If EA_DLL is defined, then the user is building EASTDC as a DLL and EASTDC's
+// non-templated functions will be exported. EASTDC template functions are not
+// labelled as EASTDC_API (and are thus not exported in a DLL build). This is 
+// because it's not possible (or at least unsafe) to implement inline templated 
+// functions in a DLL.
+//
+// Example usage of EASTDC_API:
+//    EASTDC_API int someVariable = 10;         // Export someVariable in a DLL build.
+//
+//    struct EASTDC_API SomeClass{              // Export SomeClass and its member functions in a DLL build.
+//        EASTDC_LOCAL void PrivateMethod();    // Not exported.
+//    };
+//
+//    EASTDC_API void SomeFunction();           // Export SomeFunction in a DLL build.
+//
+// For GCC, see http://gcc.gnu.org/wiki/Visibility
+//
+#ifndef EASTDC_API // If the build file hasn't already defined this to be dllexport...
+	#if EASTDC_DLL 
+		#if defined(_MSC_VER)
+			#define EASTDC_API      __declspec(dllimport)
+			#define EASTDC_LOCAL
+		#elif defined(__CYGWIN__)
+			#define EASTDC_API      __attribute__((dllimport))
+			#define EASTDC_LOCAL
+		#elif (defined(__GNUC__) && (__GNUC__ >= 4))
+			#define EASTDC_API      __attribute__ ((visibility("default")))
+			#define EASTDC_LOCAL    __attribute__ ((visibility("hidden")))
+		#else
+			#define EASTDC_API
+			#define EASTDC_LOCAL
+		#endif
+	#else
+		#define EASTDC_API
+		#define EASTDC_LOCAL
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_MEMORY_INLINE_ENABLED
+//
+// Defined as 0 or 1, with 1 being the default.
+// This controls whether EAMemory functions are inlined or not in builds.
+// The advantage of them being inlined is that they can pass straight through
+// to inlinable code. The disadvantage is increased code size and lack of 
+// diagnostic functionality that's available when not inlined.
+//
+#if !defined(EASTDC_MEMORY_INLINE_ENABLED)
+	#define EASTDC_MEMORY_INLINE_ENABLED 1
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_VSNPRINTF8_ENABLED
+//
+// *** This option is deprecated. ***
+// Defined as 0 or 1. Default is 0.
+// If enabled then Vsnprintf8 and Vsnprintf16 are enabled. These are functions
+// which simply call Vsnprintf(char8_t..) and Vsnprintf(char16_t). Vsnprintf8
+// is the older name for this function and is deprecated. However, some existing
+// code uses Vsnprintf8 (most notably the EASTL package).
+//
+#ifndef EASTDC_VSNPRINTF8_ENABLED
+	#define EASTDC_VSNPRINTF8_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_MEMCPY16_ENABLED
+//
+// Defined as 0 or 1. Default is 1.
+// The Memcpy(char16_t*, const char16_t*, size_t) function and Memmove 
+// equivalent have been deprecated. For the time being we have an option 
+// to control their existence. 
+//
+#ifndef EASTDC_MEMCPY16_ENABLED
+	#define EASTDC_MEMCPY16_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_MEMCPY16_ENABLED
+//
+// Defined as 0 or 1. Default is 0.
+//
+#ifndef EASTDC_MEMCHR16_ENABLED
+	#define EASTDC_MEMCHR16_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_CHAR32_SUPPORT_ENABLED
+//
+// Defined as 0 or 1. Default is 1 except on platforms where it would never be used.
+// Defines whether functions that use char32_t are supported. If not then there
+// are no available declarations or definitions of char32_t functions (e.g. Sprintf(char32_t*,...)
+// Note that some char32_t functionality might nevertheless be enabled even if 
+// EASTDC_CHAR32_SUPPORT_ENABLED is 0. In these cases it's expected that the 
+// C/C++ linker will link away such functions.
+//
+#ifndef EASTDC_CHAR32_SUPPORT_ENABLED
+	#define EASTDC_CHAR32_SUPPORT_ENABLED 1
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_GLOBALPTR_SUPPORT_ENABLED
+//
+// Defined as 0 or 1. Default is 1.
+// See include/EAStdC/EAGlobal.h for a full description on this feature.
+#ifndef EASTDC_GLOBALPTR_SUPPORT_ENABLED
+	#define EASTDC_GLOBALPTR_SUPPORT_ENABLED 1
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASprintf configuration parameters
+//
+#ifndef EASPRINTF_FIELD_MAX                 // Defines the maximum supported length of a field, except string fields, which have no size limit. 
+	#if defined(EA_PLATFORM_UNIX)           // This value relates to the size of buffers used in the stack space.
+		#define EASPRINTF_FIELD_MAX 4096
+	#elif defined(EA_PLATFORM_DESKTOP)
+		#define EASPRINTF_FIELD_MAX 3600    // Much higher than this and it could risk problems with stack space.
+	#else
+		#define EASPRINTF_FIELD_MAX 1024    // The reason it is 1024 and not the 4095 that the C99 Standard specifies is that 4095 can blow up the stack on some platforms (e.g. PS3).
+	#endif
+#endif
+
+#ifndef EASPRINTF_MS_STYLE_S_FORMAT         // Microsoft uses a non-standard interpretation of the %s field type. 
+	#define EASPRINTF_MS_STYLE_S_FORMAT 1   // For wsprintf MSVC interprets %s as a wchar_t string and %S as a char string.
+#endif                                      // You can make your code portable by using %hs and %ls to force the type.
+
+#ifndef EASPRINTF_SNPRINTF_C99_RETURN       // The C99 standard specifies that snprintf returns the required strlen 
+	#define EASPRINTF_SNPRINTF_C99_RETURN 1 // of the output, which may or may not be less than the supplied buffer size. 
+#endif                                      // Some snprintf implementations instead return -1 if the supplied buffer is too small.
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASTDC_THREADING_SUPPORTED
+// 
+// Defined as 0 or 1. Default is 1 (enabled).
+// Specifies if code that uses multithreading is available. If enabled then
+// this package is dependent on the EAThread package.
+//
+#ifndef EASTDC_THREADING_SUPPORTED
+	#define EASTDC_THREADING_SUPPORTED 1
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_VALGRIND_ENABLED
+//
+// Defined as 0 or 1. It's value depends on the compile environment.
+// Specifies whether the code is being built with Valgrind instrumentation.
+// Note that you can detect valgrind at runtime via getenv("RUNNING_ON_VALGRIND")
+//
+#if !defined(EASTDC_VALGRIND_ENABLED)
+	#define EASTDC_VALGRIND_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_ASAN_ENABLED
+//
+// Defined as 0 or 1. It's value depends on the compile environment.
+// Specifies whether the code is being built with Clang's Address Sanitizer.
+//
+#if defined(__has_feature)
+	#if __has_feature(address_sanitizer)
+		#define EASTDC_ASAN_ENABLED 1
+	#else
+		#define EASTDC_ASAN_ENABLED 0
+	#endif
+#else
+	#define EASTDC_ASAN_ENABLED 0
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_STATIC_ANALYSIS_ENABLED
+//
+// Defined as 0 or 1. It's value depends on the compile environment.  This is
+// generic compile-time flag that indicates EAStdc is running under static
+// analysis environment.  This is important because specific string
+// optimizations are disabled which are correctly flagged as errors but in
+// practice do not cause any harm.
+//
+#if !defined(EASTDC_STATIC_ANALYSIS_ENABLED)
+	#if EASTDC_ASAN_ENABLED || EASTDC_VALGRIND_ENABLED
+		#define EASTDC_STATIC_ANALYSIS_ENABLED 1
+	#else
+		#define EASTDC_STATIC_ANALYSIS_ENABLED 0
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operator new
+//
+// A user-provided operator new of the following types is required.
+// Note that the array versions of this are the same operator new used/required by EASTL.
+//
+// Example usage:
+//    SomeClass* pObject = new("SomeClass") SomeClass(1, 2, 3);
+//    SomeClass* pObject = new("SomeClass", 0, 0, __FILE__, __LINE__) SomeClass(1, 2, 3);
+//    SomeClass* pObject = new("SomeClass", EA::Allocator::kFlagNormal, 1 << EA::Allocator::GeneralAllocatorDebug::kDebugDataIdCallStack) SomeClass(1, 2, 3);
+//
+/// Example usage:
+///    SomeClass* pArray = new("SomeClass", EA::Allocator::kFlagPermanent, __FILE__, __LINE__) SomeClass(1, 2, 3)[4];
+///    SomeClass* pArray = new("SomeClass", EA::Allocator::kFlagNormal, 1 << EA::Allocator::GeneralAllocatorDebug::kDebugDataIdCallStack, __FILE__, __LINE__) SomeClass(1, 2, 3)[4];
+//
+void* operator new     (size_t size, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void* operator new[]   (size_t size, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void* operator new     (size_t size, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void* operator new[]   (size_t size, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+
+void  operator delete  (void* p, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void  operator delete[](void* p, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void  operator delete  (void* p, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+void  operator delete[](void* p, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_ALLOC_PREFIX
+//
+// Defined as a string literal. Defaults to this package's name.
+// Can be overridden by the user by predefining it or by editing this file.
+// This define is used as the default name used by this package for naming
+// memory allocations and memory allocators.
+//
+// All allocations names follow the same naming pattern:
+//     <package>/<module>[/<specific usage>]
+// 
+// Example usage:
+//     void* p = pCoreAllocator->Alloc(37, EASTDC_ALLOC_PREFIX, 0);
+//
+// Example usage:
+//     gMessageServer.GetMessageQueue().get_allocator().set_name(EASTDC_ALLOC_PREFIX "MessageSystem/Queue");
+//
+#ifndef EASTDC_ALLOC_PREFIX
+	#define EASTDC_ALLOC_PREFIX "EAStdC/"
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_USE_STANDARD_NEW
+//
+// Defines whether we use the basic standard operator new or the named
+// extended version of operator new, as per the EASTL package.
+//
+#ifndef EASTDC_USE_STANDARD_NEW
+	#if EASTDC_DLL  // A DLL must provide its own implementation of new, so we just use built-in new.
+		#define EASTDC_USE_STANDARD_NEW 1
+	#else
+		#define EASTDC_USE_STANDARD_NEW 0
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_NEW
+//
+// This is merely a wrapper for operator new which can be overridden and 
+// which has debug/release forms.
+//
+// Example usage:
+//    SomeClass* pObject = EASTDC_NEW("SomeClass") SomeClass(1, 2, 3);
+//
+#ifndef EASTDC_NEW
+	#if EASTDC_USE_STANDARD_NEW
+			#define EASTDC_NEW(name)                            new
+			#define EASTDC_NEW_ALIGNED(alignment, offset, name) new
+			#define EASTDC_DELETE                               delete
+	#else
+		#if defined(EA_DEBUG)
+			#define EASTDC_NEW(name)                            new(name, 0, 0, __FILE__, __LINE__)
+			#define EASTDC_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, __FILE__, __LINE__)
+			#define EASTDC_DELETE                               delete
+		#else
+			#define EASTDC_NEW(name)                            new(name, 0, 0, 0, 0)
+			#define EASTDC_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, 0, 0)
+			#define EASTDC_DELETE                               delete
+		#endif
+	#endif
+#endif
+
+
+#if !defined(EA_ASSERT_HEADER)
+#define EA_ASSERT_HEADER <EAAssert/eaassert.h>
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+//
+// Defined as 0 or 1. 
+// If 1 then CPU cycle counts are used instead of system timer counts.
+// For systems where CPU frequencies are stable, this should be defined.
+//
+#ifndef EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+	#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)
+		#define EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE 0 // x86 rdtsc is unreliable.
+	#else
+		#define EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE 1
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_STOPWATCH_OVERHEAD_ENABLED
+//
+// Defined as 0 or 1. Default depends on the platform.
+// When defined as 1 the overhead is esimated on startup and applied to 
+// timing events.
+// On some systems the overhead of reading the current time is small
+// enough that we consider it insiginificant, but on some others it is not. 
+// We consider significance to mean more than ~100 CPU clock ticks.
+//
+#ifndef EASTDC_STOPWATCH_OVERHEAD_ENABLED
+	#if defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_DESKTOP)
+		#define EASTDC_STOPWATCH_OVERHEAD_ENABLED 1
+	#else
+		#define EASTDC_STOPWATCH_OVERHEAD_ENABLED 0
+	#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_SCANF_WARNINGS_ENABLED
+//
+// Defined as 0 or 1. Default is 0 (disabled).
+// If enabled then scanf execution that is not an error but could be considered
+// a warning is reported. An example is a character format conversion that loses
+// information.
+//
+#ifndef EASTDC_SCANF_WARNINGS_ENABLED
+	#define EASTDC_SCANF_WARNINGS_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_PRINTF_WARNINGS_ENABLED
+//
+// Defined as 0 or 1. Default is 0 (disabled).
+// If enabled then printf execution that is not an error but could be considered
+// a warning is reported. An example is a character format conversion that loses
+// information.
+//
+#ifndef EASTDC_PRINTF_WARNINGS_ENABLED
+	#define EASTDC_PRINTF_WARNINGS_ENABLED 0
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_XXXXX_AVAILABLE
+// Similar functionality is present in recent versions of EABase via <eahave.h>.
+// To do: Migrate to using EABase versions of these.
+//
+// Defined as 0 or 1. 
+// Tells if various headers are available. Some platforms/compile targets don't
+// fully support standard C/C++ libraries.
+//
+#ifndef EASTDC_TIME_H_AVAILABLE             // time.h header and associated functionality.
+		#define EASTDC_TIME_H_AVAILABLE 1
+#endif
+
+#ifndef EASTDC_SYS_TIME_H_AVAILABLE             // sys/time.h header and associated functionality.
+	#if defined(EA_PLATFORM_POSIX) || defined(__APPLE__)
+		#define EASTDC_SYS_TIME_H_AVAILABLE 1
+	#else
+		#define EASTDC_SYS_TIME_H_AVAILABLE 0
+	#endif
+#endif
+
+#ifndef EASTDC_SYS__TIMEVAL_H_AVAILABLE         // sys/_timeval.h header and associated functionality.
+	#if defined(EA_PLATFORM_FREEBSD)
+		#define EASTDC_SYS__TIMEVAL_H_AVAILABLE 1
+	#else
+		#define EASTDC_SYS__TIMEVAL_H_AVAILABLE 0
+	#endif
+#endif
+
+#ifndef EASTDC_LOCALE_H_AVAILABLE               // locale.h header and associated functionality.
+		#define EASTDC_LOCALE_H_AVAILABLE 1
+#endif
+
+#ifndef EASTDC_SYS_MMAN_H_AVAILABLE             // sys/mman.h header and associated functionality
+	#if defined(EA_PLATFORM_POSIX)
+		#define EASTDC_SYS_MMAN_H_AVAILABLE 1
+	#else
+		#define EASTDC_SYS_MMAN_H_AVAILABLE 0
+	#endif
+#endif
+
+#ifndef EASTDC_SYS_WAIT_H_AVAILABLE             // sys/wait.h header and associated functionality
+	#if defined(EA_PLATFORM_UNIX)
+		#define EASTDC_SYS_WAIT_H_AVAILABLE 1
+	#else
+		#define EASTDC_SYS_WAIT_H_AVAILABLE 0
+	#endif
+#endif
+
+#ifndef EASTDC_FILE_AVAILABLE                   // FILE io, such as fopen, fread.
+		#define EASTDC_FILE_AVAILABLE 1
+#endif
+
+#ifndef EASTDC_UNIX_TZNAME_AVAILABLE            // The global tzname variable.
+	#if defined(EA_PLATFORM_UNIX)
+		#define EASTDC_UNIX_TZNAME_AVAILABLE 1
+	#else
+		#define EASTDC_UNIX_TZNAME_AVAILABLE 0
+	#endif
+#endif
+
+#ifndef EASTDC_CLOCK_GETTIME_AVAILABLE          // The clock_gettime function, which is an alternative to gettimeofday.
+	//#if defined(CLOCK_REALTIME) // Disabled until we can get usage of this better.
+	//    #define EASTDC_CLOCK_GETTIME_AVAILABLE 1
+	//#else
+		#define EASTDC_CLOCK_GETTIME_AVAILABLE 0
+	//#endif
+#endif
+
+
+// EA_COMPILER_HAS_BUILTIN
+//
+// Present in recent versions of EABase; provided here for backward compatibility.
+//
+#ifndef EA_COMPILER_HAS_BUILTIN
+	#if defined(__clang__)
+		#define EA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
+	#else
+		#define EA_COMPILER_HAS_BUILTIN(x) 0
+	#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// EASTDC_SSE_POPCNT
+//
+// Defined as 1 or undefined.
+// Implements support for x86 POPCNT instruction. We do not use __builtin_popcnt()
+// from gcc/clang for example as those will compile to a table-based lookup or
+// some other software implementation on processors with SSE version < 4.2
+// but we have our own software implementation we want to fall back on
+// popcnt instruction was added in SSE4.2 starting with Nehalem on Intel and
+// SSE4A starting with Barcelona on AMD
+//
+// x86 Android and OSX require popcnt target feature enabled on clang inorder to compile
+// which is why they are excluded for now
+#ifndef EASTDC_SSE_POPCNT
+	#if ((defined(EA_SSE4_2) && EA_SSE4_2) || (defined(EA_SSE4A) && EA_SSE4A)) && \
+		(!defined(EA_PLATFORM_OSX) && !defined(EA_PLATFORM_ANDROID))
+		#define EASTDC_SSE_POPCNT 1
+	#endif
+#endif
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EABase fallbacks
+/////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_CHAR16
+// Present in recent versions of EABase.
+//
+// EA_CHAR16 is defined in EABase 2.0.20 and later. If we are using an earlier
+// version of EABase then we replicate what EABase 2.0.20 does.
+//
+#ifndef EA_CHAR16
+	#if !defined(EA_CHAR16_NATIVE)
+		#if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) && _HAS_CHAR16_T_LANGUAGE_SUPPORT // VS2010+
+			#define EA_CHAR16_NATIVE 1
+		#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 404) && (defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__STDC_VERSION__)) // g++ (C++ compiler) 4.4+ with -std=c++0x or gcc (C compiler) 4.4+ with -std=gnu99
+			#define EA_CHAR16_NATIVE 1
+		#else
+			#define EA_CHAR16_NATIVE 0
+		#endif
+	#endif
+
+	#if EA_CHAR16_NATIVE && !defined(_MSC_VER) // Microsoft doesn't support char16_t string literals.
+		#define EA_CHAR16(s) u ## s
+	#elif (EA_WCHAR_SIZE == 2)
+		#define EA_CHAR16(s) L ## s
+	#endif
+#endif
+
+
+// ------------------------------------------------------------------------
+// EA_UNUSED
+// Present in recent versions of EABase.
+// 
+// Makes compiler warnings about unused variables go away.
+//
+// Example usage:
+//    void Function(int x)
+//    {
+//        int y;
+//        EA_UNUSED(x);
+//        EA_UNUSED(y);
+//    }
+//
+#ifndef EA_UNUSED
+	// The EDG solution below is pretty weak and needs to be augmented or replaced.
+	// It can't handle the C language, is limited to places where template declarations
+	// can be used, and requires the type x to be usable as a functions reference argument. 
+	#if defined(__cplusplus) && defined(__EDG__)
+		template <typename T>
+		inline void EABaseUnused(T const volatile & x) { (void)x; }
+		#define EA_UNUSED(x) EABaseUnused(x)
+	#else
+		#define EA_UNUSED(x) (void)x
+	#endif
+#endif
+
+
+// ------------------------------------------------------------------------
+// EA_DISABLE_CLANG_WARNING / EA_RESTORE_CLANG_WARNING
+// Present in recent versions of EABase.
+//
+// Example usage:
+//     // Only one warning can be ignored per statement, due to how clang works.
+//     EA_DISABLE_CLANG_WARNING(-Wuninitialized)
+//     EA_DISABLE_CLANG_WARNING(-Wunused)
+//     <code>
+//     EA_RESTORE_CLANG_WARNING()
+//     EA_RESTORE_CLANG_WARNING()
+//
+#ifndef EA_DISABLE_CLANG_WARNING
+	#if defined(EA_COMPILER_CLANG)
+		#define EACLANGWHELP0(x) #x
+		#define EACLANGWHELP1(x) EACLANGWHELP0(clang diagnostic ignored x)
+		#define EACLANGWHELP2(x) EACLANGWHELP1(#x)
+
+		#define EA_DISABLE_CLANG_WARNING(w)   \
+			_Pragma("clang diagnostic push")  \
+			_Pragma(EACLANGWHELP2(w))
+	#else
+		#define EA_DISABLE_CLANG_WARNING(w)
+	#endif
+#endif
+
+#ifndef EA_RESTORE_CLANG_WARNING
+	#if defined(EA_COMPILER_CLANG)
+		#define EA_RESTORE_CLANG_WARNING()    \
+			_Pragma("clang diagnostic pop")
+	#else
+		#define EA_RESTORE_CLANG_WARNING()
+	#endif
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EA_HAVE_WCHAR_IMPL
+// 
+#if !defined(EA_HAVE_localtime_DECL) && !defined(EA_NO_HAVE_localtime_DECL)
+		#define EA_HAVE_localtime_DECL 1
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EA_DISABLE_ALL_VC_WARNINGS
+//
+// This is defined in newer versions of EABase, but we temporarily define it here for backward compatibility.
+// To do: Remove this in July 2014, at which point the EABase version will be two years old.
+// 
+#ifndef EA_DISABLE_ALL_VC_WARNINGS
+	#if defined(_MSC_VER)
+		#define EA_DISABLE_ALL_VC_WARNINGS()  \
+			__pragma(warning(push, 0)) \
+			__pragma(warning(disable: 4244 4267 4350 4509 4548 4710 4985 6320)) // Some warnings need to be explicitly called out.
+	#else
+		#define EA_DISABLE_ALL_VC_WARNINGS()
+	#endif
+#endif
+
+#ifndef EA_RESTORE_ALL_VC_WARNINGS
+	#if defined(_MSC_VER)
+		#define EA_RESTORE_ALL_VC_WARNINGS()  \
+			__pragma(warning(pop))
+	#else
+		#define EA_RESTORE_ALL_VC_WARNINGS()
+	#endif
+#endif
+
+#endif // Header include guard

+ 158 - 0
include/EAStdC/internal/EAMemory.inl

@@ -0,0 +1,158 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+// No include guards should be necessary since this is an internal file which
+// is only ever #included in one place.
+
+
+#include <EAAssert/eaassert.h>
+
+
+// If EASTDC_MEMORY_INLINE_ENABLED is 1 (usually optimized builds) then this .inl file is 
+// #included from EAMemory.h and thus we want the functions to be declared inline and don't 
+// want them ever declared with DLL-export tags like EASTDC_API does. But otherwise (usually
+// debug builds) this .inl file is #included from EAMemory.cpp and we don't want 'inline' 
+// but do want EASTDC_API.
+#if EASTDC_MEMORY_INLINE_ENABLED
+	#define EASTDC_EAMEMORY_DECL inline
+#else
+	#define EASTDC_EAMEMORY_DECL EASTDC_API  // Maps to __declspec(dllexport) in DLL-builds (with VC++).
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	EASTDC_EAMEMORY_DECL void Memclear(void* pDestination, size_t n)
+	{
+		memset(pDestination, 0, n);
+	}
+
+
+	EASTDC_EAMEMORY_DECL void MemclearC(void* pDestination, size_t n)
+	{
+		memset(pDestination, 0, n);
+	}
+
+
+	EASTDC_EAMEMORY_DECL uint8_t* Memset8(void* pDestination, uint8_t c, size_t uint8Count)
+	{
+		return (uint8_t*)memset(pDestination, c, uint8Count);
+	}
+
+
+	EASTDC_EAMEMORY_DECL uint8_t* Memset8C(void* pDestination, uint8_t c, size_t uint8Count)
+	{
+		if(c == 0)
+		{
+			Memclear(pDestination, uint8Count);
+			return (uint8_t*)pDestination;
+		}
+
+		return (uint8_t*)memset(pDestination, c, uint8Count);
+	}
+
+
+	EASTDC_EAMEMORY_DECL uint8_t* Memset8_128(void* pDestination, uint8_t c, size_t uint8Count)
+	{
+		// To do: Make an optimized version of this.
+		return (uint8_t*)memset(pDestination, c, uint8Count);
+	}
+
+
+	EASTDC_EAMEMORY_DECL uint8_t* Memset8_128C(void* pDestination, uint8_t c, size_t uint8Count)
+	{
+		if(c == 0)
+		{
+			Memclear(pDestination, uint8Count);
+			return (uint8_t*)pDestination;
+		}
+
+		return (uint8_t*)memset(pDestination, c, uint8Count);
+	}
+
+
+	EASTDC_EAMEMORY_DECL void* MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount)
+	{
+		#if (EA_PLATFORM_PTR_SIZE == 8)
+			return Memset64(pDestination, (uint64_t)(uintptr_t)pValue, ptrCount);
+		#else
+			return Memset32(pDestination, (uint32_t)(uintptr_t)pValue, ptrCount);
+		#endif
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* Memcpy(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
+	{
+		EA_ASSERT((pSource      >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
+				  (pDestination >= (const uint8_t*)pSource      + nByteCount));
+
+			// Some compilers offer __builtin_memcpy, but we haven't found it to be faster than memcpy for any platforms
+			// and it's significantly slower than memcpy for some platform/compiler combinations (e.g. SN compiler on PS3).
+			return (char8_t*)memcpy(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* MemcpyC(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
+	{
+		EA_ASSERT((pSource      >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
+				  (pDestination >= (const uint8_t*)pSource      + nByteCount));
+
+		return (char8_t*)memcpy(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* MemcpyS(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
+	{
+		EA_ASSERT((pSource      >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
+				  (pDestination >= (const uint8_t*)pSource      + nByteCount));
+
+
+		return (char8_t*)memcpy(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* Memcpy128(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
+	{
+		EA_ASSERT((pSource      >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
+				  (pDestination >= (const uint8_t*)pSource      + nByteCount));
+
+		// This is expected to work with both cacheable and uncacheable memory, 
+		// thus we can't use all alternative optimized functions that exist for memcpy.
+		return (char8_t*)memcpy(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* Memcpy128C(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
+	{
+		EA_ASSERT((pSource      >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
+				  (pDestination >= (const uint8_t*)pSource      + nByteCount));
+
+		return (char8_t*)memcpy(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* Memmove(void* pDestination, const void* pSource, size_t nByteCount)
+	{
+		// Some compilers offer __builtin_memmove, but we haven't found it to be faster than memcpy for any platforms
+		// and it's significantly slower than memcpy for some platform/compiler combinations (e.g. SN compiler on PS3).
+		return (char8_t*)memmove(pDestination, pSource, nByteCount);
+	}
+
+
+	EASTDC_EAMEMORY_DECL char8_t* MemmoveC(void* pDestination, const void* pSource, size_t nByteCount)
+	{
+		return (char8_t*)memmove(pDestination, pSource, nByteCount);
+	}
+
+} // namespace StdC
+} // namespace EA
+
+
+// See the top of this file for #define EASTDC_API and an explanation of it.
+#undef EASTDC_EAMEMORY_DECL
+

+ 706 - 0
include/EAStdC/internal/IntrusiveList.h

@@ -0,0 +1,706 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This is a curtailed version of eastl::intrusive_list.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_INTRUSIVELIST_H
+#define EASTDC_INTRUSIVELIST_H
+
+
+#include <EABase/eabase.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+// Disabled because we want to remove our dependence on STL.
+//
+//#ifdef _MSC_VER
+//    #pragma warning(push, 0)
+//#endif
+//#include <iterator>
+//#ifdef _MSC_VER
+//    #pragma warning(pop)
+//#endif
+
+
+namespace EA
+{
+	namespace StdC
+	{
+
+		template <typename Category, typename T, typename Distance = ptrdiff_t, 
+				  typename Pointer = T*, typename Reference = T&>
+		struct iterator
+		{
+			typedef Category  iterator_category;
+			typedef T         value_type;
+			typedef Distance  difference_type;
+			typedef Pointer   pointer;
+			typedef Reference reference;
+		};
+
+
+		/* Disabled because we want to remove our dependence on STL.
+
+		// struct iterator_traits
+		template <typename Iterator>
+		struct iterator_traits
+		{
+			typedef typename Iterator::iterator_category iterator_category;
+			typedef typename Iterator::value_type        value_type;
+			typedef typename Iterator::difference_type   difference_type;
+			typedef typename Iterator::pointer           pointer;
+			typedef typename Iterator::reference         reference;
+		};
+
+		template <typename T>
+		struct iterator_traits<T*>
+		{
+			typedef std::random_access_iterator_tag          iterator_category;
+			typedef T                                        value_type;
+			typedef ptrdiff_t                                difference_type;
+			typedef T*                                       pointer;
+			typedef T&                                       reference;
+		};
+
+		template <typename T>
+		struct iterator_traits<const T*>
+		{
+			typedef std::random_access_iterator_tag          iterator_category;
+			typedef T                                        value_type;
+			typedef ptrdiff_t                                difference_type;
+			typedef const T*                                 pointer;
+			typedef const T&                                 reference;
+		};
+
+
+		/// reverse_iterator
+		///
+		/// From the C++ standard:
+		/// Bidirectional and random access iterators have corresponding reverse 
+		/// iterator adaptors that iterate through the data structure in the 
+		/// opposite direction. They have the same signatures as the corresponding 
+		/// iterators. The fundamental relation between a reverse iterator and its 
+		/// corresponding iterator i is established by the identity:
+		///     &*(reverse_iterator(i)) == &*(i - 1).
+		/// This mapping is dictated by the fact that while there is always a pointer 
+		/// past the end of an array, there might not be a valid pointer before the
+		/// beginning of an array.
+		///
+		template <typename Iterator>
+		class reverse_iterator : public iterator<typename EA::StdC::iterator_traits<Iterator>::iterator_category,
+												 typename EA::StdC::iterator_traits<Iterator>::value_type,
+												 typename EA::StdC::iterator_traits<Iterator>::difference_type,
+												 typename EA::StdC::iterator_traits<Iterator>::pointer,
+												 typename EA::StdC::iterator_traits<Iterator>::reference>
+		{
+		public:
+			typedef Iterator                                                      iterator_type;
+			typedef typename EA::StdC::iterator_traits<Iterator>::pointer         pointer;
+			typedef typename EA::StdC::iterator_traits<Iterator>::reference       reference;
+			typedef typename EA::StdC::iterator_traits<Iterator>::difference_type difference_type;
+
+		protected:
+			Iterator mIterator;
+
+		public:
+			reverse_iterator()      // It's important that we construct mIterator, because if Iterator  
+				: mIterator() { }   // is a pointer, there's a difference between doing it and not.
+
+			explicit reverse_iterator(iterator_type i)
+				: mIterator(i) { }
+
+			reverse_iterator(const reverse_iterator& ri)
+				: mIterator(ri.mIterator) { }
+
+			template <typename U>
+			reverse_iterator(const reverse_iterator<U>& ri)
+				: mIterator(ri.base()) { }
+
+			// This operator= isn't in the standard, but the the C++ 
+			// library working group has tentatively approved it, as it
+			// allows const and non-const reverse_iterators to interoperate.
+			template <typename U>
+			reverse_iterator<Iterator>& operator=(const reverse_iterator<U>& ri)
+				{ mIterator = ri.base(); return *this; }
+
+			iterator_type base() const
+				{ return mIterator; }
+
+			reference operator*() const
+			{
+				iterator_type i(mIterator);
+				return *--i;
+			}
+
+			pointer operator->() const
+				{ return &(operator*()); }
+
+			reverse_iterator& operator++()
+				{ --mIterator; return *this; }
+
+			reverse_iterator operator++(int)
+			{
+				reverse_iterator ri(*this);
+				--mIterator;
+				return ri;
+			}
+
+			reverse_iterator& operator--()
+				{ ++mIterator; return *this; }
+
+			reverse_iterator operator--(int)
+			{
+				reverse_iterator ri(*this);
+				++mIterator;
+				return ri;
+			}
+
+			reverse_iterator operator+(difference_type n) const
+				{ return reverse_iterator(mIterator - n); }
+
+			reverse_iterator& operator+=(difference_type n)
+				{ mIterator -= n; return *this; }
+
+			reverse_iterator operator-(difference_type n) const
+				{ return reverse_iterator(mIterator + n); }
+
+			reverse_iterator& operator-=(difference_type n)
+				{ mIterator += n; return *this; }
+
+			reference operator[](difference_type n) const
+				{ return mIterator[-n - 1]; } 
+		};
+		*/
+
+
+		/// intrusive_list_node
+		///
+		struct intrusive_list_node
+		{
+			intrusive_list_node* mpNext;
+			intrusive_list_node* mpPrev;
+		};
+
+
+		/// intrusive_list_iterator
+		///
+		template <typename T, typename Pointer, typename Reference>
+		class intrusive_list_iterator
+		{
+		public:
+			typedef intrusive_list_iterator<T, Pointer, Reference>   this_type;
+			typedef intrusive_list_iterator<T, T*, T&>               iterator;
+			typedef intrusive_list_iterator<T, const T*, const T&>   const_iterator;
+			typedef T                                                value_type;
+			typedef T                                                node_type;
+			typedef ptrdiff_t                                        difference_type;
+			typedef Pointer                                          pointer;
+			typedef Reference                                        reference;
+		  //typedef std::bidirectional_iterator_tag                  iterator_category;
+
+		public:
+			pointer mpNode; // Needs to be public for operator==() to work
+
+		public:
+			intrusive_list_iterator();
+			explicit intrusive_list_iterator(pointer pNode);  // Note that you can also construct an iterator from T via this, since value_type == node_type.
+			intrusive_list_iterator(const iterator& x);
+
+			reference operator*() const;
+			pointer   operator->() const;
+
+			intrusive_list_iterator& operator++();
+			intrusive_list_iterator& operator--();
+
+			intrusive_list_iterator operator++(int);
+			intrusive_list_iterator operator--(int);
+
+		}; // class intrusive_list_iterator
+
+
+
+		/// intrusive_list_base
+		///
+		class intrusive_list_base
+		{
+		public:
+			typedef size_t       size_type;     // See config.h for the definition of this, which defaults to uint32_t.
+			typedef ptrdiff_t    difference_type;
+
+		protected:
+			intrusive_list_node mAnchor;          ///< Sentinel node (end). All data nodes are linked in a ring from this node.
+
+		public:
+			intrusive_list_base();
+
+			bool         empty() const;
+			size_t       size() const;              ///< Returns the number of elements in the list; O(n).
+			void         clear();                   ///< Clears the list; O(1). No deallocation occurs.
+			void         pop_front();               ///< Removes an element from the front of the list; O(1). The element must exist, but is not deallocated.
+			void         pop_back();                ///< Removes an element from the back of the list; O(1). The element must exist, but is not deallocated.
+
+		}; // class intrusive_list_base
+
+
+
+		template <typename T = intrusive_list_node>
+		class intrusive_list : public intrusive_list_base
+		{
+		public:
+			typedef intrusive_list<T>                               this_type;
+			typedef intrusive_list_base                             base_type;
+			typedef T                                               node_type;
+			typedef T                                               value_type;
+			typedef typename base_type::size_type                   size_type;
+			typedef typename base_type::difference_type             difference_type;
+			typedef T&                                              reference;
+			typedef const T&                                        const_reference;
+			typedef T*                                              pointer;
+			typedef const T*                                        const_pointer;
+			typedef intrusive_list_iterator<T, T*, T&>              iterator;
+			typedef intrusive_list_iterator<T, const T*, const T&>  const_iterator;
+		  //typedef EA::StdC::reverse_iterator<iterator>            reverse_iterator;
+		  //typedef EA::StdC::reverse_iterator<const_iterator>      const_reverse_iterator;
+
+		public:
+			intrusive_list();                                ///< Creates an empty list.
+			intrusive_list(const this_type& x);              ///< Creates an empty list; ignores the argument.
+			this_type& operator=(const this_type& x);        ///< Clears the list; ignores the argument.
+
+			iterator                begin();                 ///< Returns an iterator pointing to the first element in the list.
+			const_iterator          begin() const;           ///< Returns a const_iterator pointing to the first element in the list.
+			iterator                end();                   ///< Returns an iterator pointing one-after the last element in the list.
+			const_iterator          end() const;             ///< Returns a const_iterator pointing one-after the last element in the list.
+		  //reverse_iterator        rbegin();                ///< Returns a reverse_iterator pointing at the end of the list (start of the reverse sequence).
+		  //const_reverse_iterator  rbegin() const;          ///< Returns a const_reverse_iterator pointing at the end of the list (start of the reverse sequence).
+		  //reverse_iterator        rend();                  ///< Returns a reverse_iterator pointing at the start of the list (end of the reverse sequence).
+		  //const_reverse_iterator  rend() const;            ///< Returns a const_reverse_iterator pointing at the start of the list (end of the reverse sequence).
+			
+			reference               front();                 ///< Returns a reference to the first element. The list must be empty.
+			const_reference         front() const;           ///< Returns a const reference to the first element. The list must be empty.
+			reference               back();                  ///< Returns a reference to the last element. The list must be empty.
+			const_reference         back() const;            ///< Returns a const reference to the last element. The list must be empty.
+
+			void        push_front(T& x);                    ///< Adds an element to the front of the list; O(1). The element is not copied. The element must not be in any other list.
+			void        push_back(T& x);                     ///< Adds an element to the back of the list; O(1). The element is not copied. The element must not be in any other list.
+
+			bool        contains(const T& x) const;          ///< Returns true if the given element is in the list; O(n). Equivalent to (locate(x) != end()).
+
+			iterator        locate(T& x);                    ///< Converts a reference to an object in the list back to an iterator, or returns end() if it is not part of the list. O(n)
+			const_iterator  locate(const T& x) const;        ///< Converts a const reference to an object in the list back to a const iterator, or returns end() if it is not part of the list. O(n)
+
+			iterator    insert(iterator pos, T& x);          ///< Inserts an element before the element pointed to by the iterator. O(1)
+			iterator    erase(iterator pos);                 ///< Erases the element pointed to by the iterator. O(1)
+			iterator    erase(iterator pos, iterator last);  ///< Erases elements within the iterator range [pos, last). O(1)
+			void        swap(intrusive_list&);               ///< Swaps the contents of two intrusive lists; O(1).
+
+			static void remove(T& value);                    ///< Erases an element from a list; O(1). Note that this is static so you don't need to know which list the element, although it must be in some list.
+
+		}; // intrusive_list
+
+
+
+
+		///////////////////////////////////////////////////////////////////////
+		// intrusive_list_iterator
+		///////////////////////////////////////////////////////////////////////
+
+		template <typename T, typename Pointer, typename Reference>
+		inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator()
+		{
+			// Empty
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator(pointer pNode)
+			: mpNode(pNode)
+		{
+			// Empty
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator(const iterator& x)
+			: mpNode(x.mpNode)
+		{
+			// Empty
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::reference
+		intrusive_list_iterator<T, Pointer, Reference>::operator*() const
+		{
+			return *mpNode;
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::pointer
+		intrusive_list_iterator<T, Pointer, Reference>::operator->() const
+		{
+			return mpNode;
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type&
+		intrusive_list_iterator<T, Pointer, Reference>::operator++()
+		{
+			mpNode = static_cast<node_type*>(mpNode->mpNext);
+			return *this;
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type
+		intrusive_list_iterator<T, Pointer, Reference>::operator++(int)
+		{
+			intrusive_list_iterator it(*this);
+			mpNode = static_cast<node_type*>(mpNode->mpNext);
+			return it;
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type&
+		intrusive_list_iterator<T, Pointer, Reference>::operator--()
+		{
+			mpNode = static_cast<node_type*>(mpNode->mpPrev);
+			return *this;
+		}
+
+
+		template <typename T, typename Pointer, typename Reference>
+		inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type
+		intrusive_list_iterator<T, Pointer, Reference>::operator--(int)
+		{
+			intrusive_list_iterator it(*this);
+			mpNode = static_cast<node_type*>(mpNode->mpPrev);
+			return it;
+		}
+
+
+		// The C++ defect report #179 requires that we support comparisons between const and non-const iterators.
+		// Thus we provide additional template paremeters here to support this. The defect report does not
+		// require us to support comparisons between reverse_iterators and const_reverse_iterators.
+		template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
+		inline bool operator==(const intrusive_list_iterator<T, PointerA, ReferenceA>& a, 
+							   const intrusive_list_iterator<T, PointerB, ReferenceB>& b)
+		{
+			return a.mpNode == b.mpNode;
+		}
+
+
+		template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
+		inline bool operator!=(const intrusive_list_iterator<T, PointerA, ReferenceA>& a, 
+							   const intrusive_list_iterator<T, PointerB, ReferenceB>& b)
+		{
+			return a.mpNode != b.mpNode;
+		}
+
+
+		// We provide a version of operator!= for the case where the iterators are of the 
+		// same type. This helps prevent ambiguity errors in the presence of rel_ops.
+		template <typename T, typename Pointer, typename Reference>
+		inline bool operator!=(const intrusive_list_iterator<T, Pointer, Reference>& a, 
+							   const intrusive_list_iterator<T, Pointer, Reference>& b)
+		{
+			return a.mpNode != b.mpNode;
+		}
+
+
+
+
+		///////////////////////////////////////////////////////////////////////
+		// intrusive_list_base
+		///////////////////////////////////////////////////////////////////////
+
+		inline intrusive_list_base::intrusive_list_base() 
+		{
+			mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
+		}
+
+
+		inline bool intrusive_list_base::empty() const
+		{
+			return mAnchor.mpPrev == &mAnchor;
+		}
+
+
+		inline intrusive_list_base::size_type intrusive_list_base::size() const
+		{
+			const intrusive_list_node* p = &mAnchor;
+			size_type n = (size_type)-1;
+
+			do {
+				++n;
+				p = p->mpNext;
+			} while(p != &mAnchor);
+
+			return n;
+		}
+
+
+		inline void intrusive_list_base::clear()
+		{
+			mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
+		}
+
+
+		inline void intrusive_list_base::pop_front()
+		{
+			mAnchor.mpNext->mpNext->mpPrev = &mAnchor;
+			mAnchor.mpNext = mAnchor.mpNext->mpNext;
+		}
+
+
+		inline void intrusive_list_base::pop_back()
+		{
+			mAnchor.mpPrev->mpPrev->mpNext = &mAnchor;
+			mAnchor.mpPrev = mAnchor.mpPrev->mpPrev;
+		}
+
+
+
+
+		///////////////////////////////////////////////////////////////////////
+		// intrusive_list
+		///////////////////////////////////////////////////////////////////////
+
+		template <typename T>
+		inline intrusive_list<T>::intrusive_list()
+		{
+		}
+
+
+		template <typename T>
+		inline intrusive_list<T>::intrusive_list(const this_type& /*x*/)
+		{
+			// We intentionally ignore argument x.
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::this_type& intrusive_list<T>::operator=(const this_type& /*x*/)
+		{ 
+			return *this; // We intentionally ignore argument x.
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::begin()
+		{
+			return iterator(static_cast<T*>(mAnchor.mpNext));
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::const_iterator intrusive_list<T>::begin() const
+		{
+			return const_iterator(static_cast<T*>(mAnchor.mpNext));
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::end()
+		{
+			return iterator(static_cast<T*>(&mAnchor));
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const
+		{
+			return const_iterator(static_cast<const T*>(&mAnchor));
+		}
+
+
+		//template <typename T>
+		//inline typename intrusive_list<T>::reverse_iterator intrusive_list<T>::rbegin()
+		//{
+		//    return reverse_iterator(iterator(static_cast<T*>(&mAnchor)));
+		//}
+
+
+		//template <typename T>
+		//inline typename intrusive_list<T>::const_reverse_iterator intrusive_list<T>::rbegin() const
+		//{
+		//    return const_reverse_iterator(const_iterator(static_cast<const T*>(&mAnchor)));
+		//}
+
+
+		//template <typename T>
+		//inline typename intrusive_list<T>::reverse_iterator intrusive_list<T>::rend()
+		//{
+		//    return reverse_iterator(iterator(static_cast<T*>(mAnchor.mpNext)));
+		//}
+
+
+		//template <typename T>
+		//inline typename intrusive_list<T>::const_reverse_iterator intrusive_list<T>::rend() const
+		//{
+		//    return const_reverse_iterator(const_iterator(static_cast<const T*>(mAnchor.mpNext)));
+		//}
+		
+
+		template <typename T>
+		inline typename intrusive_list<T>::reference intrusive_list<T>::front()
+		{
+			return *static_cast<T*>(mAnchor.mpNext);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::const_reference intrusive_list<T>::front() const
+		{
+			return *static_cast<const T*>(mAnchor.mpNext);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::reference intrusive_list<T>::back()
+		{
+			return *static_cast<T*>(mAnchor.mpPrev);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::const_reference intrusive_list<T>::back() const
+		{
+			return *static_cast<const T*>(mAnchor.mpPrev);
+		}
+
+
+		template <typename T>
+		inline void intrusive_list<T>::push_front(T& x)
+		{
+			x.mpNext = mAnchor.mpNext;
+			x.mpPrev = &mAnchor;
+			mAnchor.mpNext = &x;
+			x.mpNext->mpPrev = &x;
+		}
+
+
+		template <typename T>
+		inline void intrusive_list<T>::push_back(T& x)
+		{
+			x.mpPrev = mAnchor.mpPrev;
+			x.mpNext = &mAnchor;
+			mAnchor.mpPrev = &x;
+			x.mpPrev->mpNext = &x;
+		}
+
+
+		template <typename T>
+		inline bool intrusive_list<T>::contains(const T& x) const
+		{
+			for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
+			{
+				if(p == &x)
+					return true;
+			}
+
+			return false;
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::locate(T& x)
+		{
+			for(intrusive_list_node* p = (T*)mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
+			{
+				if(p == &x)
+					return iterator(static_cast<T*>(p));
+			}
+
+			return iterator((T*)&mAnchor);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::const_iterator intrusive_list<T>::locate(const T& x) const
+		{
+			for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
+			{
+				if(p == &x)
+					return const_iterator(static_cast<const T*>(p));
+			}
+
+			return const_iterator((T*)&mAnchor);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(iterator pos, T& x)
+		{
+			node_type& next = *pos;
+			node_type& prev = *static_cast<node_type*>(next.mpPrev);
+			prev.mpNext = next.mpPrev = &x;
+			x.mpPrev    = &prev;
+			x.mpNext    = &next;
+			return iterator(&x);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(iterator pos)
+		{
+			node_type& prev = *static_cast<node_type*>(pos.mpNode->mpPrev);
+			node_type& next = *static_cast<node_type*>(pos.mpNode->mpNext);
+			prev.mpNext = &next;
+			next.mpPrev = &prev;
+			return iterator(&next);
+		}
+
+
+		template <typename T>
+		inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(iterator pos, iterator last)
+		{
+			node_type& prev = *static_cast<node_type*>(pos.mpNode->mpPrev);
+			node_type& next = *last.mpNode;
+			prev.mpNext = &next;
+			next.mpPrev = &prev;
+			return last;
+		}
+
+
+		template <typename T>
+		void intrusive_list<T>::swap(intrusive_list& x)
+		{
+			// swap anchors
+			const intrusive_list_node temp(mAnchor);
+			mAnchor   = x.mAnchor;
+			x.mAnchor = temp;
+
+			// Fixup node pointers into the anchor, since the addresses of 
+			// the anchors must stay the same with each list.
+			if(mAnchor.mpNext == &x.mAnchor)
+				mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
+			else
+				mAnchor.mpNext->mpPrev = mAnchor.mpPrev->mpNext = &mAnchor;
+
+			if(x.mAnchor.mpNext == &mAnchor)
+				x.mAnchor.mpNext = x.mAnchor.mpPrev = &x.mAnchor;
+			else
+				x.mAnchor.mpNext->mpPrev = x.mAnchor.mpPrev->mpNext = &x.mAnchor;
+		}
+
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+
+
+#endif // Header include guard
+

+ 321 - 0
include/EAStdC/internal/ScanfCore.h

@@ -0,0 +1,321 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_INTERNAL_SCANFCORE_H
+#define EASTDC_INTERNAL_SCANFCORE_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/stdioEA.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+
+
+#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4127)  // conditional expression is constant.
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+namespace ScanfLocal
+{
+
+/////////////////////////////////////////////////////////////////////////////
+// Constants
+/////////////////////////////////////////////////////////////////////////////
+
+typedef float  float32_t;
+typedef double float64_t;
+
+union FloatUint32
+{
+	uint32_t i;
+	float    f;
+};
+
+union DoubleUint64
+{
+	uint64_t i;
+	double   f;
+};
+
+const int          kConversionBufferSize           = EASCANF_FIELD_MAX + 8; // This is the size for a single field's representation, and not the entire formatted string representation. Multiple references say that this value must be at least 509, but I can't find that in the C99 standard.
+const int          kMaxWidth                       = kConversionBufferSize - 8; 
+const int          kMaxPrecision                   = kConversionBufferSize - 8; 
+const int          kNoPrecisionLimit               = INT_MAX;
+const int          kNoWidthLimit                   = INT_MAX;
+const int          kMinDoubleExponent              = DBL_MIN_10_EXP;   // From <float.h>
+const int          kMaxDoubleExponent              = DBL_MAX_10_EXP;   // From <float.h>
+const int          kFormatError                    = 0;
+const int          kMaxSignificandDigits           = 24;
+const uint32_t     kFloat32PositiveInfinityBits    = UINT32_C(0x7F800000);
+const uint64_t     kFloat64PositiveInfinityBits    = UINT64_C(0x7FF0000000000000);
+const FloatUint32  kInfinityUnion32                = { kFloat32PositiveInfinityBits };
+const DoubleUint64 kInfinityUnion64                = { kFloat64PositiveInfinityBits };
+//const float32_t  kFloat32Infinity                = kInfinityUnion32.f;
+//const float64_t  kFloat64Infinity                = kInfinityUnion64.f;
+const uint32_t     kFloat32NANBits                 = UINT32_C(0x7FFFFFFF);
+const uint64_t     kFloat64NANBits                 = UINT64_C(0x7FFFFFFFFFFFFFFF);
+const FloatUint32  kNANUnion32                     = { kFloat32NANBits };
+const DoubleUint64 kNANUnion64                     = { kFloat64NANBits };
+//const float32_t  kFloat32NAN                     = kNANUnion32.f;
+const float64_t    kFloat64NAN                     = kNANUnion64.f;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Enumerations
+/////////////////////////////////////////////////////////////////////////////
+
+enum Alignment          // The C99 standard incorrectly uses the term "justification" when it means to use "alignment". There is no such thing as left or right justification, but thinking so is a common mistake for typography newbies.
+{                       // 
+	kAlignmentLeft,     // 
+	kAlignmentRight,    // 
+	kAlignmentZeroFill  // 
+};
+
+enum Sign
+{
+	kSignNone,          // Never show any sign.
+	kSignMinus,         // Only show sign if minus (this is default).
+	kSignMinusPlus,     // Show sign if plus or minus.
+	kSignSpace          // Show space in place of a plus sign.
+};
+
+enum Modifier
+{
+	kModifierNone,       // No modifier, use the type as-is.
+	kModifierChar,       // Use char8_t instead of int. Specified by hh in front of d, i, o, u, x, or X.
+	kModifierShort,      // Use short instead of int. Specified by h in front of d, i, o, u, x, or X.
+	kModifierInt,        // This is a placeholder, as integer is the default fd for integral types.
+	kModifierLong,       // Use long instead of int. Specified by l in front of d, i, o, u, x, or X.
+	kModifierLongLong,   // Use long long instead of int. Specified by ll in front of d, i, o, u, x, or X.
+	kModifierMax_t,      // Use intmax_t argument. Specified by 'j' in front of d, i, o, u, x, or X.
+	kModifierSize_t,     // Use size_t argument. Specified by 'z' in front of d, i, o, u, x, or X.
+	kModifierPtrdiff_t,  // Use ptrdiff_t argument. Specified by 't' in front of d, i, o, u, x, or X.
+	kModifierDouble,     // Use double instead of float. Specified by nothing in front of e, E, f, F, g, G for printf and l for scanf.
+	kModifierLongDouble, // Use long double instead of double. Specified by l in front of e, E, f, F, g, G for printf and L for scanf.
+	kModifierWChar,      // Use wide char8_t instead of char8_t. Specified by l (in front of c).
+	kModifierInt8,       // Use int8_t or uint8_t.   Specified by I8 in front of d, i, o, u.
+	kModifierInt16,      // Use int16_t or uint16_t. Specified by I16 in front of d, i, o, u.
+	kModifierInt32,      // Use int32_t or uint32_t. Specified by I32 in front of d, i, o, u.
+	kModifierInt64,      // Use int64_t or uint64_t. Specified by I64 in front of d, i, o, u.
+	kModifierInt128      // Use int128_t or uint128_t. Specified by I128 in front of d, i, o, u.
+};
+
+
+enum ReadIntegerState                       // The ^ chars below indicate what part of the string the state refers to.
+{                                           // "   -00123456"
+	kRISLeadingSpace        = 0x0001,       //  ^
+	kRISZeroTest            = 0x0002,       //     ^
+	kRISAfterZero           = 0x0004,       //        ^
+	kRISReadFirstDigit      = 0x0008,       //        ^
+	kRISReadDigits          = 0x0010,       //         ^ 
+	kRISEnd                 = 0x0020,       //              ^
+	kRISError               = 0x0040        //              ^
+};
+
+enum ReadDoubleState                        // The ^ chars below indicate what part of the string the state refers to.
+{                                           // "   -123.345e-0023"
+	kRDSLeadingSpace        = 0x0001,       //  ^
+	kRDSSignificandBegin    = 0x0002,       //     ^
+	kRDSSignificandLeading  = 0x0004,       //     ^
+	kRDSIntegerDigits       = 0x0008,       //      ^
+	kRDSFractionBegin       = 0x0010,       //          ^
+	kRDSFractionLeading     = 0x0020,       //          ^
+	kRDSFractionDigits      = 0x0040,       //           ^
+	kRDSSignificandEnd      = 0x0080,       //             ^
+	kRDSExponentBegin       = 0x0100,       //              ^
+	kRDSExponentBeginDigits = 0x0200,       //              ^
+	kRDSExponentLeading     = 0x0400,       //               ^
+	kRDSExponentDigits      = 0x0800,       //                ^
+	kRDSInfinity            = 0x1000,       //                   ^
+	kRDSNAN                 = 0x2000,       //                   ^
+	kRDSEnd                 = 0x4000,       //                   ^
+	kRDSError               = 0x8000        //                   ^
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CharBitmap
+//
+// Used to do fast char set tests.
+/////////////////////////////////////////////////////////////////////////////
+
+struct CharBitmap
+{
+	uint32_t mBits[8];  // 32 bits per uint32_t times 8 values => 256 bits.
+
+	CharBitmap()
+		{ memset(mBits, 0, sizeof(mBits)); }
+
+	int Get(char8_t c) const
+		{ return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); }
+
+	// This isn't correct. To do this completely right for all uses of scanf, 
+	// we need to make a 16 bit bitmap which handles 65536 bits. However, this
+	// would entail rather obscure scanf %[...]s usage for Unicode text.
+	int Get(char16_t c) const // If c >= 256, we return whatever the first bit is, since it will be equal to what bits 256 - 65536 are meant to be.
+		{ if(c < 256) return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); else return (int)(mBits[0] & 0x00000001); }
+
+	int Get(char32_t c) const // If c >= 256, we return whatever the first bit is, since it will be equal to what bits 256 - 2^32 are meant to be.
+		{ if(c < 256) return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); else return (int)(mBits[0] & 0x00000001); }
+
+	void Set(char8_t c)
+		{ mBits[(uint8_t)(unsigned)c >> 5] |= (1 << (c & 31)); }
+
+	void Set(char16_t c)
+		{ if((uint16_t)c < 256) mBits[(uint8_t)(uint16_t)c >> 5] |= (1 << (c & 31)); }
+
+	void Set(char32_t c)
+		{ if((uint32_t)c < 256) mBits[(uint8_t)(uint32_t)c >> 5] |= (1 << (c & 31)); }
+
+	void SetAll()
+		{ memset(mBits, 0xffffffff, sizeof(mBits)); }
+
+	void NegateAll()
+		{ for(size_t i = 0; i < 8; i++) mBits[i] = ~mBits[i]; }
+
+	void ClearAll()
+		{ memset(mBits, 0, sizeof(mBits)); }
+
+	void Clear(int c)
+		{ mBits[(uint8_t)(unsigned)c >> 5] &= ~(1 << (c & 31)); }
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// DoubleValue
+//
+// Used as the lowest level string representation of a double.
+/////////////////////////////////////////////////////////////////////////////
+
+struct DoubleValue
+{
+	char8_t mSigStr[kMaxSignificandDigits + 1];  // String
+	int16_t mSigLen;                             // Length of string
+	int16_t mExponent;                           // Exponent value.
+
+	DoubleValue()
+	  : mSigLen(0), mExponent(0)
+	{ mSigStr[0] = 0; }
+
+	double ToDouble() const;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// FormatData
+//
+// Used by Scanf's state machine.
+/////////////////////////////////////////////////////////////////////////////
+
+struct FormatData
+{
+	int         mnWidth;            // Field width in characters.
+	Modifier    mModifier;          // One of enum Modifier. Example is 'h' for 'short'.
+	int         mnType;             // 'c', 'C', 'b', 'd', 'i', 'u', 'e', 'E', 'f', 'g', 'G', 'o', 's', 'S', 'x', 'X', 'p', 'n', '%', or 0 (for error).
+	bool        mbWidthSpecified;   // True if the field width was specified by the user.
+	bool        mbSkipAssignment;   // True if the user used * in the format, which means to eat the input field without assigning it to anything.
+	CharBitmap  mCharBitmap;        // Allows fast character inclusion/exclusion tests.
+	int         mDecimalPoint;      // Typically equal to '.', but could be ',' for some locales.
+
+	FormatData()
+	  : mnWidth(kNoWidthLimit),
+		mModifier(kModifierNone),
+		mnType(kFormatError),
+		mbWidthSpecified(false),
+		mbSkipAssignment(false),
+		mCharBitmap(),
+		mDecimalPoint('.')
+	{}
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// SscanfContext8 / SscanfContext16
+//
+// Used by StringReader8 / StringReader16
+/////////////////////////////////////////////////////////////////////////////
+
+struct SscanfContext8
+{
+	const char8_t* mpSource;
+	int            mbEndFound;
+
+	SscanfContext8(const char8_t* pSource = NULL)
+	  : mpSource(pSource),
+		mbEndFound(0)
+	{}
+};
+
+struct SscanfContext16
+{
+	const char16_t* mpSource;
+	int             mbEndFound;
+
+	SscanfContext16(const char16_t* pSource = NULL)
+	  : mpSource(pSource),
+		mbEndFound(0)
+	{}
+};
+
+struct SscanfContext32
+{
+	const char32_t* mpSource;
+	int             mbEndFound;
+
+	SscanfContext32(const char32_t* pSource = NULL)
+	  : mpSource(pSource),
+		mbEndFound(0)
+	{}
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Reader functions, of type ReadFunction8
+/////////////////////////////////////////////////////////////////////////////
+
+int FILEReader8  (ReadAction readAction, int value, void* pContext);
+int FILEReader16 (ReadAction readAction, int value, void* pContext);
+int FILEReader32 (ReadAction readAction, int value, void* pContext);
+
+int StringReader8 (ReadAction readAction, int value, void* pContext);
+int StringReader16(ReadAction readAction, int value, void* pContext);
+int StringReader32(ReadAction readAction, int value, void* pContext);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// VscanfCore
+//
+int VscanfCore(ReadFunction8  pReadFunction8,  void* pReadFunction8Context, const char8_t* pFormat,  va_list arguments);
+int VscanfCore(ReadFunction16 pReadFunction16, void* pReadFunction8Context, const char16_t* pFormat, va_list arguments);
+int VscanfCore(ReadFunction32 pReadFunction32, void* pReadFunction8Context, const char32_t* pFormat, va_list arguments);
+
+
+} // namespace ScanfLocal
+} // namespace StdC
+} // namespace EA
+
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+#endif // Header include guard
+
+

+ 305 - 0
include/EAStdC/internal/SprintfCore.h

@@ -0,0 +1,305 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_INTERNAL_SPRINTFCORE_H
+#define EASTDC_INTERNAL_SPRINTFCORE_H
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/stdioEA.h>
+#include <EAStdC/EASprintf.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// IsNeg
+//
+// bool IsNeg(double x); 
+//
+#if defined(EA_PLATFORM_MICROSOFT)
+	#define IsNeg(x) ((*((uint64_t*)&(x)) & UINT64_C(0x8000000000000000)) != 0) // This assumes that double is a 64 bit value.
+#elif defined(__APPLE__) || defined(EA_PLATFORM_ANDROID)
+	inline  bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
+#elif defined(__CYGWIN__) && defined(__GNUC__)
+	inline  bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
+#else
+	inline  bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
+#endif
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASPRINTF_MIN / EASPRINTF_MAX
+//
+#ifndef EASPRINTF_MIN
+	#define EASPRINTF_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef EASPRINTF_MAX
+	#define EASPRINTF_MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+namespace SprintfLocal
+{
+
+/////////////////////////////////////////////////////////////////////////////
+// Constants
+/////////////////////////////////////////////////////////////////////////////
+
+const int      kConversionBufferSize = EASPRINTF_FIELD_MAX + 8; // This is the size for a single field's representation, and not the entire formatted string representation. Multiple references say that this value must be at least 509, but I can't find that in the C99 standard.
+const int      kMaxWidth             = kConversionBufferSize - 8; 
+const int      kMaxPrecision         = kConversionBufferSize - 8; 
+const int      kNoPrecision          = INT_MAX;
+const int      kFormatError          = 0;
+const char8_t  kStringNull8[]        = { '(', 'n', 'u', 'l', 'l', ')', '\0' }; // Used if the user uses "%s" but passes NULL as the string pointer.
+const char16_t kStringNull16[]       = { '(', 'n', 'u', 'l', 'l', ')', '\0' };
+const char32_t kStringNull32[]       = { '(', 'n', 'u', 'l', 'l', ')', '\0' };
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Enumerations
+/////////////////////////////////////////////////////////////////////////////
+
+enum Alignment          // The C99 standard incorrectly uses the term "justification" when it means to use "alignment". There is no such thing as left or right justification, but thinking so is a common mistake for typography newbies.
+{                       // 
+	kAlignmentLeft,     // 
+	kAlignmentRight,    // 
+	kAlignmentZeroFill  // 
+};
+
+enum Sign
+{
+	kSignNone,          // Never show any sign.
+	kSignMinus,         // Only show sign if minus (this is default).
+	kSignMinusPlus,     // Show sign if plus or minus.
+	kSignSpace          // Show space in place of a plus sign.
+};
+
+enum Modifier
+{
+	kModifierNone,       // No modifier, use the type as-is.
+	kModifierChar,       // Use char8_t instead of int. Specified by hh in front of d, i, o, u, x, or X.
+	kModifierShort,      // Use short instead of int. Specified by h in front of d, i, o, u, x, or X.
+	kModifierInt,        // This is a placeholder, as integer is the default format for integral types.
+	kModifierLong,       // Use long instead of int. Specified by l in front of d, i, o, u, x, or X.
+	kModifierLongLong,   // Use long long instead of int. Specified by ll in front of d, i, o, u, x, or X.
+	kModifierMax_t,      // Use intmax_t argument. Specified by 'j' in front of d, i, o, u, x, or X.
+	kModifierSize_t,     // Use size_t argument. Specified by 'z' in front of d, i, o, u, x, or X.
+	kModifierPtrdiff_t,  // Use ptrdiff_t argument. Specified by 't' in front of d, i, o, u, x, or X.
+	kModifierDouble,     // This is a placeholder, as double is the default format for floating point types.
+	kModifierLongDouble, // Use long double instead of double. Specified by l in front of e, E, f, g, G.
+	kModifierWChar,      // Use wide char8_t instead of char8_t. Specified by l (in front of c).
+	kModifierInt8,       // Use int8_t or uint8_t.     Specified by I8 in front of d, i, o, u.
+	kModifierInt16,      // Use int16_t or uint16_t.   Specified by I16 in front of d, i, o, u.
+	kModifierInt32,      // Use int32_t or uint32_t.   Specified by I32 in front of d, i, o, u.
+	kModifierInt64,      // Use int64_t or uint64_t.   Specified by I64 in front of d, i, o, u.
+	kModifierInt128      // Use int128_t or uint128_t. Specified by I128 in front of d, i, o, u.
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Structs
+/////////////////////////////////////////////////////////////////////////////
+
+union AllTypes
+{
+	char        mChar;
+	short       mShort;
+	int         mInt;
+	long        mLong;
+	long long   mLongLong;
+	intmax_t    mMax;
+	size_t      mSize;
+	ptrdiff_t   mPtrDiff;
+	double      mDouble;
+	long double mLongDouble;
+	wchar_t     mWChar;
+	int8_t      mInt8;
+	int16_t     mInt16;
+	int32_t     mInt32;
+	int64_t     mInt64;
+  //int128_t    mInt128; // Disabled until we support it fully. We have the EAStdC int128_t type we can use.
+};
+
+struct FormatData
+{
+	Alignment mAlignment;           // One of enum Alignment.
+	Sign      mSign;                // One of enum Sign.
+	bool      mbAlternativeForm;    // See the C99 standard, section 7.19.6.1.6.
+	int       mnWidth;              // Field width in characters.
+	int       mnPrecision;          // A value of kNoPrecision means that precision was not specified.
+	Modifier  mModifier;            // One of enum Modifier. Example is 'h' for 'short'.
+	int       mnType;               // 'c', 'C', 'b', 'd', 'i', 'u', 'e', 'E', 'f', 'g', 'G', 'o', 's', 'S', 'x', 'X', 'p', 'n', '%', or 0 (for error).
+	int       mDecimalPoint;        // Typically equal to '.', but could be ',' for some locales.
+	bool      mbDisplayThousands;   // Non-standard extension, though found in a number of Unix compiler C library printf implementations.
+	int       mThousandsSeparator;  // Typically equal to ',' but could be '.' or ' ' for some locales.
+
+	FormatData() 
+	  : mAlignment(kAlignmentRight), // The standard states (7.19.6.1.6) that right alignment is default.
+		mSign(kSignMinus),           // The standard states (7.19.6.1.6) that the default mode is to display the minus sign only.
+		mbAlternativeForm(false),
+		mnWidth(0), 
+		mnPrecision(kNoPrecision),
+		mModifier(kModifierNone), 
+		mnType(kFormatError),
+		mDecimalPoint('.'),          // To do: Make this configurable.
+		mbDisplayThousands(false),
+		mThousandsSeparator(',')     // To do: Make this configurable.
+	{}
+};
+
+
+
+struct SnprintfContext8
+{
+	char8_t* mpDestination;     // Start of destination data. Ptr doesn't change once it has been initialized.
+	size_t   mnCount;           // Count written to destination so far.
+	size_t   mnMaxCount;        // The max count we can write to the destination.
+	bool     mbMaxCountReached; // True if the max count has been reached. Used because multi-byte strings (e.g. UTF8) could end with mnCount < mnMaxCount.
+
+	SnprintfContext8(char8_t* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
+	  : mpDestination(pDestination),
+		mnCount(nCount),
+		mnMaxCount(nMaxCount),
+		mbMaxCountReached(false)
+	{}
+};
+
+struct SnprintfContext16
+{
+	char16_t* mpDestination;
+	size_t    mnCount;
+	size_t    mnMaxCount;
+
+	SnprintfContext16(char16_t* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
+	  : mpDestination(pDestination),
+		mnCount(nCount),
+		mnMaxCount(nMaxCount)
+	{}
+};
+
+struct SnprintfContext32
+{
+	char32_t* mpDestination;
+	size_t    mnCount;
+	size_t    mnMaxCount;
+
+	SnprintfContext32(char32_t* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
+	  : mpDestination(pDestination),
+		mnCount(nCount),
+		mnMaxCount(nMaxCount)
+	{}
+};
+
+#ifdef EA_PLATFORM_ANDROID
+	struct PlatformLogWriterContext8
+	{
+		char8_t mBuffer[512];
+		size_t  mPosition;
+
+		PlatformLogWriterContext8() { mBuffer[0] = 0; mPosition = 0; }
+	};
+#else
+	// No context is required on most platforms
+	struct PlatformLogWriterContext8 {};
+#endif
+
+// Default output writers
+int StringWriter8     (const char8_t*  EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8,  WriteFunctionState wfs);
+int FILEWriter8       (const char8_t*  EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8,  WriteFunctionState wfs);
+int PlatformLogWriter8(const char8_t*  EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8,  WriteFunctionState wfs);
+
+int StringWriter16   (const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState wfs);
+int FILEWriter16     (const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState wfs);
+
+int StringWriter32   (const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState wfs);
+int FILEWriter32     (const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState wfs);
+
+
+// VaListContainer
+//
+// This exists because some compilers (e.g. some versions of GCC, Green Hills) aren't complaint with the 
+// C99 Standard in allowing the taking of the address of a va_list variable. The problem is that some 
+// compilers implement va_list as an array[1], which is converted to a pointer upon passing to a function,
+// and this breaks the ability of us to take its address as a va_list. We work around that by putting 
+// va_list objects in a struct, which can be passed as an argument to a function without changing its type.
+// This struct isn't yet in use while we investigate possible alternative workarounds for the compiler issues.
+//
+struct VaListContainer
+{
+	va_list value;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Internal init/shutdown functions. 
+// The user doesn't need to call these, as these are already called when the 
+// user calls the EAStdC::Init and EAStdC::Shutdown high level functions.
+//
+void EASprintfInit();
+void EASprintfShutdown();
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ReadFormat
+//
+// Reads the current format into FormatData. Return value is pointer to first
+// char8_t/char16_t after the format data.
+//
+// To know how printf truly needs to work, see the ISO C 1999 standard, section 7.19.6.1.
+// See http://www.cplusplus.com/ref/cstdio/printf.html or http://www.opengroup.org/onlinepubs/007908799/xsh/fprintf.html 
+// for decent basic online documentation about how printf is supposed to work.
+// 
+// Argument pFormat is a string pointing to a % format specification of the form:
+//        %[flags][width][.precision][modifiers]type
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+template <typename CharT>
+const CharT* ReadFormat(const CharT* EA_RESTRICT pFormat, SprintfLocal::FormatData* EA_RESTRICT pFormatData, va_list* EA_RESTRICT pArguments);
+
+///////////////////////////////////////////////////////////////////////////////
+// VprintfCore
+//
+int VprintfCore(WriteFunction8  pWriteFunction8,  void* EA_RESTRICT pWriteFunctionContext8,  const char8_t*  EA_RESTRICT pFormat, va_list arguments);
+int VprintfCore(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pWriteFunctionContext16, const char16_t* EA_RESTRICT pFormat, va_list arguments);
+int VprintfCore(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pWriteFunctionContext32, const char32_t* EA_RESTRICT pFormat, va_list arguments);
+
+
+
+} // namespace SprintfLocal
+} // namespace StdC
+} // namespace EA
+
+
+#endif // Header include guard
+
+
+
+
+

+ 85 - 0
include/EAStdC/internal/Thread.h

@@ -0,0 +1,85 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_THREAD_H
+#define EASTDC_THREAD_H
+
+
+#include <EABase/eabase.h>
+#include <eathread/eathread_atomic.h>
+#include <eathread/eathread_mutex.h>
+
+
+namespace EA
+{
+	namespace StdC
+	{
+		/// Safely sets a new value. Returns the old value.
+		uint32_t AtomicSet(uint32_t* pValue, uint32_t newValue);
+
+		/// Safely increments the value. Returns the new value.
+		/// This function acts the same as the C++ pre-increment operator.
+		uint32_t AtomicIncrement(uint32_t* pValue);
+
+		/// Safely decrements the value. Returns the new value.
+		/// This function acts the same as the C++ pre-decrement operator.
+		uint32_t AtomicDecrement(uint32_t* pValue);
+
+		/// Safely sets the value to a new value if the original value is equal to
+		/// a condition value. Returns true if the condition was met and the
+		/// assignment occurred. The comparison and value setting are done as
+		/// an atomic operation and thus another thread cannot intervene between
+		/// the two as would be the case with simple C code.
+		bool AtomicCompareSwap(uint32_t* pValue, uint32_t newValue, uint32_t condition);
+
+
+		/// Mutex
+		///
+		/// Implements a very simple portable Mutex class.
+		///
+		class Mutex
+		{
+		public:
+			void Lock() { mMutex.Lock(); }
+			void Unlock() { mMutex.Unlock(); }
+
+		protected:
+			EA::Thread::Mutex mMutex;
+		};
+	}
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// inline implmentation
+///////////////////////////////////////////////////////////////////////////////
+namespace EA
+{
+	namespace StdC
+	{
+		inline uint32_t AtomicIncrement(uint32_t* pValue) { return EA::Thread::AtomicFetchIncrement(pValue) + 1; }
+
+		inline uint32_t AtomicDecrement(uint32_t* pValue) { return EA::Thread::AtomicFetchDecrement(pValue) - 1; }
+
+		inline uint32_t AtomicSet(uint32_t* pValue, uint32_t newValue)
+		{
+			return EA::Thread::AtomicSetValue(pValue, newValue);
+		}
+
+		inline bool AtomicCompareSwap(uint32_t* pValue, uint32_t newValue, uint32_t condition)
+		{
+			return EA::Thread::AtomicSetValueConditional(pValue, newValue, condition);
+		}
+
+	} // namespace StdC
+
+} // namespace EA
+
+
+
+
+#endif // Header include guard
+

+ 44 - 0
include/EAStdC/internal/stdioEA.h

@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// stdioEA.h
+//
+// Declares elements of stdio.h that are missing from various platforms.
+// Some platform/compiler combinations don't support some or all of the 
+// standard C stdio.h functionality, so we declare the functionality 
+// ourselves here. This doesn't always mean that we implement the functionality.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_INTERNAL_STDIOEA_H
+#define EASTDC_INTERNAL_STDIOEA_H
+
+
+#include <EAStdC/internal/Config.h>
+
+
+#if !EASTDC_FILE_AVAILABLE
+
+	#include <stdio.h>
+
+	//struct FILE
+	//{
+	//    char8_t* mpMemory;
+	//    int32_t  mPosition;
+	//};
+
+	size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
+	size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
+	int    fwide(FILE* stream, int mode);
+	char*  fgets(char* str, int num, FILE* stream);
+	int    fputs(const char* str, FILE* stream);
+	int    fgetc(FILE* stream);
+	int    ungetc(int character, FILE* stream);
+	int    feof(FILE* stream);
+	int    ferror(FILE* stream);
+
+#endif // EASTDC_FILE_AVAILABLE
+
+#endif // Header include guard

+ 15 - 0
include/EAStdC/version.h

@@ -0,0 +1,15 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EASTDC_VERSION_H
+#define EASTDC_VERSION_H
+
+#include <EABase/eabase.h>
+#if defined(EA_PRAGMA_ONCE_SUPPORTED)
+	#pragma once
+#endif
+
+#include <EAStdC/internal/Config.h>
+
+#endif

+ 121 - 0
source/EACType.cpp

@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EACType.h>
+#include <EAAssert/eaassert.h>
+
+
+// Here we define character types in a simplistic way for the first 
+// 256 characters of the Unicode standard. We assume that char16_t refers 
+// to a wide unicode character of at least 8 bits (and usually 16 or 32 bits).
+
+#define wctrl  EASTDC_WCTYPE_CONTROL_1
+#define wmotn  EASTDC_WCTYPE_MOTION
+#define wspac  EASTDC_WCTYPE_SPACE_1
+#define wpunc  EASTDC_WCTYPE_PUNCT
+#define wdigi  EASTDC_WCTYPE_DIGIT
+#define whexd  EASTDC_WCTYPE_XDIGIT
+#define wlowc  EASTDC_WCTYPE_LOWER
+#define wuppc  EASTDC_WCTYPE_UPPER
+#define wdhex  (EASTDC_WCTYPE_XDIGIT | EASTDC_WCTYPE_DIGIT)
+#define wlhex  (EASTDC_WCTYPE_XDIGIT | EASTDC_WCTYPE_LOWER)
+#define wuhex  (EASTDC_WCTYPE_XDIGIT | EASTDC_WCTYPE_UPPER)
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+EASTDC_API uint8_t EASTDC_WCTYPE_MAP[256] =
+{
+	wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wmotn, wmotn, wmotn, wmotn, wmotn, wctrl, wctrl,
+	wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl,
+	wspac, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc,
+	wdhex, wdhex, wdhex, wdhex, wdhex, wdhex, wdhex, wdhex, wdhex, wdhex, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc,
+	wpunc, wuhex, wuhex, wuhex, wuhex, wuhex, wuhex, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc,
+	wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wpunc, wpunc, wpunc, wpunc, wpunc,
+	wpunc, wlhex, wlhex, wlhex, wlhex, wlhex, wlhex, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc,
+	wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wpunc, wpunc, wpunc, wpunc, wctrl,
+	wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl,
+	wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl, wctrl,
+	wspac, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wctrl, wpunc, wpunc,
+	wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc, wpunc,
+	wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc,
+	wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc, wuppc,
+	wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc,
+	wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wpunc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc, wlowc
+};
+
+
+EASTDC_API uint8_t EASTDC_WLOWER_MAP[256] =
+{
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'', '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '[',  '\\', ']',  '^',  '_',
+	'`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '{',  '|',  '}',  '~',  0x7F,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+	0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+	0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+
+
+EASTDC_API uint8_t EASTDC_WUPPER_MAP[256] =
+{
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'', '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
+	'`',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z',  '{',  '|',  '}',  '~',  0x7F,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+	0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+	0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#undef wctrl
+#undef wmotn
+#undef wspac
+#undef wpunc
+#undef wdigi
+#undef whexd
+#undef wlowc
+#undef wuppc
+#undef wdhex
+#undef wlhex
+#undef wuhex
+
+
+
+
+
+
+
+
+
+

+ 874 - 0
source/EACallback.cpp

@@ -0,0 +1,874 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// To do: Deal with possible int64_t rollover in various parts of code below.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EACallback.h>
+#include <EAStdC/EARandomDistribution.h>
+#include <string.h>
+#include <EAAssert/eaassert.h>
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// Some stuff used to support the callbacks
+//
+#if EASTDC_THREADING_SUPPORTED
+	#define EA_CALLBACK_PROCESSOR_MUTEX_LOCK()   mMutex.Lock()
+	#define EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK() mMutex.Unlock()
+#else
+	#define EA_CALLBACK_PROCESSOR_MUTEX_LOCK() 
+	#define EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK() 
+#endif
+
+
+// We define the following macros from CoreAllocator here to avoid a dependency on the MemoryMan package.
+// #ifndef EA_CB_CA_NEW
+//     #define EA_CB_CA_NEW(Class, pAllocator, pName) new ((pAllocator)->Alloc(sizeof(Class), pName, 0, EA_ALIGN_OF(Class), 0)) Class
+// #endif
+// 
+// #ifndef EA_CB_CA_DELETE
+//     #define EA_CB_CA_DELETE(pObject, pAllocator) EA::StdC::delete_object(pObject, pAllocator)
+// #endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Misc
+///////////////////////////////////////////////////////////////////////////////
+
+// The use of standard min/max leads to compile errors sensitive 
+// to the state of EASTDC_THREADING_SUPPORTED.
+template <class T>
+const T smin(const T& a, const T& b)
+{
+	return a < b ? a : b;
+}
+
+
+static void DefaultCallback(Callback* pCallback, void*, uint64_t, uint64_t)
+{
+	pCallback->Stop();
+}
+
+
+/*
+/// delete_object
+///
+/// Deletes an object created by create_object.
+/// See create_object for specifications and examples.
+///
+template <typename T>
+inline void delete_object(T* pObject, Allocator::ICoreAllocator* pAllocator)
+{
+	if(pObject) // As per the C++ standard, deletion of NULL results in a no-op.
+	{
+		pObject->~T();
+		pAllocator->Free(pObject);
+	}
+}
+*/
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Callback
+///////////////////////////////////////////////////////////////////////////////
+
+Callback::Callback()
+	: mPeriod(UINT64_C(1000000000))
+	, mPrecision(500000)
+	, mpCallbackManager(NULL)
+	, mpFunction(NULL)
+	, mpFunctionArg(NULL)
+	, mType(kTypeTime)
+	, mbStarted(0)
+	, mbOneShot(false)
+	, mbEnableRefCount(false)
+	, mNextCallbackEvent(0)
+	, mLastCallbackEvent(0)
+{
+	EA_ASSERT((int64_t)mPeriod > 0);     // Sanity checks.
+	EA_ASSERT((int64_t)mPrecision > 0);
+
+	SetFunctionInfo(NULL, NULL, false);
+}
+
+
+Callback::Callback(CallbackFunctionType pCallbackFunc, void* pCallbackFuncArg, uint64_t period, 
+				   uint64_t precision, Type type, bool bEnableRefCount)
+	: mPeriod(period)
+	, mPrecision(precision) 
+	, mpCallbackManager(NULL)
+	, mpFunction(NULL)
+	, mpFunctionArg(NULL)
+	, mType(type)
+	, mbStarted(0)
+	, mbOneShot(false)
+	, mbEnableRefCount(false)
+	, mNextCallbackEvent(0)
+	, mLastCallbackEvent(0)
+{
+	EA_ASSERT((int64_t)mPeriod > 0);     // Sanity checks.
+	EA_ASSERT((int64_t)mPrecision > 0);
+
+	SetFunctionInfo(pCallbackFunc, pCallbackFuncArg, bEnableRefCount);
+}
+
+
+Callback::~Callback()
+{
+	if((int32_t)mbStarted) // Cast to int32_t because mbStarted might be an atomic int class.
+		Stop();
+}
+
+
+// Sets the function which is called when the time/tick count expire. Note that if the 
+// in asynch mode, the callback could occur in a different thread from the thread that 
+// started the timer.
+bool Callback::SetFunctionInfo(Callback::CallbackFunctionType pCallbackFunction, void* pCallbackArgument, bool bEnableRefCount)
+{
+	if(pCallbackFunction)
+	{
+		mpFunction    = pCallbackFunction;
+		mpFunctionArg = pCallbackArgument;
+	}
+	else 
+	{
+		mpFunction    = DefaultCallback;
+		mpFunctionArg = this;
+	}
+
+	if(bEnableRefCount)
+	{
+		mbEnableRefCount = true;
+		AddRefCallback(); // This will AddRef the pointer if it is non-NULL.
+	}
+
+	return true;
+}
+
+
+void Callback::GetFunctionInfo(Callback::CallbackFunctionType& pCallbackFunction, void*& pCallbackArgument) const
+{
+	pCallbackFunction = mpFunction;
+	pCallbackArgument = mpFunctionArg;
+}
+
+
+void Callback::Call(uint64_t absoluteValue, uint64_t deltaValue)
+{
+	EA_ASSERT(mpFunction);
+	if(mpFunction)
+		mpFunction(this, mpFunctionArg, absoluteValue, deltaValue);
+}
+
+
+uint64_t Callback::GetPeriod() const
+{
+	return mPeriod;
+}
+
+
+bool Callback::SetPeriod(uint64_t nPeriod)
+{
+	EA_ASSERT((int64_t)nPeriod > 0);     // Sanity checks.
+	mPeriod = nPeriod;
+	return true;
+}
+
+
+uint64_t Callback::GetPrecision() const
+{
+	return mPrecision;
+}
+
+
+bool Callback::SetPrecision(uint64_t nPrecision)
+{
+	EA_ASSERT((int64_t)nPrecision >= 0);
+	mPrecision = nPrecision;
+	return true;
+}
+
+
+bool Callback::Start(ICallbackManager* pCallbackManager, bool bOneShot)
+{
+	if(!(int32_t)mbStarted) // Cast to int32_t because mbStarted might be an atomic int class.
+	{
+		if(!pCallbackManager)
+			pCallbackManager = GetCallbackManager();
+
+		mpCallbackManager = pCallbackManager;
+
+		if(pCallbackManager)
+			mbStarted = (mpCallbackManager->Add(this, bOneShot) ? 1 : 0);
+	}
+
+	return ((int32_t)mbStarted != 0);
+}
+
+
+void Callback::Stop()
+{
+	if((int32_t)mbStarted) // Cast to int32_t because mbStarted might be an atomic int class.
+	{
+		mpCallbackManager->Remove(this);
+		mbStarted = 0;
+
+		// Note that the following may result in the Callback object (this)
+		// being deleted, due to a reference count decrement on itself.
+		// Thus it is important that this be the last thing done in this function.
+		if(mbEnableRefCount)
+			ReleaseCallback();
+	}
+}
+
+
+bool Callback::IsStarted() const
+{
+	return ((int32_t)mbStarted != 0); // Cast to int32_t because mbStarted might be an atomic int class.
+}
+
+
+bool Callback::SetType(Type type)
+{
+	mType = type;
+	return true;
+}
+
+
+Callback::Type Callback::GetType() const
+{
+	return mType;
+}
+
+
+void Callback::AddRefCallback()
+{
+	Call(kMessageAddRef, 0);
+}
+
+
+void Callback::ReleaseCallback()
+{
+	Call(kMessageRelease, 0);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CallbackVector
+///////////////////////////////////////////////////////////////////////////////
+
+CallbackManager::CallbackVector::CallbackVector()
+  : mpBegin(mLocalBuffer),
+	mpEnd(mLocalBuffer),
+	mpCapacity(mLocalBuffer + EAArrayCount(mLocalBuffer))
+{
+	#if defined(EA_DEBUG)
+		memset(mLocalBuffer, 0, sizeof(mLocalBuffer));
+	#endif
+}
+
+
+CallbackManager::CallbackVector::~CallbackVector()
+{
+	if(mpBegin != mLocalBuffer)
+		EASTDC_DELETE[] mpBegin; // It's OK if this is NULL; C++ allows it.
+}
+
+
+CallbackManager::CallbackVector::iterator CallbackManager::CallbackVector::erase(value_type* pIterator)
+{
+	EA_ASSERT((pIterator >= mpBegin) && (pIterator < mpEnd));
+	const size_t moveCount = (size_t)((mpEnd - pIterator) - 1);
+	memmove(pIterator, pIterator + 1, moveCount * sizeof(value_type));
+
+	--mpEnd;
+
+	#if defined(EA_DEBUG)
+		memset(mpEnd, 0, sizeof(value_type));
+	#endif
+
+	return pIterator;
+}
+
+
+CallbackManager::CallbackVector::iterator CallbackManager::CallbackVector::push_back(value_type value)
+{
+	if((mpEnd + 1) >= mpCapacity) // If there is insufficient existing capacity...
+	{
+		const size_t oldSize     = (size_t)(mpEnd - mpBegin);
+		const size_t oldCapacity = (size_t)(mpCapacity - mpBegin);
+		const size_t newCapacity = (oldCapacity >= 2) ? (oldCapacity * 2) : 4;
+
+		value_type* pBegin = EASTDC_NEW("EACallback") value_type[newCapacity];
+		EA_ASSERT(pBegin);
+		memcpy(pBegin, mpBegin, oldSize * sizeof(value_type));
+		if(mpBegin != mLocalBuffer)
+			EASTDC_DELETE[] mpBegin;
+		mpBegin    = pBegin;
+		mpEnd      = pBegin + oldSize;
+		mpCapacity = pBegin + newCapacity;
+	}
+
+	*mpEnd = value;
+
+	return ++mpEnd;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CallbackManager
+///////////////////////////////////////////////////////////////////////////////
+
+static ICallbackManager* gpCallbackManager = NULL;
+
+EASTDC_API ICallbackManager* GetCallbackManager()
+{
+	return gpCallbackManager;
+}
+
+EASTDC_API void SetCallbackManager(ICallbackManager* pCallbackManager)
+{
+	gpCallbackManager = pCallbackManager;
+}
+
+
+
+CallbackManager::CallbackManager()
+  : mCallbackArray()
+  , mStopwatch(EA::StdC::Stopwatch::kUnitsNanoseconds)
+  , mTickCounter(0)
+  , mUserEventCounter(0)
+  , mbInitialized(false)
+  , mbRunning(false)
+  , mbAsync(false)
+  , mRandom()
+  #if EASTDC_THREADING_SUPPORTED
+  , mNSecPerTick(10000000)
+  , mNSecPerTickLastTimeMeasured(INT64_MIN)
+  , mNSecPerTickLastTickMeasured(INT64_MIN)
+  , mNextCallbackEventTime(0)
+  , mNextCallbackEventTick(0)
+  , mMutex()
+  , mThread()
+  , mbThreadStarted(0)
+  , mThreadParam()
+  #endif
+{
+	// mCallbackArray.reserve(8); Disabled because it already has a built-in mLocalBuffer.
+}
+
+CallbackManager::~CallbackManager()
+{
+	CallbackManager::Shutdown();
+}
+
+
+bool CallbackManager::Init(bool bAsync, bool bAsyncStart
+						#if EASTDC_THREADING_SUPPORTED
+                           , EA::Thread::ThreadParameters threadParam	
+						#endif
+                           )
+{
+	if(!mbRunning)
+	{
+		mbAsync   = bAsync;
+		mbRunning = true;
+
+        #if EASTDC_THREADING_SUPPORTED
+            mThreadParam = threadParam;
+        #else
+			EA_ASSERT(!mbAsync);
+			mbAsync = false; // The best we can do. Should never happen though.
+		#endif
+
+		mStopwatch.Restart();
+
+		if(mbAsync && bAsyncStart)
+			mbRunning = StartThread(); // If StartThread fails then set mbRunning to false.
+	}
+
+	return mbRunning;
+}
+
+
+void CallbackManager::Shutdown()
+{
+	EA_CALLBACK_PROCESSOR_MUTEX_LOCK();
+
+	if(mbRunning)
+	{
+		mbRunning = false;  // Set this to false so no further calls to CallbackManager will proceed.
+
+		StopThread();
+
+		mStopwatch.Stop();
+
+		// Stop all running Callbacks. This allows them to do cleanup.
+		for(size_t i = 0, iEnd = mCallbackArray.size(); i < iEnd; ++i)
+		{
+			if(mCallbackArray[i]) // It's possible this could be NULL, because stopped callbacks are merely NULL their in the mCallbackArray.
+			{
+				Callback* pCallback = mCallbackArray[i]; // Make a temp because we will be unlocking our mutex below.
+				mCallbackArray[i] = NULL;                // Leave it as NULL for now. We'll actually erase the entry later during our update cycle. Our code is fine with NULL pointers and it's useful to keep them because their slots can be re-used.
+
+				EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK();
+				pCallback->Stop();
+				EA_CALLBACK_PROCESSOR_MUTEX_LOCK();
+			}
+		}
+
+		mCallbackArray.clear();
+	}
+
+	EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK();
+}
+
+
+// Returns true if the thread is running upon return of this function.
+// Will return true if the thread was already running upon calling this function.
+bool CallbackManager::StartThread()
+{
+	#if EASTDC_THREADING_SUPPORTED
+		if(mbAsync)
+		{
+			if(mbThreadStarted.SetValueConditional(1, 0)) // If the thread was previously 0 and we set it to 1...
+			{
+				mThreadParam.mpName = "CallbackManager";  // Some platforms have an extremely limited thread name buffer and will clip this.
+				EA::Thread::ThreadId threadId = mThread.Begin(RunStatic, static_cast<CallbackManager*>(this), &mThreadParam);
+
+				EA_ASSERT(threadId != EA::Thread::kThreadIdInvalid);
+				return (threadId != EA::Thread::kThreadIdInvalid);
+			}
+			return true; // Else the thread was already running...
+		}
+	#endif
+
+	return false;
+}
+
+
+void CallbackManager::StopThread()
+{
+	#if EASTDC_THREADING_SUPPORTED
+		if(mbThreadStarted.SetValueConditional(0, 1)) // If the thread was previously 1 and we set it to 0...
+		{
+			mThread.Wake();            // Should be a semaphore or condition variable signal.
+			mThread.WaitForEnd();
+		}
+	#endif
+}
+
+void CallbackManager::Update()
+{
+	int64_t curTick = 0;
+	int64_t curTime = 0;
+	int64_t curUserEvent = 0;
+
+	UpdateInternal(curTick, curTime, curUserEvent);
+
+	EA_UNUSED(curTick);
+	EA_UNUSED(curTime);
+	EA_UNUSED(curUserEvent);
+}
+
+
+struct TempUnitsInfo
+{
+	int64_t  mUnits;
+	int64_t* mpNextEventUnits;
+};
+
+void CallbackManager::UpdateInternal(int64_t& curTick, int64_t& curTime, int64_t& curUserEvent)
+{
+	EA_CALLBACK_PROCESSOR_MUTEX_LOCK();
+
+	EA_ASSERT(mbRunning); // The user must have called CallbackManager::Init before using it.
+
+	curTick      = ++mTickCounter;
+	curTime      = (int64_t)mStopwatch.GetElapsedTime();
+	curUserEvent = (int64_t)mUserEventCounter;
+	
+	if(!mCallbackArray.empty())
+	{
+		// Every time Update is called, we need to call the elapsed Callbacks and then 
+		// figure out the next time we'll need to do a callback.
+
+		// Scan our list and call the callbacks as needed
+		int64_t nextCallBackUserEvent = 0;
+
+		TempUnitsInfo timeInfo      = { curTime,      &mNextCallbackEventTime };
+		TempUnitsInfo tickInfo      = { curTick,      &mNextCallbackEventTick };
+		TempUnitsInfo userEventInfo = { curUserEvent, &nextCallBackUserEvent  };
+
+		for(size_t i = 0; i < mCallbackArray.size(); ++i) // Intentionally re-evaluate size every time through, as it could change dynamically below. Intentionally use < instead of !=, as size could decrease by any amount during the execution below.
+		{
+			Callback*      pCallback = mCallbackArray[i];
+			TempUnitsInfo* pTUI      = NULL;
+
+			if(pCallback)
+			{
+				// Call the callback function if needed 
+				switch(pCallback->GetType())
+				{
+					case Callback::kTypeTime:
+						pTUI = &timeInfo;
+						break;
+						
+					case Callback::kTypeTick:
+						pTUI = &tickInfo;
+						break;
+
+					default:
+					case Callback::kTypeUserEvent:
+						pTUI = &userEventInfo;
+						break;
+				}
+
+				EA_ASSERT(pTUI != NULL);
+				if(pTUI->mUnits >= pCallback->mNextCallbackEvent) // If it's time to call this callback...
+				{
+					// We have to beware that this Call might result in the callee manipulating 
+					// CallbackManager (us) and change our state, particularly with respect to 
+					// starting and stopping callbacks (including this callback).
+					// As of this writing, our mutex is locked during the Call. This leaves an 
+					// opportunity for threading deadlock. To consider: See if we can unlock the 
+					// mutex before calling this. We would need to re-evaluate some of our state
+					// upon return if we did this. Maybe have a member variable called mHasChanged
+					// to make this more efficient.
+					pCallback->Call((uint64_t)pTUI->mUnits, (uint64_t)(pTUI->mUnits - pCallback->mLastCallbackEvent));
+
+					if((i < mCallbackArray.size()) && (mCallbackArray[i] == pCallback)) // If the callback wasn't stopped and removed during the Call to the user above...
+					{
+						pCallback->mLastCallbackEvent = pTUI->mUnits;
+
+						if(pCallback->mbOneShot)
+							pCallback->Stop();
+						else 
+						{
+							const int32_t precision = (int32_t)pCallback->GetPrecision();
+							const int64_t period    = (int64_t)pCallback->GetPeriod();
+
+							pCallback->mNextCallbackEvent = (pTUI->mUnits + period);
+
+							if(precision) // To consider; For kTypeTime it might be worth testing for (precision > 100) or similar instead here.
+							{
+								// An alternative to the use of random below would be a load minimization 
+								// strategy with quite a bit more involved implementation.
+								const int32_t delta             = RandomInt32UniformRange(mRandom, -precision, precision - 1); // Note by Paul P: I added this -1 so unit tests could pass, but it doesn't seem right.
+								const int64_t nextCallbackEvent = pCallback->mNextCallbackEvent + delta;
+
+								if(nextCallbackEvent > pTUI->mUnits) // Ignore precision adjustments that make it so the next event is prior to the current one.
+									pCallback->mNextCallbackEvent = nextCallbackEvent;
+							}
+
+							EA_ASSERT(pCallback->mNextCallbackEvent >= pTUI->mUnits); // Assert that the next event is not backwards in time.
+
+							if(mbAsync)
+							{
+								if(*pTUI->mpNextEventUnits > pCallback->mNextCallbackEvent)     // Update mNextCallbackEventTime or mNextCallbackEventTick to reflect what is the
+									*pTUI->mpNextEventUnits = pCallback->mNextCallbackEvent;    // minimum time until the next event. We'll use that in the thread Run function to
+							}                                                                   // know how long to sleep/wait before it needs to do another callback.
+						}
+					}
+				}
+			} // if(pCallback)
+			else 
+			{
+				mCallbackArray.erase(&mCallbackArray[i]);
+			}
+
+		} // for(...)
+
+	} // if(!mCallbackArray.empty())
+
+	EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK();
+}
+
+
+// Thread function.
+intptr_t CallbackManager::Run()
+{
+	#if EASTDC_THREADING_SUPPORTED
+		EA_ASSERT(mbThreadStarted.GetValue() != 0);
+
+		while(mbRunning)
+		{
+			int64_t curTick;
+			int64_t curTime;  
+			int64_t curUserEvent;  
+
+			UpdateInternal(curTick, curTime, curUserEvent);
+
+			// Update msec/tick value if needed.
+			// Note by Paul Pedriana: I don't like this nanosecond tick calculation logic. IMO it's too delicate.
+			const int64_t kNSecPerTickFrequency = UINT64_C(50000000); // in nsec -- how often to update mNSecPerTick
+
+			if(curTime > (mNSecPerTickLastTimeMeasured + kNSecPerTickFrequency))
+			{
+				mNSecPerTick                 = ((double)curTime - (double)mNSecPerTickLastTimeMeasured) / ((double)curTick - (double)mNSecPerTickLastTickMeasured);
+				mNSecPerTickLastTimeMeasured = curTime;
+				mNSecPerTickLastTickMeasured = curTick;
+			}
+
+			// Come up with sleeping time and put the thread to sleep 
+			// To do: We need to switch this to an alternative synchronization primitive, as sleeping isn't very great.
+			#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) // If using a Microsoft OS where Wake is supported and we can sleep for a long time. 
+				int64_t timeToNextEventMs = INT_MAX;
+			#else
+				int64_t timeToNextEventMs = 50;
+			#endif
+
+			if(!mCallbackArray.empty()) // If there are any active callbacks...
+			{
+				if(mNextCallbackEventTime < curTime)
+					mNextCallbackEventTime = curTime + 100000000;   // 100 milliseconds worth of nanoseconds. The number is arbitrary. Probably should be a smaller value for faster machines.
+				if(mNextCallbackEventTick < curTick)
+					mNextCallbackEventTick = curTick + 1000;        // Arbitrary.
+
+			  //const int64_t absoluteTime = (int64_t)mStopwatch.GetElapsedTime();                          // Nanoseconds
+				const int64_t timeDelta    = mNextCallbackEventTime - curTime;                              // Nanoseconds
+				const int64_t tickDelta    = (int64_t)((mNextCallbackEventTick - curTick) * mNSecPerTick);  // Nanoseconds
+				const int64_t minDelta     = smin(timeDelta, tickDelta);                                    // Nanoseconds
+
+				timeToNextEventMs = (minDelta / 1000000) / 2;   // Convert minDelta to milliseconds (what ThreadSleep wants) and half it in order to oversample the callback time (is this necessary?)
+
+				if(timeToNextEventMs < 0)
+					timeToNextEventMs = 0; // simply yield.
+			} 
+
+			// Question by Paul Pedriana upon examining this code: Why is this implemented using thread sleeping instead of a conventional 
+			// thread synchronization primitive such as a Semaphore? At the least this should be a semaphore wait with a timeout set 
+			// to be equal to timeToNextEventMs. Then instead of waking a thread with Thread::Wake, we can simply signal the semaphore. This is 
+			// expecially so because some platforms don't support waking threads from sleep.
+
+			if(timeToNextEventMs == 0)
+				Thread::ThreadSleep(EA::Thread::kTimeoutYield);
+			else
+				Thread::ThreadSleep(EA::Thread::ThreadTime(timeToNextEventMs));
+
+			//#if defined(EA_DEBUG)
+			//    static int64_t lastSleepTimes[200];
+			//    static int lastSleepTimeIndex = 0;
+			//    if(lastSleepTimeIndex == 200)
+			//        lastSleepTimeIndex = 0;
+			//    lastSleepTimes[lastSleepTimeIndex++] = timeToNextEventMs;
+			//#endif
+		}
+	#endif
+
+	return 0;
+}
+
+
+bool CallbackManager::Add(Callback* pCallback, bool bOneShot)
+{
+	bool bReturnValue = false;
+
+	EA_ASSERT(pCallback != NULL);
+
+	EA_CALLBACK_PROCESSOR_MUTEX_LOCK();
+	
+	if(mbRunning)
+	{
+		size_t found       = 0xffffffff;
+		size_t found_empty = 0xffffffff;
+
+		// See if pCallback is already added and while doing so see if there is an existing empty slot if it's not already added.
+		for(size_t i = 0, iEnd = mCallbackArray.size(); i < iEnd; ++i)
+		{
+			Callback* pCallbackTemp = mCallbackArray[i];
+
+			if(pCallbackTemp == pCallback)
+			{
+				found = i;
+				break;
+			}
+			else if(!pCallbackTemp && (found_empty == 0xffffffff))
+				found_empty = i;
+		}              
+		
+		if(found == 0xffffffff) // If pCallback isn't already present...
+		{
+			if(found_empty == 0xffffffff) // If no empty slot was found...
+				mCallbackArray.push_back(pCallback);
+			else 
+				mCallbackArray[found_empty] = pCallback;
+
+			int64_t  units           = 0;   // This is the current time, current tick, or current user event number.
+			int64_t  nextUnits       = 0;
+			int64_t* pNextEventUnits = &nextUnits;
+			int32_t  precision       = (int32_t)pCallback->GetPrecision();
+			int64_t  period          = (int64_t)pCallback->GetPeriod();
+
+			switch(pCallback->GetType())
+			{
+				case Callback::kTypeTime: // If the callback triggers after a set amount of time...
+					units           = (int64_t)mStopwatch.GetElapsedTime();
+					pNextEventUnits = &mNextCallbackEventTime;
+					break;
+					
+				case Callback::kTypeTick: // If the callback triggers after a set amount of ticks...
+					units           = (int64_t)mTickCounter;
+					pNextEventUnits = &mNextCallbackEventTick;
+					break;
+
+				case Callback::kTypeUserEvent: // If the callback triggers after a manually user-generated event...
+				default:
+					break;
+			}
+
+			pCallback->mbOneShot          = bOneShot;
+			pCallback->mNextCallbackEvent = units + period;
+			pCallback->mLastCallbackEvent = units;
+
+			if(precision)
+			{
+				const int32_t delta             = RandomInt32UniformRange(mRandom, -precision, precision - 1); // Note by Paul P: I added this -1 so unit tests could pass, but it doesn't seem right.
+				const int64_t nextCallbackEvent = pCallback->mNextCallbackEvent + delta;
+
+				if(nextCallbackEvent > pCallback->mNextCallbackEvent) // Ignore precision adjustments that make it so the next event is prior to the current one.
+					pCallback->mNextCallbackEvent = nextCallbackEvent;
+			}
+
+			EA_ASSERT(pCallback->mNextCallbackEvent >= units); // Assert that the next event is not backwards in time.
+
+			if(mbAsync)
+			{
+				// Note by Paul P: Is the following really supposed to use a < comparison? I didn't originally write this.
+				// It works but I'm not sure it's the best way to do this. It seems to me that the next 
+				// event units should by default be a very long time from now and newly added Callback 
+				// objects should reduce that time. I think that this code here works because while we set a 
+				// mNextCallbackEventTime/mNextCallbackEventTick to be further in the future, the RunInternal function
+				// will loop over Callback objects and select the actual soonest one. If we switched the > here to a >
+				// then we would need to make sure we initially set mNextCallbackEventTime/mNextCallbackEventTick to be
+				// a high value instead of it's initial default of 0, because if it starts as zero it will get stuck 
+				// there permanently because it never gets updated (I tried this so I know it happens like so).
+				if(*pNextEventUnits < pCallback->mNextCallbackEvent)
+					*pNextEventUnits = pCallback->mNextCallbackEvent;
+			}
+		} 
+
+		bReturnValue = true; // This might turn false below in case of an error.
+
+		#if EASTDC_THREADING_SUPPORTED
+			if(mbAsync) // If we run in async (background thread) mode...
+			{
+				if(mbThreadStarted.GetValue() == 0) // If the thread hasn't been started yet...
+					bReturnValue = StartThread();   // Starts it if not already started. Is there something useful we could do with the return value of this?
+
+				if((mNextCallbackEventTime < (int64_t)mStopwatch.GetElapsedTime()) || // If we need to wake the thread now to do a callback...
+				   (mNextCallbackEventTick < (int64_t)mTickCounter))
+				{
+					// Note: Some platforms don't have the capability of waking a sleeping thread.
+					// This code should be using a semaphore instead of thread sleep/wake.
+					mThread.Wake();
+				}
+			}
+		#endif                    
+	}
+
+	EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK();
+
+	return bReturnValue;
+} 
+
+
+bool CallbackManager::Remove(Callback* pCallback)
+{
+	bool bRemoved = false;
+
+	EA_CALLBACK_PROCESSOR_MUTEX_LOCK();
+
+	if(pCallback)
+	{
+		if(mbRunning)
+		{
+			for(size_t i = 0, iEnd = mCallbackArray.size(); i < iEnd; ++i)
+			{
+				if(mCallbackArray[i] == pCallback)
+				{
+					mCallbackArray[i] = NULL; // We might re-use this slot later, so we don't take the CPU cycles to free the array right now.
+					bRemoved = true;
+					break;
+				}
+			}              
+		}
+	}
+
+	EA_CALLBACK_PROCESSOR_MUTEX_UNLOCK();
+
+	// It's important to call this outside our mutex lock.
+	if(bRemoved)
+		pCallback->Stop();
+
+	return bRemoved;
+}
+
+
+#if EASTDC_THREADING_SUPPORTED
+
+EA::Thread::Thread& CallbackManager::GetThread()
+{
+	return mThread;
+}
+
+void CallbackManager::Lock()
+{
+	mMutex.Lock();
+}
+
+void CallbackManager::Unlock()
+{
+	mMutex.Unlock();
+}
+
+#endif
+
+
+
+void CallbackManager::OnUserEvent()
+{
+	// To consider: Call the Update function here if callbacks waiting on user events are due.
+	//              The problem with doing this is that it makes OnUserEvent have side effects
+	//              which may be beyond what the user wants or expects.
+
+	#if EASTDC_THREADING_SUPPORTED
+		// Note: Some platforms don't have the capability to wake a sleeping thread.
+		// This code should be using a semaphore instead of thread sleep/wake.
+		if(mThread.GetStatus() == EA::Thread::Thread::kStatusRunning)
+			mThread.Wake();
+	#endif
+
+	++mUserEventCounter;
+}
+
+
+uint64_t CallbackManager::GetTime()
+{
+	return mStopwatch.GetElapsedTime();
+}
+
+
+
+} // namespace StdC
+
+} // namespace EA
+
+
+

+ 2403 - 0
source/EADateTime.cpp

@@ -0,0 +1,2403 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Implements a suite of functionality to manage dates, times, and calendars.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EADateTime.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EACType.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EAMemory.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+#include <EAAssert/eaassert.h>
+
+#if EASTDC_TIME_H_AVAILABLE
+	#include <time.h>
+#else
+	static tm sTm;
+
+	time_t time(time_t*)
+	{
+		return 0;
+	}
+
+	tm* gmtime(const time_t*)
+	{
+		return &sTm;
+	}
+
+	tm* localtime(const time_t*)
+	{
+		return &sTm;
+	}
+
+#endif
+
+#if EASTDC_LOCALE_H_AVAILABLE
+	#include <locale.h>
+#endif
+
+#if   defined(EA_PLATFORM_APPLE)
+	#include <mach/mach.h>
+	#include <mach/mach_time.h>
+#endif
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#if defined(_MSC_VER)
+		#pragma warning(push, 0)
+	#endif
+	#if   (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+		#include <Windows.h>
+	#else
+		#include <WinSock2.h>
+	#endif
+	#if defined(_MSC_VER)
+		#pragma warning(pop)
+	#endif
+
+	// Microsoft has a GetLocalTime function, but it isn't available for all platform build targets. 
+	// The following is the most Microsoft-portable workaround version we can come up with.
+	void GetLocalTimeAlternative(SYSTEMTIME* pSystemTime)
+	{
+		#if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_WINDOWS_PHONE)
+			GetLocalTime(pSystemTime);
+		#else
+			// Get current date/time
+			FILETIME time, localTime;
+
+			::GetSystemTimeAsFileTime(&time);
+			::FileTimeToLocalFileTime(&time, &localTime);
+			::FileTimeToSystemTime(&localTime, pSystemTime);
+		#endif
+	}
+
+	#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		bool EAStdCGetDateFormat(DWORD dwFlags, const SYSTEMTIME* lpDate, char* lpDateStr, size_t cchDate)
+		{
+			// size_t cchDate is unsigned and 32 or 64 bit so needs to be cast to pass to GetDateFormat
+			return (GetDateFormatA(LOCALE_USER_DEFAULT, dwFlags, lpDate, NULL, lpDateStr, static_cast<int>(cchDate)) != 0);
+		}
+		bool EAStdCGetTimeFormat(DWORD dwFlags, const SYSTEMTIME* lpTime, char* timeStr, size_t cchTime)
+		{
+			// size_t cchTime is unsigned and 32 or 64 bit so needs to be cast to pass to GetTimeFormat
+			return (GetTimeFormatA(LOCALE_USER_DEFAULT, dwFlags, lpTime, NULL, timeStr, static_cast<int>(cchTime)) != 0);
+		}
+
+		int EAStdCGetLocaleInfo(LCTYPE lcType, char* lcData, size_t cchData)
+		{
+			// size_t cchData is unsigned and 32 or 64 bit so needs to be cast to pass to GetLocaleInfo
+			return GetLocaleInfoA(LOCALE_USER_DEFAULT, lcType, lcData, static_cast<int>(cchData));
+		}
+	#else
+		bool EAStdCGetDateFormat(DWORD dwFlags, const SYSTEMTIME* lpDate, char* dateStr, size_t cchDate)
+		{
+			wchar_t* temp = static_cast<wchar_t*>(EAAlloca(cchDate * sizeof(wchar_t)));
+			const bool res = GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpDate, NULL, temp, static_cast<int>(cchDate), NULL) != 0;
+			EA::StdC::Strlcpy(dateStr, temp, cchDate);
+			return res;
+		}
+
+		bool EAStdCGetTimeFormat(DWORD dwFlags, const SYSTEMTIME* lpTime, char* timeStr, size_t cchTime)
+		{
+			wchar_t* temp = static_cast<wchar_t*>(EAAlloca(cchTime * sizeof(wchar_t)));
+			const bool res = GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpTime, NULL, temp, static_cast<int>(cchTime)) != 0;
+			EA::StdC::Strlcpy(timeStr, temp, cchTime);
+			return res;
+		}
+
+		int EAStdCGetLocaleInfo(LCTYPE lcType, char* lcData, size_t cchData)
+		{
+			wchar_t* temp = lcData ? static_cast<wchar_t*>(EAAlloca(cchData * sizeof(wchar_t))) : nullptr;
+			const int res = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, lcType, temp, static_cast<int>(cchData));
+			if (lcData)
+				EA::StdC::Strlcpy(lcData, temp, cchData);
+			return res;
+		}
+	#endif
+#else
+	typedef struct _FILETIME
+	{
+		uint32_t dwLowDateTime;
+		uint32_t dwHighDateTime;
+	} FILETIME;
+
+	typedef struct _SYSTEMTIME
+	{
+		uint16_t wYear;
+		uint16_t wMonth;
+		uint16_t wDayOfWeek;
+		uint16_t wDay;
+		uint16_t wHour;
+		uint16_t wMinute;
+		uint16_t wSecond;
+		uint16_t wMilliseconds;
+	} SYSTEMTIME;
+#endif
+
+
+#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4365) // 'argument' : conversion from 'int' to 'uint32_t', signed/unsigned mismatch
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+	// static table containing number of days for each month
+	static const uint32_t kDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	
+	// static table containing total number of days within a year up to a given month
+	static const uint32_t  kDaysInYear[26]  = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,   // for regular years
+												0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; // for leap years
+
+
+	// macro for computing number of leap years that occured up to the specified year
+	#define EADT_COUNT_LEAP_YEARS(Y) (((Y - 1) / 4) - ((Y - 1) / 100) + ((Y - 1) / 400))
+
+
+	#ifndef EASTDC_ABS
+		#define EASTDC_ABS(x) (x >= 0 ? x : -x)
+	#endif
+
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetTime
+	//
+	// Returns nanoseconds since January 1, 1970.
+	// There are 584 years worth of nanoseconds that can be stored in a uint64_t.
+	// See the declaration for a more precise specification.
+	//
+	EASTDC_API uint64_t GetTime()
+	{
+		// This code is thread-unsafe for the case that the first ever call to GetTime 
+		// occurs by two threads at the same time, and/or there is a memory view latency
+		// between two CPUs that call this. The fix is probably to use a formal atomic in 
+		// initializing the stopwatch and sSystemTimeDiffAtCapture variables.
+		static EA::StdC::Stopwatch sStopwatch(Stopwatch::kUnitsNanoseconds, true);
+		static uint64_t            sInitialTime = 0;
+		uint64_t                   t = sStopwatch.GetElapsedTime();
+
+		if(sInitialTime == 0) // If this is the first time this function is called...
+		{
+			timeval tv;
+			GetTimeOfDay(&tv, NULL, true); // Returns time in the form of seconds/useconds since 1970.
+			sInitialTime = (uint64_t)((tv.tv_sec * UINT64_C(1000000000)) + (tv.tv_usec * UINT64_C(1000)));
+		}
+
+		return sInitialTime + t;
+	}
+
+
+	#if 0 // Alternative variation, which on the surface seems more efficient.
+	/*
+	EASTDC_API uint64_t GetTime()
+	{
+		static struct TimeWatch
+		{
+			EA::StdC::Stopwatch stopwatch;
+			uint64_t initialTime;
+		
+			TimeWatch() :
+				stopwatch(Stopwatch::kUnitsNanoseconds, true)
+			{
+				timeval tv;
+				GetTimeOfDay(&tv, NULL, true);
+				initialTime = (uint64_t)((tv.tv_sec * UINT64_C(1000000000)) + (tv.tv_usec * UINT64_C(1000)));
+			}
+		} sTimeWatch;
+
+		return sTimeWatch.initialTime + sTimeWatch.stopwatch.GetElapsedTime();
+	}
+	*/
+	#endif
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetTimePrecision
+	//
+	// Returns the precision of the GetTime and GetTimeOfDay functions.
+	//
+	EASTDC_API uint64_t GetTimePrecision()
+	{
+		#if   defined(EA_PLATFORM_MICROSOFT)
+			return 100;                         // 100-nanosecond precision. 
+
+		#elif defined(EA_PLATFORM_APPLE) || defined(__APPLE__) || defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_IPHONE)
+			return 1000;                        // Microsecond precision.
+ 
+		#else
+			return UINT64_C(1000000000);        // Second-level precision.
+
+		#endif
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// DateTimeParameters
+	///////////////////////////////////////////////////////////////////////////
+
+	DateTimeParameters::DateTimeParameters()
+	{
+		EA_COMPILETIME_ASSERT(kDateTimeIgnored == 0xffffffff);
+		memset(this, 0xff, sizeof(DateTimeParameters));
+	}
+
+
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// DateTime
+	///////////////////////////////////////////////////////////////////////////
+
+	///////////////////////////////////////////////////////////////////////
+	// GetParameter
+	//
+	// Gets the given parameter. If you want to get the year, 
+	// you would call GetParameter(kParameterYear).
+	//
+	uint32_t DateTime::GetParameter(Parameter parameter) const
+	{
+		uint32_t result = 0;
+
+		switch (parameter)
+		{
+			// Refers to full year value, such as 1994, 2006, etc. but not 
+			// a two digit value such as 94 or 04. The valid range is 0 - INT_MAX.      
+			case kParameterYear:                 
+			{
+				// compute total number of days
+				const int64_t nDays = (mnSeconds / kSecondsPerDay);
+
+				// compute the year
+				const int64_t leapYearCount = EADT_COUNT_LEAP_YEARS(nDays / 365);
+				result = 1 + (uint32_t)((nDays - 1 - leapYearCount) / 365);
+
+				break;
+			}
+
+			// Refers to month of year, starting with 1 for January. The valid range is 1 - 12.
+			case kParameterMonth:    
+			{
+				// get all the data we need
+				const uint32_t nYear      = GetParameter(kParameterYear);
+				const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
+
+				// check if the year is a leap year
+				const bool bLeap = IsLeapYear(nYear);   
+
+				// compute the month
+				for(uint32_t nMonth = kMonthJanuary; nMonth <= kMonthDecember; nMonth++)
+				{
+					if(nDayOfYear <= kDaysInYear[nMonth + (13 * bLeap)])
+					{
+						result = nMonth;
+						break;
+					}
+				}
+
+				if(result == 0)
+					result = kMonthJanuary;
+
+				break;
+			}
+
+			// Refers to a day of the year, starting with 1 for January 1st.
+			// The valid range is 1 - 366.
+			case kParameterDayOfYear:
+			{
+				// get the year
+				const uint32_t nYear = GetParameter(kParameterYear);
+
+				// compute total number of days
+				const int64_t nDays = (mnSeconds / kSecondsPerDay);
+
+				// compute the day of the year
+				const uint32_t leapYearCount = EADT_COUNT_LEAP_YEARS(nYear);
+				result = (uint32_t)(nDays - (((nYear - 1) * 365) + leapYearCount));
+
+				if(result == 0)
+					result = 1;
+
+				break;
+			}
+
+			// Refers to the day of the month, starting with 1 for the first 
+			// of the month. The valid range is 1 - 31.
+			case kParameterDayOfMonth:
+			{
+				// get all the data we need
+				const uint32_t nYear      = GetParameter(kParameterYear);
+				const uint32_t nMonth     = GetParameter(kParameterMonth);
+				const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
+
+				// get the day of the month
+				const int isLeapYear = IsLeapYear(nYear) ? 1 : 0;   
+				result = nDayOfYear - kDaysInYear[(nMonth - 1) + (13 * isLeapYear)];
+
+				break;
+			}
+
+			// Refers to the day of the week, starting with 1 for Sunday. 
+			// The valid range is 1-7.
+			case kParameterDayOfWeek:
+			{
+				// compute total number of days
+				const int64_t nDays = (mnSeconds / kSecondsPerDay);
+
+				// compute the day of the week
+				result = 1 + (uint32_t)(nDays % 7);
+
+				break;
+			}
+
+			// Refers to the hour of a day in 24 hour format, starting 
+			// with 0 for midnight. The valid range is 0-23.
+			case kParameterHour:
+				result = (uint32_t)((mnSeconds / kSecondsPerHour) % 24);
+				break;
+
+			// Refers to the minute of the hour, starting with 0 for 
+			// the first minute. The valid range is 0-59.
+			case kParameterMinute:
+				result = (uint32_t)((mnSeconds / kSecondsPerMinute) % 60);
+				break;
+
+			// Refers to the second of the minute, starting with 0 
+			// for the first second. The valid range is 0-59.
+			case kParameterSecond:
+				result = (uint32_t)(mnSeconds % 60);
+				break;
+
+			case kParameterNanosecond:
+				result = mnNanosecond;
+				break;
+
+			// Refers to the week of the year, starting with 1 for the 
+			// week of January 1. The valid range is 1-52.
+			case kParameterWeekOfYear:
+			{
+				const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
+				result = 1 + (nDayOfYear - 1) / 7;
+
+				break;
+			}
+
+			// Refers to the week of the month, starting with 1 for the 
+			// first week. The valid range is 1-5.
+			case kParameterWeekOfMonth:
+			{
+				const uint32_t nDayOfMonth = GetParameter(kParameterDayOfMonth);
+				result = 1 + (nDayOfMonth - 1) / 7;
+
+				break;
+			}
+
+			case kParameterUnknown:
+			default:
+				break; // This removes compiler warnings about unused cases.
+		}
+
+		return result;
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// SetParameter
+	//
+	// Sets the given parameter. If you want to set the year to 1999, 
+	// you would call SetParameter(kParameterYear, 1999).
+	//
+	void DateTime::SetParameter(Parameter parameter, uint32_t nValue)
+	{
+		switch (parameter)
+		{
+			// straight-forward calls to set
+			case kParameterYear:
+				Set(nValue,        kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
+				break; 
+			
+			case kParameterMonth:
+				Set(kValueIgnored, nValue,        kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
+				break; 
+
+			case kParameterDayOfMonth:
+				Set(kValueIgnored, kValueIgnored, nValue,        kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
+				break;
+
+			case kParameterHour:
+				Set(kValueIgnored, kValueIgnored, kValueIgnored, nValue,        kValueIgnored, kValueIgnored, kValueIgnored);
+				break;
+
+			case kParameterMinute:
+				Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue,        kValueIgnored, kValueIgnored);
+				break;
+
+			case kParameterSecond:
+				Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue       , kValueIgnored);
+				break;
+
+			case kParameterNanosecond:
+				Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue        );
+				break;
+
+			// set the day of the year
+			case kParameterDayOfYear:
+			{
+				// update the total number of seconds by the offset from the current day of the year
+				const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
+				mnSeconds += (int32_t)(nValue - nDayOfYear) * kSecondsPerDay;
+				break;
+			}
+
+			// set the day of the week
+			case kParameterDayOfWeek:
+			{
+				// make sure the new value is valid
+				if((nValue >= kDayOfWeekSunday) && (nValue <= kDayOfWeekSaturday))
+				{
+					// update the total number of seconds by the offset from the current day of the week
+					const uint32_t nDayOfWeek = GetParameter(kParameterDayOfWeek);
+					mnSeconds += (int32_t)(nValue - nDayOfWeek) * kSecondsPerDay;
+				}
+				else
+				{
+					EA_FAIL_M("EAStdC DateTime");
+				}
+				break;
+			}
+
+			// set the week of the year or month
+			case kParameterWeekOfYear:
+				// Fall through.
+			case kParameterWeekOfMonth:
+			{
+				// update the total number of seconds by the offset from the current week of the year
+				const uint32_t nParameterValue = GetParameter(parameter);
+				mnSeconds += (int32_t)(nValue - nParameterValue) * 7 * kSecondsPerDay;
+				break;
+			}
+
+			case kParameterUnknown:
+			default:
+				break; // This removes compiler warnings about unused cases.
+		}
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// Set
+	//
+	// Sets the time and date based on various inputs. If any input is 
+	// kValueIgnored, then the input is ignored and the current value is used. 
+	// If any of the cyclic inputs is beyond its valid range, the modulo
+	// of the value is used and the division of the value is added to the 
+	// next higher bracket. For example, if the input nMinute is 65, then 
+	// the minute used is 5 and 1 is added to the current hour value.
+	//
+	void DateTime::Set(uint32_t nYear, uint32_t nMonth,  uint32_t nDayOfMonth, 
+					   uint32_t nHour, uint32_t nMinute, uint32_t nSecond, uint32_t nNanosecond)
+	{
+		if(nYear       == kValueIgnored || !nYear) 
+			nYear       = GetParameter(kParameterYear);
+		if(nMonth      == kValueIgnored || !nMonth) 
+			nMonth      = GetParameter(kParameterMonth);
+		if(nDayOfMonth == kValueIgnored || !nDayOfMonth) 
+			nDayOfMonth = GetParameter(kParameterDayOfMonth);
+		if(nHour       == kValueIgnored) 
+			nHour       = GetParameter(kParameterHour);
+		if(nMinute     == kValueIgnored) 
+			nMinute     = GetParameter(kParameterMinute);
+		if(nSecond     == kValueIgnored) 
+			nSecond     = GetParameter(kParameterSecond);
+		if(nNanosecond == kValueIgnored) 
+			nNanosecond = mnNanosecond;
+
+		// wrap the month value
+		if(nMonth > 12)
+		{
+			nYear +=  (nMonth - 1) / 12;
+			nMonth = ((nMonth - 1) % 12) + 1;
+		}
+
+		// compute total number of days for the given year adding all leap days
+		const uint32_t leapYearCount = EADT_COUNT_LEAP_YEARS(nYear);
+		int64_t nDays = ((nYear - 1) * 365) + leapYearCount;
+
+		// add month and day
+		const int isLeapYear = (IsLeapYear(nYear) ? 1 : 0);
+		nDays += kDaysInYear[(nMonth - 1) + (13 * isLeapYear)] + (nDayOfMonth - 0); // Should this -0 be -1?
+
+		// convert the number of days to seconds
+		mnSeconds = nDays * kSecondsPerDay;
+
+		// add current time
+		mnSeconds += nHour   * kSecondsPerHour;
+		mnSeconds += nMinute * kSecondsPerMinute;
+		mnSeconds += nSecond;
+
+		mnSeconds += (nNanosecond / 1000000000);
+
+		mnNanosecond = (nNanosecond % 1000000000);
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// Set
+	//
+	// Sets the time based on the current time. If the timeFrame is 
+	// kTimeFrameUTC, the time is set to what the current UTC time is, 
+	// which will be a fixed number of hours off of what the current 
+	// local time is.
+	//
+	// We have the option of not reading nanoseconds, because reading nanoseconds means calling the GetTimeOfDay
+	// function, and the GetTimeOfDay function needs to indirectly call SetTimeZoneBias, which in turn
+	// calls Set. So we need to break the possibility of an infinite loop, and we can do that by having 
+	// SetTimeZoneBias call SetInternal(timeFrame, *false*); SetTimeZoneBias doesn't need the nanosecond 
+	// precision anyway.
+	//
+	void DateTime::Set(TimeFrame timeFrame, bool bSetNanoseconds)
+	{
+		#if   defined(EA_PLATFORM_MICROSOFT)
+			SYSTEMTIME s;
+
+			if(timeFrame == kTimeFrameLocal)
+				GetLocalTimeAlternative(&s);
+			else
+				GetSystemTime(&s);
+
+			Set(s.wYear,                                            // wYear: 1601 through 30827.
+				s.wMonth,                                           // wMonth: 1 through 12.
+				s.wDay,                                             // wDay: 1 through 31.
+				s.wHour,                                            // wHour: 0 through 23.
+				s.wMinute,                                          // wMinute: 0 through 59.
+				s.wSecond,                                          // wSecond: 0 through 59.
+				bSetNanoseconds ? s.wMilliseconds * 1000000 : 0);   // wMilliseconds: 0 through 999.
+		#else
+			const  time_t    nTime  = time(NULL);
+			struct tm* const pTime  = ((timeFrame == kTimeFrameUTC) ? gmtime(&nTime) : localtime(&nTime));
+			struct tm  const tmCopy = *pTime; // Need to make a copy because calling external code below could change what pTime points to, given that pTime comes from a static pointer inside the C Standard Library.
+
+			timeval tv;
+			if(bSetNanoseconds)
+				GetTimeOfDay(&tv, NULL, (timeFrame == kTimeFrameUTC));
+			else
+				tv.tv_usec = 0;
+
+			Set(tmCopy.tm_year + 1900, tmCopy.tm_mon + kMonthJanuary, tmCopy.tm_mday, tmCopy.tm_hour, tmCopy.tm_min, tmCopy.tm_sec, (uint32_t)(tv.tv_usec * 1000));
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// Compare
+	//
+	// This function compares this object with another DateTime object 
+	// and returns an integer result. The return value is the same as with 
+	// the strcmp string comparison function. If this object is less than 
+	// the argument dateTime, the return value is < 0. Comparison operators 
+	// are defined outside this class, though they use the Compare function 
+	// to do their work.
+	//
+	int DateTime::Compare(const DateTime& dateTime, bool bCompareDate, bool bCompareTime) const
+	{
+		// Note: I believe the code below which just does / and % is valid. The reason it wouldn't 
+		// be valid is if some days have more seconds than others. Yes there are leap seconds that 
+		// occur every few years, but leap seconds don't affect the recorded time of day; they reflect
+		// the measurement of time passage in the real world. Also, leap years (which are in fact a
+		// way in which the recorded days in a year differ) don't affect this, because they add whole
+		// days as opposed to fractional ones. Daylight savings time doesn't affect this because
+		// it too doesn't affect recorded time but rather affects real world exerpeience only.
+		// Time zones don't affect this calculation, as this function assumes that both DateTime 
+		// objects being compared are within the same time zone.
+
+		bool bCompareNanoseconds = true;  // Until proven false below.
+
+		// this is what we will be comparing
+		int64_t nValueA = mnSeconds;
+		int64_t nValueB = dateTime.GetSeconds();
+
+		if(bCompareDate && !bCompareTime)
+		{
+			// Date only - compare the total number of days.
+			// Make nValueA and nValueB be days instead of seconds.
+			nValueA = nValueA / kSecondsPerDay;
+			nValueB = nValueB / kSecondsPerDay;
+			bCompareNanoseconds = false;
+		}
+		else if(!bCompareDate && bCompareTime)
+		{
+			// Time of day only - extract the time portion of the total seconds value.
+			// Make nValueA and nValueB be seconds since the start of the day instead of absolute time seconds.
+			nValueA = nValueA % kSecondsPerDay;
+			nValueB = nValueB % kSecondsPerDay;
+		}
+		// else compare both date and time.
+
+		if(bCompareNanoseconds && (nValueA == nValueB))
+		{
+			nValueA = mnNanosecond;
+			nValueB = dateTime.mnNanosecond;
+		}
+
+		if(nValueA == nValueB) 
+			return 0;
+		else if(nValueA < nValueB)
+			return -1;
+		return 1;
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// AddTime
+	//
+	// Allows you to increment (or decrement) the given parameter by the given amount.
+	//
+	void DateTime::AddTime(Parameter parameter, int64_t nValue)
+	{
+		switch (parameter)
+		{
+			case kParameterYear:     
+				SetParameter(kParameterYear, (uint32_t)(GetParameter(kParameterYear) + nValue));
+				break;
+
+			case kParameterMonth:      
+			{
+				// first - compute new year
+				uint32_t nYear = (uint32_t)(GetParameter(kParameterYear) + (nValue / 12));
+
+				// now - how many months do we have left?
+				nValue = nValue % 12;
+
+				// compute new month value
+				const uint32_t nMonth = GetParameter(kParameterMonth);
+				nValue = (int64_t)nMonth + nValue;
+
+				// did we cross a year boundary ?
+				if(nValue < 1)
+				{
+					nYear--;
+					nValue = nValue + 12; // compute new month value
+				}
+				else if(nValue > 12)
+				{
+					nYear++;
+					nValue = nValue - 12; // compute new month value
+				}
+
+				// set the new year and month
+				Set(nYear, (uint32_t)(uint64_t)nValue, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
+
+				break;
+			}
+
+			// There really is no difference between the handling of the following 
+			// 3 parameter types - each offsets the date by a given number of days.
+			case kParameterDayOfMonth: 
+			case kParameterDayOfYear:
+			case kParameterDayOfWeek:
+				mnSeconds += (int64_t)(nValue * kSecondsPerDay);
+				break;
+
+			case kParameterHour:      
+				mnSeconds += (int64_t)(nValue * kSecondsPerHour);
+				break;
+
+			case kParameterMinute:    
+				mnSeconds += (int64_t)(nValue * kSecondsPerMinute);
+				break;
+
+			case kParameterSecond:
+				mnSeconds += nValue;
+				break;
+
+			case kParameterNanosecond:
+			{
+				int64_t newNanoseconds = GetParameter(kParameterNanosecond) + nValue;
+				int64_t addedSeconds   = (newNanoseconds / 1000000000);
+				newNanoseconds %= 1000000000;
+
+				EA_ASSERT(addedSeconds < INT64_C(0xffffffff));
+				AddTime(kParameterSecond, (uint32_t)addedSeconds);
+
+				EA_ASSERT(newNanoseconds < INT64_C(0xffffffff));
+				SetParameter(kParameterNanosecond, (uint32_t)newNanoseconds);
+
+				break;
+			}
+
+			// kParameterWeekOfYear and kParameterWeekOfMonth act the same 
+			// for this: each offsets the date by a given number of weeks
+			case kParameterWeekOfYear:
+				// Fall through
+			case kParameterWeekOfMonth:
+				mnSeconds += (int64_t)(nValue * 7 * kSecondsPerDay);
+				break;
+
+			case kParameterUnknown:
+			default:
+				break; // This removes compiler warnings about unused cases.
+		}
+
+		EA_ASSERT(mnSeconds >= 0); // Verify that the operation didn't cause integer wraparound.
+		if(mnSeconds < 0)
+			mnSeconds = 0;
+	}
+
+
+	int64_t DateTime::GetSeconds() const { return mnSeconds; }
+	void DateTime::SetSeconds(int64_t nSeconds) { mnSeconds = nSeconds; }
+
+
+	uint64_t DateTime::GetMilliseconds() const { return (uint64_t)mnSeconds * 1000 + mnNanosecond / 1000000; }
+	void DateTime::SetMilliseconds(uint64_t milliseconds)
+	{
+		mnSeconds = milliseconds / 1000;
+		mnNanosecond = (milliseconds % 1000) * 1000000;
+	}
+
+
+	EA::StdC::int128_t DateTime::GetNanoseconds() const
+	{
+		return (EA::StdC::int128_t(mnSeconds) * 1000000000) + mnNanosecond;
+	}
+
+	void DateTime::SetNanoseconds(const EA::StdC::int128_t& nanoseconds)
+	{
+		EA::StdC::int128_t seconds = nanoseconds / 1000000000;
+		EA::StdC::int128_t nanosecond = nanoseconds % 1000000000;
+
+		mnSeconds = seconds.AsInt64();
+		mnNanosecond = nanosecond.AsUint32();
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// IsLeapYear
+	//
+	// Returns true if the given year is a leap year.
+	// Algorithm from K & R, "The C Programming Language", 1st ed.
+	//
+	EASTDC_API bool IsLeapYear(uint32_t nYear)
+	{
+		return (!(nYear & 3) && (nYear % 100)) || !(nYear % 400);       
+		
+		// Alternative:
+		//if((nYear % 4) == 0)
+		//{
+		//    if((nYear % 100) == 0)
+		//        return ((nYear % 400) == 0);
+		//    else
+		//        return true;
+		//}
+		//return false;
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetDaysInYear
+	//
+	// Returns the number of days in the given year. The value will vary 
+	// based on whether the year is a leap year or not.
+	//
+	EASTDC_API uint32_t GetDaysInYear(uint32_t nYear)
+	{
+		return IsLeapYear(nYear) ? (uint32_t)366 : (uint32_t)365;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetDaysInMonth
+	//
+	// Implemented by Blazej Stompel
+	//
+	// Returns the number of days in the given month. The value will vary 
+	// based on the month and based on whether the year is a leap year. 
+	// The input nMonth takes one of enum Month or an integer equivalent.
+	//
+	EASTDC_API uint32_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear)
+	{
+		// Make sure the month value is valid
+		if((nMonth >= kMonthJanuary) && (nMonth <= kMonthDecember))
+		{
+			// Special case for leap years
+			if(nMonth == kMonthFebruary)
+			{
+				const bool isLeapYear = IsLeapYear(nYear);
+
+				if(isLeapYear)
+					return kDaysInMonth[nMonth - 1] + 1;
+			}
+
+			return kDaysInMonth[nMonth - 1];
+		}
+
+		return 0;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetDayOfYear
+	//
+	// The input nMonth takes one of enum Month or an integer equivalent.
+	// The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
+	//
+	EASTDC_API uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear)
+	{
+		const DateTime sDateTime(nYear, nMonth, nDayOfMonth, 0, 0, 0);
+
+		return sDateTime.GetParameter(kParameterDayOfYear);
+	}
+
+
+
+
+	// Code to regenerate the kEpochSeconds array.
+	//
+	// kEpochSeconds[kEpochJulian]         = DateTime((uint32_t)-4712,  1,  1, 12,  0,  0).GetSeconds(); // -4712/01/01/12:00:00
+	// kEpochSeconds[kEpochModifiedJulian] = DateTime((uint32_t) 1858, 11, 17,  0,  0,  0).GetSeconds(); //  1858/11/17/00:00:00
+	// kEpochSeconds[kEpochGregorian]      = DateTime((uint32_t) 1752,  9, 14,  0,  0,  0).GetSeconds(); //  1752/09/14/00:00:00
+	// kEpochSeconds[kEpoch1900]           = DateTime((uint32_t) 1900,  1,  1,  0,  0,  0).GetSeconds(); //  1900/01/01/00:00:00
+	// kEpochSeconds[kEpoch1950]           = DateTime((uint32_t) 1950,  1,  1,  0,  0,  0).GetSeconds(); //  1950/01/01/00:00:00
+	// kEpochSeconds[kEpoch1970]           = DateTime((uint32_t) 1970,  1,  1,  0,  0,  0).GetSeconds(); //  1970/01/01/00:00:00
+	// kEpochSeconds[kEpoch2000]           = DateTime((uint32_t) 2000,  1,  1,  0,  0,  0).GetSeconds(); //  2000/01/01/00:00:00
+	// kEpochSeconds[kEpochJ2000]          = DateTime((uint32_t) 2000,  1,  1, 11, 58, 55).GetSeconds(); //  2000/01/01/11:58:55
+	// kEpochSeconds[kEpochDateTime]       = 0;
+
+	static int64_t kEpochSeconds[10] = 
+	{
+		INT64_C(0),                 // kEpochUnknown
+		INT64_C(89839426968000),    // kEpochJulian          Began at -4712/01/01/12:00:00 (Year 1858, January 1, noon).
+		INT64_C(55278460800),       // kEpochGregorian       Began at  1752/09/14/00:00:00 (Year 1752, January 1, midnight). Beginning of the Gregorian calendar.
+		INT64_C(58628966400),       // kEpochModifiedJulian  Began at  1858/11/17/00:00:00 (Year 1858, January 1, midnight). 2,400,000.5 days after Julian epoch began.
+		INT64_C(59926694400),       // kEpoch1900            Began at  1900/01/01/00:00:00 (Year 1900, January 1, midnight). Same epoch used by the Network Time Protocol.
+		INT64_C(61504531200),       // kEpoch1950            Began at  1950/01/01/00:00:00 (Year 1950, January 1, midnight). Used by some gaming systems.
+		INT64_C(62135683200),       // kEpoch1970            Began at  1970/01/01/00:00:00 (Year 1970, January 1, midnight). Same epoch used by C runtime library and Unix OS.
+		INT64_C(63082368000),       // kEpoch2000            Began at  2000/01/01/00:00:00 (Year 2000, January 1, midnight). Same epoch used by Apple file systems.
+		INT64_C(63082411135),       // kEpochJ2000           Began at  2000/01/01/11:58:55 (Year 2000, January 1, ~noon).    Coordinated Universal Time, also includes 816 milliseconds.
+		INT64_C(0),                 // kEpochDateTime        Began at  0000/01/01/00:00:00 (Year 0000, January 1, midnight).
+	};
+
+
+	///////////////////////////////////////////////////////////////////////
+	// ConvertEpochSeconds
+	//
+	EASTDC_API int64_t ConvertEpochSeconds(Epoch srcEpoch, int64_t srcSeconds, Epoch destEpoch)
+	{
+		if((srcEpoch < kEpochCount) && (destEpoch < kEpochCount))
+			return (srcSeconds + kEpochSeconds[srcEpoch]) - kEpochSeconds[destEpoch];
+
+		return 0;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetCurrent
+	//
+	// Returns the current year, month, hour, etc.
+	//
+	EASTDC_API uint32_t GetCurrent(Parameter parameter, TimeFrame timeFrame)
+	{
+		const DateTime sDateTime(timeFrame);
+
+		return sDateTime.GetParameter(parameter);
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// IsDST
+	//
+	// Returns true if the time is daylight savings time. This function 
+	// assumes that DST is valid for the given current locale; some locales 
+	// within the United States don't observe DST.
+	//
+	EASTDC_API bool IsDST()
+	{
+		time_t nTime = time(NULL);
+		struct tm* const pTime = localtime(&nTime); // Find out if the local time is in DST
+
+		return pTime->tm_isdst > 0;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// IsDSTDateTime
+	//
+	EASTDC_API bool IsDSTDateTime(int64_t dateTimeSeconds)
+	{
+		// DateTime seconds, based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight) (not time_t).
+		int64_t timeTSeconds = DateTimeSecondsToTimeTSeconds(dateTimeSeconds);
+		time_t  time = (time_t)timeTSeconds; // For some platforms this might result in a 64 to 32 bit chop, though the list bits should be all zero.
+
+		struct tm* const pTime = localtime(&time);
+		return pTime->tm_isdst > 0;
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetDaylightSavingsBias
+	//
+	// Returns the number of seconds that the current time is daylight 
+	// savings adjusted from the conventional time. Adding this value 
+	// to the conventional time yields the time when adjusted for 
+	// daylight savings. Some locations implement daylight savings offsets
+	// that are a half hour instead of an hour. We ignore these, as they 
+	// are uncommon and problematic.
+	//
+	EASTDC_API int64_t GetDaylightSavingsBias()
+	{  
+		return 3600;
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetTimeZoneBias
+	//
+	// Returns the number of seconds that the local time zone is off of UTC.
+	// Adding this value to the current UTC yields the current local time.
+	// For locales in the United states, this is usually a negative number
+	// like -28800 (8 hours behind UTC). For locales East of Europe it will
+	// be a positive value (hours ahead of UTC).
+	//
+	EASTDC_API int64_t GetTimeZoneBias()
+	{
+		#if defined(EA_PLATFORM_WINDOWS)
+			TIME_ZONE_INFORMATION tz;            
+			DWORD rv = GetTimeZoneInformation(&tz);
+			EA_ASSERT(rv != TIME_ZONE_ID_INVALID);
+			EA_UNUSED(rv);
+
+			return (tz.Bias * -60);
+
+		#elif defined(EA_PLATFORM_APPLE)
+			// http://linux.die.net/man/2/gettimeofday
+			struct timeval  tv;
+			struct timezone tz;
+			gettimeofday(&tv, &tz);
+			return (tz.tz_minuteswest * -60);
+
+		#elif defined(EA_PLATFORM_ANDROID) && __ANDROID_API__ >= 8
+			tzset();
+			return -timezone; // Seconds West of GMT
+
+		// Disabled until we can verify these are correct on their respective machines:
+		//#elif defined(EA_PLATFORM_LINUX)
+		//    // http://linux.die.net/man/3/daylight
+		//    return timezone;
+
+		#elif defined(EA_PLATFORM_UNIX)
+			// BSD doesn't support the timezone parameter of gettimeofday. 
+			// However, we can deduce the time zone offset by taking an properly-chosen GM time_t 
+			// value and convert it with both localtime() and gmtime() and see what the seconds difference is.
+			const time_t jan3rd1970 = (60 * 60 * 24 * 2);
+			struct tm    tmGM;
+
+			gmtime_r(&jan3rd1970, &tmGM);
+			time_t tLocal = mktime(&tmGM);
+			return (jan3rd1970 - tLocal); // This will be a negative number like -28800 (PST time zone).
+
+		#elif EASTDC_UTC_TIME_AVAILABLE
+			DateTime sDateTimeLocal(0);
+			DateTime sDateTimeUTC(0);
+			int64_t  nSecondsLocal;
+			int64_t  nSecondsUTC;
+			bool     bIsDST;
+			
+			// The reason the logic below exists is because this platform don't provide functions 
+			// to get the time zone bias, but often let you infer it because they provide 
+			// functions to tell the local time and the UTC time. One possibility is the
+			// case that the second turned over between the two time readings.
+
+			//Debug code.
+			//#if defined(EA_PLATFORM_MICROSOFT)
+			//    _putenv_s("TZ", "GST-1GDT");
+			//    _tzset();
+			//#endif
+
+			sDateTimeLocal.Set(kTimeFrameLocal, false); // Intentionally use 'false' here so that SetInternal doesn't call GetTimeOfDay, which can recursively call us back here and loop indefinitely.
+			bIsDST = IsDST(); // Intentionally call this in between the two Set calls.
+			sDateTimeUTC.Set(kTimeFrameUTC, false);
+
+			nSecondsLocal = sDateTimeLocal.GetSeconds();
+			nSecondsUTC   = sDateTimeUTC.GetSeconds();
+
+			// These seconds should be an even number of minutes apart. If they aren't then 
+			// the second must have turned over between the two Set calls above. So we detect
+			// that and handle it.
+			const int64_t difference      = (nSecondsUTC > nSecondsLocal) ? (nSecondsUTC - nSecondsLocal) : (nSecondsLocal - nSecondsUTC);
+			const int64_t differenceMod60 = difference % 60; // Usually this will be zero.
+
+			if(differenceMod60)
+			{
+				if(nSecondsUTC > nSecondsLocal)
+					nSecondsUTC -= differenceMod60;
+				else
+					nSecondsUTC -= (60 - differenceMod60);
+			}
+
+			if(bIsDST)
+				nSecondsLocal -= 3600;
+
+			return (nSecondsLocal - nSecondsUTC);
+		#else
+			return 0;
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetTimeZoneName
+	//
+	EASTDC_API bool GetTimeZoneName(char8_t* pName, bool bDaylightSavingsName)
+	{
+		#if defined(EA_PLATFORM_MICROSOFT) && defined(_MSC_VER) && (_MSC_VER >= 1400)
+			EA_COMPILETIME_ASSERT(EASTDC_UTC_TIME_AVAILABLE == 1);
+
+			size_t  size   = 0;                        // "The supplied pName must have a capacity of at least 8 bytes."
+			errno_t result = _get_tzname(&size, pName, kTimeZoneNameCapacity, bDaylightSavingsName ? 1 : 0);
+			return (result == 0);
+
+		#elif defined(EA_PLATFORM_UNIX) && EASTDC_UNIX_TZNAME_AVAILABLE
+			EA_COMPILETIME_ASSERT(EASTDC_UTC_TIME_AVAILABLE == 1); // If this assertion fails then EASTDC_UTC_TIME_AVAILABLE is 0 whereas it really could be 1.
+
+			const char* pTZName = tzname[bDaylightSavingsName ? 1 : 0];
+			Strncpy(pName, pTZName, kTimeZoneNameCapacity); // "The supplied pName must have a capacity of at least 8 bytes."
+			pName[7] = 0;
+			return true;
+
+		#else
+			EA_UNUSED(bDaylightSavingsName);
+
+			#if EASTDC_UTC_TIME_AVAILABLE
+				int64_t b = GetTimeZoneBias();
+				Snprintf(pName, kTimeZoneNameCapacity, "+%6lld", b);
+			#else
+				Strlcpy(pName, "LT", kTimeZoneNameCapacity); // Local Time. This is merely our convention for when we have no time zone information other than to say it's the local time.
+			#endif
+
+			return true;
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// DateTimeToTm
+	//
+	// Converts a DateTime to a C tm struct.
+	//
+	EASTDC_API void DateTimeToTm(const DateTime& dateTime, tm& time)
+	{
+		// time doesn't have a field for anything more precise than seconds, so kParameterNanosecond is irrelevant, as we don't apply rounding to nanoseconds.
+		time.tm_sec   = (int)dateTime.GetParameter(kParameterSecond);
+		time.tm_min   = (int)dateTime.GetParameter(kParameterMinute);
+		time.tm_hour  = (int)dateTime.GetParameter(kParameterHour);
+		time.tm_mday  = (int)dateTime.GetParameter(kParameterDayOfMonth);
+		time.tm_mon   = (int)dateTime.GetParameter(kParameterMonth) - kMonthJanuary;
+		time.tm_year  = (int)dateTime.GetParameter(kParameterYear) - 1900;
+		time.tm_wday  = (int)dateTime.GetParameter(kParameterDayOfWeek) - kDayOfWeekSunday;
+		time.tm_yday  = (int)dateTime.GetParameter(kParameterDayOfYear) - 1;
+		time.tm_isdst = 0; // We don't have a way to tell if an arbitrary dateTime object is daylight savings time. 
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// TmToDateTime
+	//
+	// Converts a C tm struct to a DateTime.
+	//
+	EASTDC_API void TmToDateTime(const tm& time, DateTime& dateTime)
+	{
+		dateTime.Set((uint32_t)(time.tm_year + 1900), (uint32_t)(time.tm_mon + kMonthJanuary), 
+					 (uint32_t)time.tm_mday, (uint32_t)time.tm_hour, (uint32_t)time.tm_min, (uint32_t)time.tm_sec);
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// DateTimeToFileTime
+	//
+	// Converts a DateTime to a FILETIME struct.
+	// A FILETIME contains a 64-bit value representing the number of 
+	// 100-nanosecond intervals since January 1, 1601 (UTC).
+	//
+	EASTDC_API void DateTimeToFileTime(const DateTime& dateTime, _FILETIME& time)
+	{
+		_SYSTEMTIME systemTime;
+		DateTimeToSystemTime(dateTime, systemTime); 
+
+		#if defined(EA_PLATFORM_MICROSOFT)
+			SystemTimeToFileTime(&systemTime, &time); // OS call.
+		#else
+			int64_t month, year;
+
+			if(systemTime.wMonth >= 3) // If month is after a leap year day could occur)...
+			{
+				month = systemTime.wMonth + 1;
+				year  = systemTime.wYear;
+			} 
+			else
+			{
+				month = systemTime.wMonth + 13;
+				year  = systemTime.wYear - 1;
+			}
+
+			const int64_t endOfCenturyLeapYearCount = ((3 * (year / 100) + 3) / 4);   // http://en.wikipedia.org/wiki/Century_leap_year
+
+			// Convert the dateTime value to 100 ns intervals since Jan 1, 1601.
+			const int64_t day = ((36525 * year) / 100) - endOfCenturyLeapYearCount +
+								 ((1959 * month) / 64) + systemTime.wDay - 584817;   // Subtract 584817 to make the time based on 1601-01-01.
+
+			const int64_t time64 = ((((day                       * kHoursPerDay      + 
+									   systemTime.wHour)         * kMinutesPerHour   + 
+									   systemTime.wMinute)       * kSecondsPerMinute + 
+									   systemTime.wSecond)       * 1000              +     // 1000 = milliseconds per second.
+									   systemTime.wMilliseconds) * 10000;                  // 10000 == (100 ns intervals per millisecond).
+
+			time.dwLowDateTime  = (uint32_t)time64;
+			time.dwHighDateTime = (uint32_t)(time64 >> INT64_C(32));
+
+		#endif
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// FileTimeToDateTime
+	//
+	// Converts a FILETIME struct to a DateTime.
+	//
+	EASTDC_API void FileTimeToDateTime(const _FILETIME& time, DateTime& dateTime)
+	{
+		#if defined(EA_PLATFORM_MICROSOFT)
+			_SYSTEMTIME systemTime;
+			FileTimeToSystemTime(&time, &systemTime);   // OS call.
+			SystemTimeToDateTime(systemTime, dateTime);
+		#else
+			// A FILETIME contains a 64-bit value representing the number of 
+			// 100-nanosecond intervals since January 1, 1601 (UTC).
+			EA_UNUSED(time);
+			memset(&dateTime, 0, sizeof(dateTime));
+
+			// To do: Implement this.
+		#endif
+	}
+
+
+
+	///////////////////////////////////////////////////////////////////////
+	// DateTimeToSystemTime
+	//
+	// Converts a DateTime to a SYSTEMTIME struct.
+	//
+	EASTDC_API void DateTimeToSystemTime(const DateTime& dateTime, _SYSTEMTIME& time)
+	{
+		time.wYear          = (uint16_t) dateTime.GetParameter(kParameterYear);
+		time.wMonth         = (uint16_t) dateTime.GetParameter(kParameterMonth);
+		time.wDayOfWeek     = (uint16_t)(dateTime.GetParameter(kParameterDayOfWeek) - 1);
+		time.wDay           = (uint16_t) dateTime.GetParameter(kParameterDayOfMonth);
+		time.wHour          = (uint16_t) dateTime.GetParameter(kParameterHour);
+		time.wMinute        = (uint16_t) dateTime.GetParameter(kParameterMinute);
+		time.wSecond        = (uint16_t) dateTime.GetParameter(kParameterSecond);
+		time.wMilliseconds  = (uint16_t)(dateTime.GetParameter(kParameterNanosecond) / 1000000);
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// SystemTimeToDateTime
+	//
+	// Converts a SYSTEMTIME struct to a DateTime.
+	//
+	EASTDC_API void SystemTimeToDateTime(const _SYSTEMTIME& time, DateTime& dateTime)
+	{
+		dateTime = DateTime(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
+		dateTime.SetParameter(kParameterNanosecond, time.wMilliseconds * 1000000);
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// GetTimeOfDay
+	//
+	// This behaves the same as the Posix gettimeofday function, with the 
+	// addition that pTZ is formally supported and that bUTC specifies to 
+	// get the time of day in UTC instead of local time.
+	// pTZ is an output parameter and it's input value has no affect on the
+	// function behavior and return value.
+	// Note that a timeval has the same meaning as time_t except that it contains
+	// sub-second information.
+	//
+	EASTDC_API int GetTimeOfDay(timeval* pTV, timezone_* pTZ, bool bUTC)
+	{
+		timezone_ tz;
+
+		if(!pTZ)
+			pTZ = &tz;
+
+		#if defined(EA_PLATFORM_MICROSOFT)
+			#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+				static bool tzsetCalled = false;
+
+				if(!tzsetCalled)
+				{
+					_tzset();
+					tzsetCalled = true;
+				}
+			#endif
+
+			if(pTV)
+			{
+				FILETIME      ft;
+				LARGE_INTEGER li;
+				int64_t       t;
+
+				GetSystemTimeAsFileTime(&ft);       // Microsoft call. The information is in Coordinated Universal Time (UTC) format.
+
+				li.LowPart  = ft.dwLowDateTime;
+				li.HighPart = ft.dwHighDateTime;
+
+				t  = li.QuadPart;                   // In 100-nanosecond intervals
+				t -= INT64_C(116444736000000000);   // Offset to the Epoch time
+				t /= 10;                            // In microseconds
+
+				pTV->tv_sec  = (long)(t / 1000000);
+				pTV->tv_usec = (long)(t % 1000000);
+
+				if(!bUTC) // If should convert to local time...
+					pTV->tv_sec -= _timezone;
+			}
+
+			pTZ->tz_minuteswest = _timezone / 60;  // _timezone is a positive value, as opposed to the negative value that 'time zone bias' is.
+			pTZ->tz_dsttime     = _daylight;
+
+			return 0;
+
+		#elif EASTDC_CLOCK_GETTIME_AVAILABLE
+
+			timeval tv;
+
+			if(!pTV) // Unix gettimeofday requires a valid timeval pointer.
+				pTV = &tv;
+
+			timespec ts;
+			int result = clock_gettime(CLOCK_REALTIME, &ts);
+
+			// To do: Handle time zone. See Unix code below.
+			memset(pTZ, 0, sizeof(timezone_));
+
+			if((result == 0) && pTV) // If OK and if the user specified a pTV to fill in...
+			{
+				// Convert timespec to timeval. They are similar except for the tv_usec/tv_nsec member.
+				pTV->tv_sec  = ts.tv_sec;
+				pTV->tv_usec = (suseconds_t)(ts.tv_nsec / 1000);
+
+				if(!bUTC) // If should convert to local time...
+					pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
+			}
+
+			return result;
+
+		#elif defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_IPHONE)
+
+			timeval tv;
+
+			if(!pTV) // Unix gettimeofday requires a valid timeval pointer.
+				pTV = &tv;
+
+			int result = gettimeofday(pTV, pTZ); // The gettimeofday function obtains the current time, expressed as seconds and microseconds since 00:00 Coordinated Universal Time (UTC), January 1, 1970
+
+			#if defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_ANDROID) // pTZ parameter is not always supported on linux platforms. timezone not defined on some Android devices (i.e. Sony Xperia X10i).
+				pTZ->tz_minuteswest = timezone / 60; //timezone is seconds west of GMT
+				time_t nowtm = pTV->tv_sec;
+				tm tmResult;
+				tm* ptmResult;
+				ptmResult = localtime_r(&nowtm, &tmResult);
+				result = result != 0 ? result : !(ptmResult == &tmResult); //if gettimeofday failed return that error code, otherwise return 1 if localtime_r returned wrong pointer, otherwise 0
+				pTZ->tz_dsttime = tmResult.tm_isdst;
+			#endif            
+
+			if((result == 0) && pTV) // If OK and if the user specified a pTV to fill in...
+			{
+				if(!bUTC) // If should convert to local time...
+					pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
+			}
+
+			return result;
+
+		#else
+
+			// We implement the most basic version which is likely to compile: 
+			// use just the time_t function and don't support time zones.
+
+			pTZ->tz_minuteswest = (int)GetTimeZoneBias() / -60; // tz_minuteswest is a positive value in the US, as opposed to the negative value that 'time zone bias' is.
+			pTZ->tz_dsttime     = 0; // Assume there is never daylight savings time.
+
+			if(pTV)
+			{
+				pTV->tv_sec  = time(NULL); // The time function shall return the value of time in seconds since the Epoch (00:00 Coordinated Universal Time (UTC), January 1, 1970). 
+				pTV->tv_usec = 0;
+
+				if(!bUTC) // If should convert to local time...
+					pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
+			}
+			return 0;
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////
+	// TimevalDifference
+	//
+	// Calculates the result of TVA - TVB.
+	// Returns 1 if TVA >= TVB, 0 if TVA == TVB, -1 if TVA < TVB. Much like strcmp().
+	// Note that a timeval has the same meaning as time_t except that it contains
+	// sub-second information.
+	//
+	EASTDC_API int TimevalDifference(const timeval* pTVA, const timeval* pTVB, timeval* pTVResult)
+	{
+		timeval tva(*pTVA);
+		timeval tvb(*pTVB);
+
+		// Perform the carry for the later subtraction by updating y.
+		if(tva.tv_usec < tvb.tv_usec)
+		{
+			const int nsec = ((tvb.tv_usec - tva.tv_usec) / 1000000) + 1;
+
+			tvb.tv_usec -= 1000000 * nsec;
+			tvb.tv_sec  += nsec;
+		}
+
+		if((tva.tv_usec - tvb.tv_usec) > 1000000)
+		{
+			const int nsec = (tvb.tv_usec - tva.tv_usec) / 1000000;
+
+			tvb.tv_usec += 1000000 * nsec;
+			tvb.tv_sec  -= nsec;
+		}
+
+		// Compute the time remaining to wait. tv_usec is always positive.
+		pTVResult->tv_sec  = tva.tv_sec  - tvb.tv_sec;
+		pTVResult->tv_usec = tva.tv_usec - tvb.tv_usec;
+
+		if(tva.tv_sec == tvb.tv_sec)
+		{
+			if(tva.tv_usec == tvb.tv_usec)
+				return 0;
+
+			return (tva.tv_usec > tvb.tv_usec) ? 1 : -1;
+		}
+
+		return (tva.tv_sec > tvb.tv_sec) ? 1 : -1;
+	}
+
+
+	namespace Internal
+	{
+		#define SUNDAY_BASED_WEEK_NUMBER(pTM)  (((pTM)->tm_yday + 7 - ((pTM)->tm_wday)) / 7)
+		#define MONDAY_BASED_WEEK_NUMBER(pTM)  (((pTM)->tm_yday + 7 - ((pTM)->tm_wday ? (pTM)->tm_wday - 1 : 6)) / 7)
+
+
+		static const TimeLocale gDefaultTimeLocale = 
+		{
+			{ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" },
+			{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" },
+			{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
+			{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" },
+			{ "AM", "PM" },
+			"%a %b %d %H:%M:%S %Y",
+			"%m/%d/%y",
+			"%H:%M:%S",
+			"%I:%M:%S %p"
+		};
+
+
+		static bool Append(const char* EA_RESTRICT p, char* EA_RESTRICT & pTimeString, size_t& capacity)
+		{
+			for(; capacity; ++pTimeString, --capacity)
+			{
+				if((*pTimeString = *p++) == 0)
+					return true;
+			}
+
+			return false;
+		}
+
+
+		static bool WriteInt(int n, int digits, char pad, bool removeLeadingZeroes, char* EA_RESTRICT & pTimeString, size_t& capacity) 
+		{
+			char  buffer[10];
+			char* p;
+
+			buffer[9] = 0;
+			for(p = buffer + 8; (n > 0) && (p > buffer); n /= 10, --digits)
+				*p-- = (char)((n % 10) + '0');
+
+			while((p > buffer) && (digits-- > 0))
+				*p-- = (char)pad;
+
+			if(removeLeadingZeroes)
+			{
+				while((p[1] == '0') || (p[1] == ' '))
+					++p;
+
+				if (p[1] == 0)
+					--p;
+			}
+
+			return Append(++p, pTimeString, capacity);
+		}
+
+
+		#if defined(EA_PLATFORM_WINDOWS)
+			#define EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE 1   // The Microsoft API supports these functions only for desktop target platforms (e.g. Win32, Win64).
+		#else
+			#define EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE 0
+		#endif
+
+		#if !EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+			// This function converts any "%" chars in the format string to "%#", writing the new format to the supplied buffer.
+			// Returns NULL for the only possible error condition: pBuffer's capacity was insufficient.
+			static char* ConvertFormatSpecifiersToAlternates(char* pBuffer, size_t bufferCapacity, const char* pFormat)
+			{
+				char* pBufferCurrent = pBuffer;
+				char* pBufferEnd     = pBuffer + (bufferCapacity - 2); // -2 because we can write two chars per loop below. If bufferCapacity is < 2 then the loop below will quit right away, which is what we want.
+
+				while(*pFormat && (pBufferCurrent < pBufferEnd))
+				{
+					*pBufferCurrent++ = *pFormat;
+
+					if(*pFormat++ == '%')
+						*pBufferCurrent++ = '#';
+				}
+
+				*pBufferCurrent = 0;
+
+				return (*pFormat == 0) ? pBuffer : NULL;
+
+			}
+		#endif
+	}
+
+
+	// Posix alternative formats:
+	// "Some conversion specifiers can be modified by the E or O modifier characters 
+	//  to indicate that an alternative format or specification should be used rather 
+	//  than the one normally used by the unmodified conversion specifier. If the 
+	//  alternative format or specification does not exist for the current locale, 
+	//  (see ERA in the XBD specification, Section 5.3.5) the behaviour will be as if 
+	//  the unmodified conversion specification were used."
+	//
+	// Microsoft alternative formats:
+	// Also, Microsoft (alternatively to Posix  E and O) supports using the # char 
+	// after % to indicate alternative behaviour as follows:
+	//     %#a, %#A, %#b, %#B, %#h, %#p, %#X, %#z, %#Z, %#%             # flag is ignored.
+	//     %#c                                                          Long date and time representation, appropriate for current locale. For example: "Tuesday, March 14, 1995, 12:41:29".
+	//     %#x                                                          Long date representation, appropriate to current locale. For example: "Tuesday, March 14, 1995".
+	//     %#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y   Remove leading zeros (if any).
+
+	EASTDC_API size_t Strftime(char* EA_RESTRICT pTimeString, size_t timeStringCapacity, 
+							   const char* EA_RESTRICT pFormat, const tm* EA_RESTRICT pTM, const TimeLocale* EA_RESTRICT pTimeLocale)
+	{
+		using namespace Internal;
+
+		size_t capacity = timeStringCapacity;
+		bool   bGMT     = false; // To consider: provide a way for the user to set this to true or to directly specify the time zone.
+		char   buffer[256];
+
+		if(!pTimeLocale)
+			pTimeLocale = &gDefaultTimeLocale;
+
+		for(; *pFormat; ++pFormat)
+		{
+			if(*pFormat == '%')
+			{
+				char cAlt = 0;  // Alternative format specifier.
+
+				if((*++pFormat == 'E') || (*pFormat == 'O') || (*pFormat == '#'))
+					cAlt = *pFormat++;
+
+				switch(*pFormat)
+				{
+					case '\0':      // We are at the end of the string, with a (not valid) trailing '%' char.
+						EA_FAIL_M("EAStdC Strftime");  // Incomplete format specifier.
+						--pFormat;
+						break;
+
+					case '%':   // %% is replaced by a % char.
+						break;
+
+					case 'a': // is replaced by the locale's abbreviated weekday name. 
+						if((pTM->tm_wday < 0) || (pTM->tm_wday > 6))
+							return 0;
+
+						if(!Append(pTimeLocale->mAbbrevDay[pTM->tm_wday], pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'A': // is replaced by the locale's full weekday name. 
+						if((pTM->tm_wday < 0) || (pTM->tm_wday > 6))
+							return 0;
+
+						if(!Append(pTimeLocale->mDay[pTM->tm_wday], pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'b': // is replaced by the locale's abbreviated month name. 
+					case 'h':
+						if((pTM->tm_mon < 0) || (pTM->tm_mon > 11))
+							return 0;
+
+						if(!Append(pTimeLocale->mAbbrevMonth[pTM->tm_mon], pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'B': // is replaced by the locale's full month name. 
+						if((pTM->tm_mon < 0) || (pTM->tm_mon > 11))
+							return 0;
+
+						if(!Append(pTimeLocale->mMonth[pTM->tm_mon], pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'c': // is replaced by the locale's appropriate date and time representation.
+					{
+						#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+							const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
+							const uint32_t   dateFormat = (cAlt == '#') ? DATE_SHORTDATE : DATE_LONGDATE;
+							const char       empty[2]   = { ' ', 0 };
+
+							if(!EAStdCGetDateFormat(dateFormat, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity) || !Append(empty, pTimeString, capacity))
+								return 0;
+							if(!EAStdCGetTimeFormat(0, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
+								return 0;
+						#else
+							char formatBuffer[256]; formatBuffer[0] = 0;
+							const char* pFormatTemp = pTimeLocale->mDateTimeFormat;
+
+							if (cAlt == '#')
+								pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
+
+							const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
+							if(!len)
+								return 0;
+							pTimeString += len;
+							capacity    -= len;
+						#endif
+						continue;
+					}
+
+					case 'C': // is replaced by the century number (the year divided by 100 and truncated to an integer) as a decimal number [00-99]. 
+					{
+						int year = (pTM->tm_year + 1900) / 100;
+						if(year == 0 && cAlt == '#')
+						{
+							if(!WriteInt(year, 1, '0', false, pTimeString, capacity))
+								return 0;
+						}
+						else
+						{
+							if(!WriteInt(year, 2, '0', cAlt == '#', pTimeString, capacity))
+								return 0;
+						}
+						continue;
+					}
+
+					case 'd': // is replaced by the day of the month as a decimal number [01,31]. 
+						if(!WriteInt(pTM->tm_mday, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'D': // same as %m/%d/%y. 
+					{
+						const size_t len = Strftime(pTimeString, capacity, "%m/%d/%y", pTM);
+						if(!len)
+							return 0;
+						pTimeString += len;
+						capacity    -= len;
+						continue;
+					}
+
+					case 'e': // is replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space. 
+						if(!WriteInt(pTM->tm_mday, 2, ' ', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+					
+					case 'F': // same as %Y-%m-%d 
+					{
+						const size_t len = Strftime(pTimeString, capacity, "%Y-%m-%d", pTM);
+						if(!len)
+							return 0;
+						pTimeString += len;
+						capacity    -= len;
+						continue;
+					}
+
+					case 'g': //Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]
+					{
+						//Unsupported as of yet
+						continue;
+					}
+
+					case 'G': //Replaced by the week-based year (see below) as a decimal number (for example, 1977).
+					{
+						//Unsupported as of yet
+						continue;
+					}
+
+					case 'H': // is replaced by the hour (24-hour clock) as a decimal number [00,23]. 
+						if(!WriteInt(pTM->tm_hour, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'I': // is replaced by the hour (12-hour clock) as a decimal number [01,12]. 
+						if(!WriteInt((pTM->tm_hour % 12) ? (pTM->tm_hour % 12) : 12, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'j': // is replaced by the day of the year as a decimal number [001,366]. 
+						if(!WriteInt(pTM->tm_yday + 1, 3, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'M': // is replaced by the minute as a decimal number [00,59]. 
+						if(!WriteInt(pTM->tm_min, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'm': // is replaced by the month as a decimal number [01,12]. 
+						if(!WriteInt(pTM->tm_mon + 1, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'n': // is replaced by a newline character.
+						buffer[0] = '\n';
+						buffer[1] = 0;
+						if(!Append(buffer, pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'p': // is replaced by the locale's equivalent of either a.m. or p.m. 
+						if(!Append(pTimeLocale->mAmPm[pTM->tm_hour >= 12], pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'r': // is replaced by the time in a.m. and p.m. notation; in the POSIX locale this is equivalent to %I:%M:%S %p. 
+					{
+						const size_t len = Strftime(pTimeString, capacity, pTimeLocale->mTimeFormatAmPm, pTM);
+						if(!len)
+							return 0;
+						pTimeString += len;
+						capacity    -= len;
+						continue;
+					}
+
+					case 'R': // is replaced by the time in 24 hour notation (%H:%M). 
+					{
+						const size_t len = Strftime(pTimeString, capacity, "%H:%M", pTM);
+						if(!len)
+							return 0;
+						pTimeString += len;
+						capacity    -= len;
+						continue;
+					}
+
+					case 'S': // is replaced by the second as a decimal number [00,61]. 
+						if(!WriteInt(pTM->tm_sec, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 't': // is replaced by a tab character. 
+						buffer[0] = '\t';
+						buffer[1] = 0;
+						if(!Append(buffer, pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'T': // is replaced by the time (%H:%M:%S). 
+					{
+						const size_t len = Strftime(pTimeString, capacity, "%H:%M:%S", pTM);
+						if(!len)
+							return 0;
+						pTimeString += len;
+						capacity    -= len;
+						continue;
+					}
+
+					case 'u': // is replaced by the weekday as a decimal number [1,7], with 1 representing Monday. 
+						if(!WriteInt(pTM->tm_wday ? pTM->tm_wday : 7, 1, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'U': // is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. 
+					{
+						int wday = SUNDAY_BASED_WEEK_NUMBER(pTM);
+						if(wday == 0 && cAlt == '#')
+						{
+							if(!WriteInt(wday, 1, '0', false, pTimeString, capacity))
+								return 0;
+						}
+						else
+						{
+							if(!WriteInt(wday, 2, '0', cAlt == '#', pTimeString, capacity))
+								return 0;
+						}
+						continue;
+					}
+
+					case 'V': // is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. If the week containing 1 January has four or more days in the new year, then it is considered week 1. Otherwise, it is the last week (53) of the previous year, and the next week is week 1. 
+					{
+						int week = MONDAY_BASED_WEEK_NUMBER(pTM);
+						int days = ((pTM->tm_yday + 7 - (pTM->tm_wday ? pTM->tm_wday - 1 : 6)) % 7);
+
+						if(days >= 4)
+							week++;
+						else if(week == 0)
+							week = 53;
+
+						if(!WriteInt(week, 2, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+
+						continue;
+					}
+
+					case 'w': // is replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. 
+						if(!WriteInt(pTM->tm_wday, 1, '0', false, pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'W': // %W is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. 
+					{
+						int wday = MONDAY_BASED_WEEK_NUMBER(pTM);
+						if(wday == 0 && cAlt == '#')
+						{
+							if(!WriteInt(wday, 1, '0', false, pTimeString, capacity))
+								return 0;
+						}
+						else
+						{
+							if(!WriteInt(wday, 2, '0', cAlt == '#', pTimeString, capacity))
+								return 0;
+						}
+						continue;
+					}
+
+					case 'x': // %x is replaced by the locale's appropriate date representation. 
+					{
+						#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+							const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
+							const uint32_t   dateFormat = (cAlt == '#') ?  DATE_SHORTDATE : DATE_LONGDATE;
+
+							if(!EAStdCGetDateFormat(dateFormat, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
+								return 0;
+						#else
+							char formatBuffer[256]; formatBuffer[0] = 0;
+							const char* pFormatTemp = pTimeLocale->mDateFormat;
+
+							if (cAlt == '#')
+								pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
+
+							const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
+							if(!len)
+								return 0;
+							pTimeString += len;
+							capacity    -= len;
+						#endif
+						continue;
+					}
+
+					case 'X': // %X is replaced by the locale's appropriate time representation. 
+					{
+						#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+							const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
+
+							if(!EAStdCGetTimeFormat(0, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
+								return 0;
+						#else
+							char formatBuffer[256]; formatBuffer[0] = 0;
+							const char* pFormatTemp = pTimeLocale->mTimeFormat;
+
+							if (cAlt == '#')
+								pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
+
+							const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
+							if(!len)
+								return 0;
+							pTimeString += len;
+							capacity    -= len;
+						#endif
+						continue;
+					}
+
+					case 'y': // %y is replaced by the year without century as a decimal number [00,99]. 
+					{
+						int year = (pTM->tm_year + 1900) % 100;
+						if(year == 0 && cAlt == '#')
+						{
+							if(!WriteInt(year, 1 /*strlen("99")*/, '0', false, pTimeString, capacity))
+								return 0;
+						}
+						else
+						{
+							if(!WriteInt(year, 2 /*strlen("99")*/, '0', cAlt == '#', pTimeString, capacity))
+								return 0;
+						}
+						continue;
+					}
+					case 'Y': // %Y is replaced by the year with century as a decimal number. 
+						if(!WriteInt((pTM->tm_year + 1900), 4 /*strlen("9999")*/, '0', cAlt == '#', pTimeString, capacity))
+							return 0;
+						continue;
+
+					case 'z': //Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no characters if no timezone is determinable. 
+							  //For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). [CX]   If tm_isdst is zero, the standard time offset is used. 
+							  //If tm_isdst is greater than zero, the daylight savings time offset is used. If tm_isdst is negative, no characters are returned.  [ tm_isdst]
+					{
+						int tzBias = (int)GetTimeZoneBias();  // tzBias will be a negative number in the United States.
+						int hour   = abs(tzBias / 3600);
+						int min    = (abs(tzBias) - (hour * 3600)) / 60;
+			 
+						buffer[5] = '\0';
+						buffer[4] = (char)((min % 10) + '0');
+						min /= 10;
+						buffer[3] = (char)((min % 10) + '0');
+						buffer[2] = (char)((hour % 10) + '0');
+						hour /= 10;
+						buffer[1] = (char)((hour % 10) + '0');
+						buffer[0] = (tzBias < 0) ? '-' : '+';
+
+						if(!Append(buffer, pTimeString, capacity))
+							return 0;
+						continue;
+					}
+
+					case 'Z': // %Z is replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. 
+					{
+						const char* pTZName = bGMT ? "GMT" : buffer;
+
+						if(!bGMT)
+							GetTimeZoneName(buffer, pTM->tm_isdst != 0);
+
+						if(pTZName && !Append(pTZName, pTimeString, capacity))
+							return 0;
+						continue;
+					}
+
+					default:
+						EA_FAIL_M("EAStdC Strftime"); // Unsupported format specifier. Just print it as-is.
+						break;
+				}
+			}
+
+			if(!capacity--)
+				return 0;
+
+			*pTimeString++ = *pFormat;
+		}
+
+		*pTimeString = 0;
+
+		return (timeStringCapacity - capacity);
+
+	} // Strftime
+
+
+	static bool ReadInt(const char*& pString, int& n, int nMin, int nMax)
+	{
+		int result = 0;
+		int rMax   = nMax;
+	 
+		if((*pString >= '0') && (*pString <= '9'))
+		{
+			do {
+				result *= 10;
+				result += *pString++ - '0';
+				rMax /= 10;
+			} while(rMax && (*pString >= '0') && (*pString <= '9') && ((result * 10) <= nMax));
+		 
+			if((result >= nMin) && (result <= nMax))
+			{
+				n = result;
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	static bool ParseDate(bool bAlt, const char *&p, tm* EA_RESTRICT pTM, const TimeLocale *pTimeLocale)
+	{
+		#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+			EA_UNUSED(pTimeLocale);
+			const int formatSize = 256;
+			char buffer[formatSize]; buffer[0] = 0;
+			char* c;
+			char* tokenizerContext = NULL;
+			size_t index = 0;
+
+			LCTYPE localeDateType = bAlt ? LOCALE_SSHORTDATE : LOCALE_SLONGDATE;
+
+			size_t bufferSize = static_cast<size_t>(EAStdCGetLocaleInfo(localeDateType, NULL, 0));
+			char* dateFormat = static_cast<char *>(EAAlloca(bufferSize + 1));
+			char* tmp = static_cast<char *>(EAAlloca(bufferSize + 1));
+			EA_ANALYSIS_ASSUME(dateFormat != NULL);
+			EAStdCGetLocaleInfo(localeDateType, dateFormat, static_cast<int>(bufferSize));
+			dateFormat[bufferSize] = 0;
+
+			Strlcpy(tmp, dateFormat, bufferSize + 1);
+			c = Strtok(tmp, "/, -", &tokenizerContext);
+			while(c != 0)
+			{
+				if(Strncmp(c, "d", formatSize) == 0)
+					Strlcat(buffer, "%#d", formatSize);
+				else if(Strncmp(c, "dd", formatSize) == 0)
+					Strlcat(buffer, "%d", formatSize);
+				else if(Strncmp(c, "ddd", formatSize) == 0)
+					Strlcat(buffer, "%#a", formatSize);
+				else if(Strncmp(c, "dddd", formatSize) == 0)
+					Strlcat(buffer, "%a", formatSize);
+				else if(Strncmp(c, "M", formatSize) == 0)
+					Strlcat(buffer, "%#m", formatSize);
+				else if(Strncmp(c, "MM", formatSize) == 0)
+					Strlcat(buffer, "%m", formatSize);
+				else if(Strncmp(c, "MMM", formatSize) == 0)
+					Strlcat(buffer, "%#b", formatSize);
+				else if(Strncmp(c, "MMMM", formatSize) == 0)
+					Strlcat(buffer, "%b", formatSize);
+				else if(Strncmp(c, "y", formatSize) == 0)
+					Strlcat(buffer, "%#y", formatSize);
+				else if(Strncmp(c, "yy", formatSize) == 0)
+					Strlcat(buffer, "%y", formatSize);
+				else if(Strncmp(c, "yyyy", formatSize) == 0)
+					Strlcat(buffer, "%Y", formatSize);
+				else if(Strncmp(c, "yyyyy", formatSize) == 0)
+					Strlcat(buffer, "%Y", formatSize);
+				else
+					return false;
+
+				index += Strlen(c);
+				size_t separators = Strcspn(&dateFormat[index], "dmyM");
+				char copiedChar = dateFormat[index + separators];
+				dateFormat[index + separators] = 0;
+				Strlcat(buffer, &dateFormat[index], formatSize);
+				dateFormat[index + separators] = copiedChar;
+				index += separators;
+
+				c = Strtok(NULL, "/, -", &tokenizerContext);
+			}
+
+			if((p = Strptime(p, buffer, pTM)) == NULL)
+				return false;
+		#else
+			char formatBuffer[256]; formatBuffer[0] = 0;
+			const char* pFormatTemp = pTimeLocale->mDateFormat;
+
+			if(bAlt)
+				pFormatTemp = Internal::ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
+
+			if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
+				return false;
+		#endif
+
+		return true;
+	}
+
+	static bool ParseTime(bool bAlt, const char *&p, tm* EA_RESTRICT pTM, const TimeLocale *pTimeLocale)
+	{
+		#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+			EA_UNUSED(pTimeLocale);
+			EA_UNUSED(bAlt);
+
+			const int formatSize = 256;
+			char buffer[formatSize]; buffer[0] = 0;
+
+			size_t bufferSize = static_cast<size_t>(EAStdCGetLocaleInfo(LOCALE_STIMEFORMAT, NULL, 0));
+			char* timeFormat = static_cast<char *>(EAAlloca(bufferSize + 1));
+			char* tmp = static_cast<char *>(EAAlloca(bufferSize + 1));
+			EA_ANALYSIS_ASSUME(timeFormat != NULL);
+
+			EAStdCGetLocaleInfo(LOCALE_STIMEFORMAT, timeFormat, static_cast<int>(bufferSize));
+			Strlcpy(tmp, timeFormat, bufferSize);
+
+			char* tokenizerContext = NULL;
+			size_t index = 0;
+			char*  c = Strtok(tmp, ": ", &tokenizerContext);
+
+			while(c != 0)
+			{
+				if(Strncmp(c, "h", formatSize) == 0)
+					Strlcat(buffer, "%#I", formatSize);
+				else if(Strncmp(c, "hh", formatSize) == 0)
+					Strlcat(buffer, "%I", formatSize);
+				else if(Strncmp(c, "H", formatSize) == 0)
+					Strlcat(buffer, "%#H", formatSize);
+				else if(Strncmp(c, "HH", formatSize) == 0)
+					Strlcat(buffer, "%H", formatSize);
+				else if(Strncmp(c, "m", formatSize) == 0)
+					Strlcat(buffer, "%#M", formatSize);
+				else if(Strncmp(c, "mm", formatSize) == 0)
+					Strlcat(buffer, "%M", formatSize);
+				else if(Strncmp(c, "s", formatSize) == 0)
+					Strlcat(buffer, "%#S", formatSize);
+				else if(Strncmp(c, "ss", formatSize) == 0)
+					Strlcat(buffer, "%S", formatSize);
+				else if(Strncmp(c, "t", formatSize) == 0)
+					Strlcat(buffer, "%#p", formatSize);
+				else if(Strncmp(c, "tt", formatSize) == 0)
+					Strlcat(buffer, "%p", formatSize);
+				else
+					return false;
+
+				index += Strlen(c);
+				size_t separators = Strcspn(&timeFormat[index], "hHmst");
+				char copiedChar = timeFormat[index + separators];
+				timeFormat[index + separators] = 0;
+				Strlcat(buffer, &timeFormat[index], formatSize);
+				timeFormat[index + separators] = copiedChar;
+
+				index += separators;
+				c = Strtok(NULL, ": ", &tokenizerContext);
+			}
+
+			if((p = Strptime(p, buffer, pTM)) == NULL)
+				return false;
+		#else
+			char formatBuffer[256]; formatBuffer[0] = 0;
+			const char* pFormatTemp = pTimeLocale->mTimeFormat;
+
+			if(bAlt)
+				pFormatTemp = Internal::ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
+
+			if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
+				return false;
+
+		#endif
+
+		return true;
+	}
+
+
+	EASTDC_API char* Strptime(const char* EA_RESTRICT pTimeString, const char* EA_RESTRICT pFormat, tm* EA_RESTRICT pTM, const TimeLocale* EA_RESTRICT pTimeLocale)
+	{
+		using namespace Internal;
+
+		const char* p = pTimeString;
+		size_t      len = 0;
+		int         i = 0;
+		bool        bSplitYear = false;
+		bool        bAlt = false;
+		char        c;
+
+		if(!pTimeLocale)
+			pTimeLocale = &gDefaultTimeLocale;
+
+		while((c = *pFormat) != 0)
+		{
+			if(Isspace(c))          // If the current format char is whitespace...
+			{                       // 
+				while(Isspace(*p))  // then eat any time string whitespace.
+					p++;
+	 
+				pFormat++;
+				continue;
+			}
+
+			c = *pFormat++;         // c will be non-whitespace.
+
+			if(c != '%')            // If we have a literal char that is outside of a % format sequence...
+			{
+				if(c != *p++)
+					return NULL;
+				continue;
+			}
+
+			bAlt = false;
+			FormatBegin:
+			c = *pFormat++;
+
+			switch (c)
+			{
+				case '%':   // Replaced by %.
+					if(c != *p++)
+						return NULL;
+					break;
+
+				case 'E':   // E alternate representation. 
+				case 'O':   // O alternate representation.
+				case '#':   // # alternate representation.
+					bAlt = true;
+					goto FormatBegin;
+
+				case 'a':   // The day of the week, using the locale's weekday names; either the abbreviated or full name may be specified.
+				case 'A':   // Equivalent to %a.
+				{
+					for(i = 0; i < 7; i++)
+					{
+						len = Strlen(pTimeLocale->mDay[i]);
+
+						if(Strnicmp(pTimeLocale->mDay[i], p, len) == 0)
+							break;
+			 
+						len = Strlen(pTimeLocale->mAbbrevDay[i]);
+
+						if(Strnicmp(pTimeLocale->mAbbrevDay[i], p, len) == 0)
+							break;
+					}
+
+					// Nothing matched.
+					if(i == 7)
+						return NULL;
+
+					pTM->tm_wday = i;
+					p += len;
+					break;
+				}
+
+				case 'b':   // The month, using the locale's month names; either the abbreviated or full name may be specified.
+				case 'B':   // Equivalent to %b.
+				case 'h':   // Equivalent to %b.
+				{
+					for(i = 0; i < 12; i++)
+					{
+						len = Strlen(pTimeLocale->mMonth[i]);
+
+						if(Strnicmp(pTimeLocale->mMonth[i], p, len) == 0)
+							break;
+
+						len = Strlen(pTimeLocale->mAbbrevMonth[i]);
+
+						if(Strnicmp(pTimeLocale->mAbbrevMonth[i], p, len) == 0)
+							break;
+					}
+
+					if(i == 12)
+						return NULL;
+
+					pTM->tm_mon = i;
+					p += len;
+					break;
+				}
+
+				case 'c':   // Replaced by the locale's appropriate date and time representation.
+				{
+					#if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
+						if(!ParseDate(bAlt, p, pTM, pTimeLocale))
+							return NULL;
+
+						while(Isspace(*p))
+							++p;
+
+						if(!ParseTime(bAlt, p, pTM, pTimeLocale))
+							return NULL;
+					#else
+						char buffer[256]; buffer[0] = 0;
+						const char* pFormatTemp = pTimeLocale->mDateTimeFormat;
+
+						if(bAlt)
+							pFormatTemp = ConvertFormatSpecifiersToAlternates(buffer, sizeof buffer, pFormatTemp);
+						
+						if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
+							return NULL;                  
+					#endif
+
+					break;
+				}
+
+				case 'C':   // The century number [00,99]; leading zeros are permitted but not required.
+					if(!ReadInt(p, i, 0, 99))
+						return NULL;
+
+					if(bSplitYear)
+						pTM->tm_year = (pTM->tm_year % 100) + (i * 100);
+					else
+					{
+						pTM->tm_year = i * 100;
+						bSplitYear   = true;
+					}
+					break;
+
+				case 'd':   // The day of the month [01,31]; leading zeros are permitted but not required.
+				case 'e':   // Equivalent to %d. 
+					if(!ReadInt(p, pTM->tm_mday, 1, 31))
+						return NULL;
+					break;
+
+				case 'D':   // The date as %m / %d / %y.
+					if((p = Strptime(p, "%m/%d/%y", pTM)) == NULL)
+						return NULL;
+					break;
+
+				case 'H':   // The hour (24-hour clock) [00,23]; leading zeros are permitted but not required.
+					if(!ReadInt(p, pTM->tm_hour, 0, 23))
+						return NULL;
+					break;
+
+				case 'I':   // The hour (12-hour clock) [01,12]; leading zeros are permitted but not required.
+					if(!ReadInt(p, pTM->tm_hour, 1, 12))
+						return NULL;
+					break;
+
+				case 'j':   // The day number of the year [001,366]; leading zeros are permitted but not required.
+					if(!ReadInt(p, i, 1, 366))
+						return NULL;
+					pTM->tm_yday = i - 1;
+					break;
+
+				case 'M':   // The minute [00,59]; leading zeros are permitted but not required.
+					if(!ReadInt(p, pTM->tm_min, 0, 59))
+						return NULL;
+					break;
+
+				case 'm':   // The month number [01,12]; leading zeros are permitted but not required.
+					if(!ReadInt(p, i, 1, 12))
+						return NULL;
+					pTM->tm_mon = i - 1;
+					break;
+
+				case 'n':   // Any white space.
+				case 't':   // Any white space.
+					while(Isspace(*p))
+						p++;
+					break;
+
+				case 'p':   // The locale's equivalent of a.m or p.m.
+				{
+					size_t lenAM = Strlen(pTimeLocale->mAmPm[0]);
+					size_t lenPM = Strlen(pTimeLocale->mAmPm[1]);
+					if(Strnicmp(pTimeLocale->mAmPm[0], p, lenAM) == 0) // Test AM
+					{
+						if (pTM->tm_hour == 12)
+							pTM->tm_hour = 0;
+
+						p += lenAM;
+						break;
+					}
+					else if(Strnicmp(pTimeLocale->mAmPm[1], p, lenPM) == 0) // Test PM
+					{
+						if(pTM->tm_hour <= 11)
+							pTM->tm_hour += 12;
+
+						if (pTM->tm_hour > 23)
+							return NULL;
+
+						p += lenPM;
+						break;
+					}
+
+					return NULL;
+				}
+
+				case 'r':   // 12-hour clock time using the AM/PM notation if t_fmt_ampm is not an empty string in the LC_TIME portion of the current locale; in the POSIX locale, this shall be equivalent to %I : %M : %S %p.
+					if((p = Strptime(p, "%I:%M:%S %p", pTM)) == NULL)
+						return NULL;
+					break;
+
+				case 'R':   // The time as %H : %M.
+					if((p = Strptime(p, "%H:%M", pTM)) == NULL)
+						return NULL;
+					break;
+
+				case 'S':   // The seconds [00,60]; leading zeros are permitted but not required.
+					if(!ReadInt(p, pTM->tm_sec, 0, 61))
+						return NULL;
+					break;
+
+				case 'T':   // The time as %H : %M : %S.
+					if((p = Strptime(p, "%H:%M:%S", pTM)) == NULL)
+						return NULL;
+					break;
+
+				case 'U':   // The week number of the year (Sunday as the first day of the week) as a decimal number [00,53]; leading zeros are permitted but not required.
+				case 'W':   // The week number of the year (Monday as the first day of the week) as a decimal number [00,53]; leading zeros are permitted but not required.
+					// It's hard to calculate this, as we need the rest of the information 
+					// to implement it right. Or we could possibly delay calculation of it.
+				{
+					//Unsupported as of yet
+					break;
+				}
+
+				case 'w':   // The weekday as a decimal number [0,6], with 0 representing Sunday; leading zeros are permitted but not required.
+					if(!ReadInt(p, pTM->tm_wday, 0, 6))
+						return NULL;
+					break;
+
+				case 'x':   // The date, using the locale's date format.
+				{
+					if (!ParseDate(bAlt, p, pTM, pTimeLocale))
+						return NULL;
+					break;
+				}
+
+				case 'X':   // The time, using the locale's time format.
+				{
+					if (!ParseTime(bAlt, p, pTM, pTimeLocale))
+						return NULL;
+					break;
+				}
+				case 'Y':   // The year, including the century (for example, 1988).
+					if(!ReadInt(p, pTM->tm_year, 0, 9999))
+						return NULL;
+					//Set the year according to the 1900epoch
+					pTM->tm_year -= 1900;
+					break;
+
+				case 'y':   // The year within century. When a century is not otherwise specified, values in the range [69,99] shall refer to years 1969 to 1999 inclusive, and values in the range [00,68] shall refer to years 2000 to 2068 inclusive; leading zeros shall be permitted but shall not be required.
+					if(!ReadInt(p, i, 0, 99))
+						return NULL;
+
+					if(bSplitYear)
+					{
+						pTM->tm_year = ((pTM->tm_year / 100) * 100) + i;
+						break;
+					}
+
+					bSplitYear = true;
+
+					if(i <= 68)
+						pTM->tm_year = (2000 - 1900) + i;
+					else
+						pTM->tm_year = i;
+					break;
+
+				default:
+					EA_FAIL_M("EAStdC Strptime"); // Unsupported format specifier. Just print it as-is.
+					return NULL;
+			}
+		}
+
+		return (char*)p;
+
+	} // Strptime
+
+
+	#undef EADT_COUNT_LEAP_YEARS
+	#undef SUNDAY_BASED_WEEK_NUMBER
+	#undef MONDAY_BASED_WEEK_NUMBER
+
+} // namespace StdC
+} // namespace EA
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+
+

+ 547 - 0
source/EAFixedPoint.cpp

@@ -0,0 +1,547 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// See the header file for documentation and example usage.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAFixedPoint.h>
+#include <EAAssert/eaassert.h>
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedMul
+//
+// returns a*b
+//
+#ifdef FP_USE_INTEL_ASM
+	EASTDC_API __declspec(naked)
+	int32_t __stdcall SFixed16::FixedMul(const int32_t /*a*/, const int32_t /*b*/)
+	{ 
+		__asm{                   // VC++ compiler will choke if you put ";" comments with a templated inline assembly function.
+			mov  eax, [esp+4]    // Move a into eax
+			mov  ecx, [esp+8]    // Move b into ecx
+			imul ecx             // Multiply by b, storing the result in edx:eax
+			shrd eax, edx, 16    // Do shift and leave result in eax. 
+			ret  8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedMul(const int32_t a, const int32_t b)
+	{
+		const int64_t c = (int64_t)a * b;
+		return (int32_t)(c >> 16);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedMul
+//
+// returns a*b
+//
+#ifdef FP_USE_INTEL_ASM
+	EASTDC_API __declspec(naked)
+	uint32_t __stdcall UFixed16::FixedMul(const uint32_t /*a*/, const uint32_t /*b*/)
+	{ 
+		__asm{                  // VC++ compiler will choke if you put ";" comments with a templated inline assembly function.
+			mov  eax, [esp+4]   // Move a into eax
+			mov  ecx, [esp+8]   // Move b into ecx
+			mul  ecx            // Multiply by b, storing the result in edx:eax
+			shrd eax, edx, 16   // Do shift and leave result in eax. 
+			ret  8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedMul(const uint32_t a, const uint32_t b)
+	{
+		const uint64_t c = (uint64_t)a * b;
+		return (uint32_t)(c >> 16);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedDiv
+//
+// returns a/b
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t FP_PASCAL SFixed16::FixedDiv(const int32_t /*a*/, const int32_t /*b*/)
+	{ 
+		__asm{
+			mov   eax, [esp+4]  // Prepare the number for division
+			rol   eax, 16       // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax       // Put the integer part in edx
+			xor   ax,  ax       // Clear the integer part from eax 
+			mov   ecx, [esp+8]  // Move b into ecx
+			idiv  ecx           // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedDiv(const int32_t a, const int32_t b)
+	{
+		const int64_t c = ((uint64_t)a) << 16;
+		return (int32_t)(c / b);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedDiv
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedDiv(const uint32_t /*a*/, const uint32_t /*b*/)
+	{ 
+		__asm{
+			mov   eax, [esp+4]  // Prepare the number for division
+			rol   eax, 16       // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax       // Put the integer part in edx
+			xor   ax,  ax       // Clear the integer part from eax 
+			mov   ecx, [esp+8]  // Move b into ecx
+			div   ecx           // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedDiv(const uint32_t a, const uint32_t b)
+	{
+		const uint64_t c = ((uint64_t)a) << 16;
+		return (uint32_t)(c / b);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedDivSafe
+//
+// returns a/b. Checks for overflow and returns max value if so, instead of bombing.
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t __stdcall SFixed16::FixedDivSafe(const int32_t /*a*/, const int32_t /*b*/)
+	{ 
+		__asm{
+			mov   ecx, [esp+8]      // Move b into ecx.
+			or    ecx, ecx          // Test to see if equal to zero.
+			je    NOT_OK            // if not OK
+			mov   eax, [esp+4]      // Put a into eax.
+			xor   edx, edx          // Clear out edx, since we'll be shifting into it.
+			shld  edx,eax,16        // double fixed
+			cmp   edx, ecx          // do a test for overflow.
+			jge   NOT_OK            // If edx is greater than ecx, then we will overflow on the divide. 
+			shl   eax,16            // double fixed
+			idiv  ecx               // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			ret   8
+			NOT_OK:
+			mov   eax, 0x7FFFFFFF   // put in max value
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedDivSafe(const int32_t a, const int32_t b)
+	{
+		if(b)
+		{
+			const int64_t c = ((int64_t)a) << 16;
+			return (int32_t)(c / b);
+		}
+		return INT32_MAX;
+	}
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedDivSafe
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedDivSafe(const uint32_t /*a*/, const uint32_t /*b*/)
+	{ 
+		__asm{
+			mov   ecx, [esp+8]  // Move b into ecx.
+			or    ecx, ecx      // Test to see if equal to zero.
+			je    NOT_OK        // if not OK
+			mov   eax, [esp+4]  // Put a into eax.
+			xor   edx, edx      // Clear out edx, since we'll be shifting into it.
+			shld  edx,eax,16    // double fixed
+			cmp   edx, ecx      // do a test for overflow.
+			jae   NOT_OK        // If edx is greater than ecx, then we will overflow on the divide. 
+			shl   eax,16        // double fixed
+			div   ecx           // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			ret   8             // return
+			NOT_OK:
+			mov   eax, 0xFFFFFFFF // put in max value
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL //Intel C++
+			return 0; //Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedDivSafe(const uint32_t a, const uint32_t b)
+	{
+		if(b)
+		{
+			const uint64_t c = ((uint64_t)a) << 16;
+			return (uint32_t)(c / b);
+		}
+		return UINT32_MAX;
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedMulDiv
+//
+// returns a*b/c Faster than separate mul and div.
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t __stdcall SFixed16::FixedMulDiv(const int32_t /*a*/, const int32_t /*b*/, const int32_t /*c*/)
+	{
+		__asm{
+			mov  eax, [esp+4]  // First set up the multiply. Put a into eax.
+			mov  ecx, [esp+8]  // Put b into ecx.
+			imul ecx           // Multiply. The result is in edx:eax.
+			mov  ecx, [esp+12] // Put the denominator into ecx.
+			idiv ecx           // Divide, leaving the result in eax and remainder in edx.
+			ret  12
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedMulDiv(const int32_t /*a*/, const int32_t /*b*/, const int32_t /*c*/)
+	{
+		// EA_ASSERT(false); // This is not yet finished.
+		return 0;
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedMulDiv
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedMulDiv(const uint32_t /*a*/, const uint32_t /*b*/, const uint32_t /*c*/)
+	{
+		__asm{
+			mov  eax, [esp+4]  // First set up the multiply. Put a into eax.
+			mov  ecx, [esp+8]  // Put b into ecx.
+			mul  ecx           // Multiply. The result is in edx:eax.
+			mov  ecx, [esp+12] // Put the denominator into ecx.
+			div  ecx           // Divide, leaving the result in eax and remainder in edx.
+			ret  12
+		}
+		#ifdef EA_COMPILER_INTEL //Intel C++
+			return 0; //Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedMulDiv(const uint32_t /*a*/, const uint32_t /*b*/, const uint32_t /*c*/)
+	{
+		// EA_ASSERT(false); // This is not yet finished.
+		return 0;
+	}
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedMulDivSafe
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t __stdcall SFixed16::FixedMulDivSafe(const int32_t /*a*/, const int32_t /*b*/, const int32_t /*c*/)
+	{
+		__asm{
+			mov  ecx, [esp+12]  // Move b into ecx.
+			or   ecx, ecx       // Test to see if equal to zero.
+			je   NOT_OK         // if not OK
+			mov  eax, [esp+4]   // 
+			mov  ebx, [esp+8]   // 
+			imul ebx            // 
+			cmp  edx, ecx       // do a test for overflow.
+			jge  NOT_OK         // If edx is greater than ecx, then we will overflow on the divide. 
+			idiv ecx            // Result is in eax.
+			ret  12
+			NOT_OK:
+			mov  eax, 0x7FFFFFFF // put in max value
+			ret  12
+		}
+		#ifdef EA_COMPILER_INTEL //Intel C++
+			return 0; //Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedMulDivSafe(const int32_t /*a*/, const int32_t /*b*/, const int32_t /*c*/)
+	{
+		// EA_ASSERT(false); // This is not yet finished.
+		return 0;
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedMulDivSafe
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedMulDivSafe(const uint32_t /*a*/, const uint32_t /*b*/, const uint32_t /*c*/)
+	{
+		__asm{
+			mov  ecx, [esp+12]    // Move b into ecx.
+			or   ecx, ecx         // Test to see if equal to zero.
+			je   NOT_OK           // if not OK
+			mov  eax, [esp+4]     // 
+			mov  ebx, [esp+8]     // 
+			mul  ebx              // 
+			cmp  edx, ecx         // do a test for overflow.
+			jae  NOT_OK           // If edx is greater than ecx, then we will overflow on the divide. 
+			div  ecx              // Result is in eax.
+			ret  12
+			NOT_OK:
+			mov  eax, 0xFFFFFFFF  // put in max value
+			ret  12
+		}
+		#ifdef EA_COMPILER_INTEL //Intel C++
+			return 0; //Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedMulDivSafe(const uint32_t /*a*/, const uint32_t /*b*/, const uint32_t /*c*/)
+	{
+		// EA_ASSERT(false); // This is not yet finished.
+		return 0;
+	}
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedMod
+//
+// returns a%b
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t __stdcall SFixed16::FixedMod(const int32_t /*a*/, const int32_t /*b*/)
+	{ 
+		__asm{
+			mov   eax, [esp+4]      // Prepare the number for division
+			rol   eax, 16           // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax           // Put the integer part in edx
+			xor   ax, ax            // Clear the integer part from eax 
+			mov   ecx, [esp+8]      // Move b into ecx
+			idiv  ecx               // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			mov   eax, edx          // The remainder after idiv is in edx. So we move it to eax before the return.
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedMod(const int32_t a, const int32_t b)
+	{
+		const uint64_t c = ((uint64_t)a) << 16;
+		return (int32_t)(c % b);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedMod
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedMod(const uint32_t /*a*/, const uint32_t /*b*/)
+	{ 
+		__asm{
+			mov   eax, [esp+4]      // Prepare the number for division
+			rol   eax, 16           // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax           // Put the integer part in edx
+			xor   ax,  ax           // Clear the integer part from eax 
+			mov   ecx, [esp+8]      // Move b into ecx
+			div   ecx               // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			mov   eax, edx          // The remainder after div is in edx. So we move it to eax before the return.
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedMod(const uint32_t a, const uint32_t b)
+	{
+		const uint64_t c = ((uint64_t)a) << 16;
+		return (uint32_t)(c % b);
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SFixed16::FixedModSafe
+//
+// returns a%b
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	int32_t __stdcall SFixed16::FixedModSafe(const int32_t /*a*/, const int32_t /*b*/)
+	{ 
+		__asm{
+			mov   ecx, [esp+8]  // Move b into ecx
+			or    ecx, ecx      // Test to see if equal to zero.
+			je    NOT_OK        // if not OK
+			mov   eax, [esp+4]  // Prepare the number for division
+			rol   eax, 16       // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax       // Put the integer part in edx
+			xor   ax,  ax       // Clear the integer part from eax 
+			cmp   edx, ecx      // do a test for overflow.
+			jge   NOT_OK        // If edx is greater than ecx, then we will overflow on the divide. 
+			div   ecx           // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			mov   eax, edx      // The remainder after idiv is in edx. So we move it to eax before the return.
+			ret   8
+			NOT_OK:
+			mov   eax, 0xFFFFFFFF // put in max value
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	int32_t FP_PASCAL SFixed16::FixedModSafe(const int32_t a, const int32_t b)
+	{
+		if(b)
+		{
+			const uint64_t c = ((uint64_t)a) << 16;
+			return (int32_t)(c % b);
+		}
+		return INT32_MAX;
+	}
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UFixed16::FixedModSafe
+//
+#ifdef FP_USE_INTEL_ASM
+	__declspec(naked)
+	uint32_t __stdcall UFixed16::FixedModSafe(const uint32_t /*a*/, const uint32_t /*b*/)
+	{ 
+		__asm{
+			mov   ecx, [esp+8]      // Move b into ecx
+			or    ecx, ecx          // Test to see if equal to zero.
+			je    NOT_OK            // if not OK
+			mov   eax, [esp+4]      // Prepare the number for division
+			rol   eax, 16           // Put the fractional part of the number in the top half of eax, rotating the top part to the bottom part for later use
+			movsx edx, ax           // Put the integer part in edx
+			xor   ax,  ax           // Clear the integer part from eax 
+			cmp   edx, ecx          // do a test for overflow.
+			jae   NOT_OK            // If edx is greater than ecx, then we will overflow on the divide. 
+			div   ecx               // Divide and store. Divide the signed 64 bit value in edx:eax by the 32 bit value in ecx.
+			mov   eax, edx          // The remainder after div is in edx. So we move it to eax before the return.
+			ret   8
+			NOT_OK:
+			mov   eax, 0xFFFFFFFF   // put in max value
+			ret   8
+		}
+		#ifdef EA_COMPILER_INTEL // Intel C++
+			return 0; // Just to get the compiler to shut up.
+		#endif
+	}
+#else
+	template<> EASTDC_API
+	uint32_t FP_PASCAL UFixed16::FixedModSafe(const uint32_t a, const uint32_t b)
+	{
+		if(b)
+		{
+			const uint64_t c = ((uint64_t)a) << 16;
+			return (uint32_t)(c % b);
+		}
+		return UINT32_MAX;
+	}
+#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+// For unity build friendliness, undef all local #defines.
+#undef FP_USE_INTEL_ASM
+
+
+
+
+
+
+
+
+

+ 910 - 0
source/EAGlobal.cpp

@@ -0,0 +1,910 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// OS globals are process-wide globals and are shared between an EXE and
+// DLLs. The OS global system works at the operating system level and has
+// auto-discovery logic so that no pointers or init calls need to be made
+// between modules for them to link their OS global systems together.
+//
+// Note that the interface to OS globals is a bit convoluted because the
+// core system needs to be thread-safe, cross-module, and independent of
+// app-level allocators. For objects for which order of initialization is
+// clearer, EASingleton is probably a better choice.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAGlobal.h>
+#include <EAStdC/internal/Thread.h>
+#include <EAStdC/EASprintf.h>
+#include <stdlib.h>
+#include <new>
+#include <EAAssert/eaassert.h>
+
+// Until we can test other implementations of Linux, we enable Linux only for regular desktop x86 Linux.
+#if (defined(EA_PLATFORM_LINUX) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) && !defined(EA_PLATFORM_ANDROID)) // What other unix-like platforms can do this?
+	#include <semaphore.h>
+	#include <fcntl.h>
+	#include <sys/mman.h>
+	#include <stdlib.h>
+	#include <unistd.h>
+
+	#define EASTDC_EAGLOBAL_UNIX 1
+#else
+	#define EASTDC_EAGLOBAL_UNIX 0
+#endif
+
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#pragma warning(push, 0)
+	#include <Windows.h>
+	#pragma warning(pop)
+#endif
+
+
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#pragma warning(push)
+	#pragma warning(disable: 4355)          // warning C4355: 'this' : used in base member initializer list
+
+	#ifndef EASTDC_INIT_SEG_DEFINED
+		#define EASTDC_INIT_SEG_DEFINED
+		// Set initialization order between init_seg(compiler) (.CRT$XCC) and
+		// init_seg(lib) (.CRT$XCL). The MSVC linker sorts the .CRT sections
+		// alphabetically so we simply need to pick a name that is between
+		// XCC and XCL. This works on both Windows and XBox.
+		#pragma warning(disable: 4075) // "initializers put in unrecognized initialization area"
+		#pragma init_seg(".CRT$XCF")
+	#endif
+#endif
+
+
+#if EASTDC_GLOBALPTR_SUPPORT_ENABLED
+
+namespace
+{
+	struct OSGlobalManager 
+	{
+		typedef EA::StdC::intrusive_list<EA::StdC::OSGlobalNode> OSGlobalList;
+
+		OSGlobalList      mOSGlobalList;
+		uint32_t          mRefCount;     //< Atomic reference count so that the allocator persists as long as the last module that needs it.
+		EA::StdC::Mutex   mcsLock;
+
+		OSGlobalManager();
+		OSGlobalManager(const OSGlobalManager&);
+
+		OSGlobalManager& operator=(const OSGlobalManager&);
+
+		void Lock() {
+			mcsLock.Lock();
+		}
+
+		void Unlock() {
+			mcsLock.Unlock();
+		}
+
+		EA::StdC::OSGlobalNode* Find(uint32_t id);
+		void                    Add(EA::StdC::OSGlobalNode* p);
+		void                    Remove(EA::StdC::OSGlobalNode* p);
+	};
+
+	OSGlobalManager::OSGlobalManager() {
+		EA::StdC::AtomicSet(&mRefCount, 0);
+	}
+
+	EA::StdC::OSGlobalNode* OSGlobalManager::Find(uint32_t id) {
+		OSGlobalList::iterator it(mOSGlobalList.begin()), itEnd(mOSGlobalList.end());
+
+		for(; it!=itEnd; ++it) {
+			EA::StdC::OSGlobalNode& node = *it;
+
+			if (node.mOSGlobalID == id)
+				return &node;
+		}
+
+		return NULL;
+	}
+
+	void OSGlobalManager::Add(EA::StdC::OSGlobalNode *p) {
+		mOSGlobalList.push_front(*p);
+	}
+
+	void OSGlobalManager::Remove(EA::StdC::OSGlobalNode *p) {
+		OSGlobalList::iterator it = mOSGlobalList.locate(*p);
+		mOSGlobalList.erase(it);
+	}
+
+	OSGlobalManager* gpOSGlobalManager = NULL;
+	uint32_t         gOSGlobalRefs     = 0;
+
+} // namespace
+
+
+
+#if defined(EA_PLATFORM_MICROSOFT)
+
+	namespace {
+
+		#define EA_GLOBAL_UNIQUE_NAME_FORMAT    "SingleMgrMutex%08x"
+		#define EA_GLOBAL_UNIQUE_NAME_FORMAT_W L"SingleMgrMutex%08x"
+
+		// For the Microsoft desktop API (e.g. Win32, Win64) we can use Get/SetEnvironmentVariable to 
+		// read/write a process-global variable. But other Microsoft APIs (e.g. XBox 360) don't support
+		// this and we resort to using a semaphore to store pointer bits.
+		#if !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) && (EA_PLATFORM_PTR_SIZE == 4)
+			HANDLE ghOSGlobalManagerPtrSemaphore;
+		#elif !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)  && (EA_PLATFORM_PTR_SIZE == 8)
+			HANDLE ghOSGlobalManagerPtrSemaphoreHi = NULL;
+			HANDLE ghOSGlobalManagerPtrSemaphoreLo = NULL;
+		#endif
+
+		bool             InitOSGlobalSystem();
+		void             ShutdownOSGlobalSystem();
+		OSGlobalManager* CreateOSGlobalManager();
+		void             DestroyOSGlobalManager(OSGlobalManager* pOSGlobalManager);
+
+
+		OSGlobalManager* CreateOSGlobalManager()
+		{
+			// Allocate the OSGlobal manager in the heap. We use the heap so that it can
+			// hop between DLLs if the EXE itself doesn't use the manager. Note that this
+			// must be the operating system heap and not an app-level heap (i.e. PPMalloc).
+			// We store the pointer to the originally allocated memory at p[-1], because we
+			// may have moved it during alignment.
+			const size_t kAlignment = 16;
+
+			void* p = HeapAlloc(GetProcessHeap(), 0, sizeof(OSGlobalManager) + kAlignment - 1 + sizeof(void *));
+			void* pAligned = (void *)(((uintptr_t)p + sizeof(void *) + kAlignment - 1) & ~(kAlignment-1));
+			((void**)pAligned)[-1] = p;
+
+			// Placement-new the global manager into the new memory.
+			return new(pAligned) OSGlobalManager;
+		}
+
+
+		void DestroyOSGlobalManager(OSGlobalManager* pOSGlobalManager)
+		{
+			if(pOSGlobalManager)
+			{
+				gpOSGlobalManager->~OSGlobalManager();
+				HeapFree(GetProcessHeap(), 0, ((void**)gpOSGlobalManager)[-1]);
+			}
+		}
+
+
+		bool InitOSGlobalSystem()
+		{
+			// The following check is not thread-safe. On most platforms this isn't an 
+			// issue in practice because this function is called on application startup
+			// and other threads won't be active. The primary concern is if the 
+			// memory changes that result below are visible to other processors later.
+			if (!gpOSGlobalManager)
+			{
+				// We create a named (process-global) mutex. Other threads or modules within 
+				// this process share this same underlying mutex. 
+				#if   !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+					// The kernel object namespace is global on Win32 so we have to choose a unique name.
+					wchar_t uniqueName[64];
+
+					EA::StdC::Sprintf(uniqueName, EA_GLOBAL_UNIQUE_NAME_FORMAT_W, (unsigned)GetCurrentProcessId());
+					HANDLE hMutex = CreateMutexExW(NULL, uniqueName, CREATE_MUTEX_INITIAL_OWNER, SYNCHRONIZE);
+				#else
+					// The kernel object namespace is global on Win32 so we have to choose a unique name.
+					char uniqueName[64];
+
+					EA::StdC::Sprintf(uniqueName, EA_GLOBAL_UNIQUE_NAME_FORMAT, (unsigned)GetCurrentProcessId());
+					HANDLE hMutex = CreateMutexA(NULL, FALSE, uniqueName);
+				#endif
+
+				if (!hMutex)
+					return false;
+
+				if (WAIT_FAILED != WaitForSingleObjectEx(hMutex, INFINITE, FALSE))
+				{
+					// If we don't have access to GetEnvironmentVariable and are a 32 bit platform...
+					#if !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) && (EA_PLATFORM_PTR_SIZE == 4)
+						// Xenon does not have memory mapping so we can't use the same technique
+						// as for Win32, and lacks GetModuleHandle() so the old "get global proc
+						// from main executable" trick won't work. What we do here is create
+						// a semaphore whose value is the pointer to gpOSGlobalManager. Semaphores
+						// can't hold negative values so we shift the pointer down by 4 bits
+						// before storing it (it has 16 byte alignment so this is OK). Also,
+						// there is no way to read a semaphore without modifying it so a mutex
+						// is needed around the operation.
+
+						wchar_t uniqueSemaphoreName[32];
+						EA::StdC::Sprintf(uniqueSemaphoreName, L"SingleMgr%u", (unsigned)GetCurrentProcessId());
+						ghOSGlobalManagerPtrSemaphore = CreateSemaphoreExW(NULL, 0, 0x7FFFFFFF, uniqueSemaphoreName, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE);
+
+						if (ghOSGlobalManagerPtrSemaphore)
+						{
+							const bool bSemaphoreExists = (GetLastError() == ERROR_ALREADY_EXISTS);
+
+							if (bSemaphoreExists) // If somebody within our process already created it..
+							{
+								LONG ptrValue;
+
+								// Read the semaphore value.
+								if (ReleaseSemaphore(ghOSGlobalManagerPtrSemaphore, 1, &ptrValue)) {
+									// Undo the +1 we added to the semaphore.
+									WaitForSingleObjectEx(ghOSGlobalManagerPtrSemaphore, INFINITE, FALSE);
+
+									// Recover the allocator pointer from the semaphore's original value.
+									gpOSGlobalManager = (OSGlobalManager *)((uintptr_t)ptrValue << 4);
+								}
+								else
+									EA_FAIL();
+							} 
+							else
+							{
+								gpOSGlobalManager = CreateOSGlobalManager();
+
+								// Set the semaphore to the pointer value. It was created with
+								// zero as the initial value so we add the desired value here.
+								ReleaseSemaphore(ghOSGlobalManagerPtrSemaphore, (LONG)((uintptr_t)gpOSGlobalManager >> 4), NULL);
+							}
+						}
+						else
+						{
+							// We have a system failure we have no way of handling.
+							EA_FAIL();
+						}
+
+					// If we don't have access to GetEnvironmentVariable and are a 64 bit platform...
+					#elif !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) && (EA_PLATFORM_PTR_SIZE == 8)
+
+						// Semaphore counts are limited to 31 bits (LONG_MAX), but we need to store a 64 bit pointer
+						// in those bits. But 64 bit pointers are always 64 bit aligned, so we need only 61 bits
+						// to store a 64 bit pointer. So we store the upper 31 bits in one semaphore and the lower
+						// 30 bits in another semaphore. Take the resulting 61 bits and shift left by 3 to get the 
+						// full 64 bit pointer.
+						// We use CreateSemaphoreExW instead of CreateSemaphoreW because the latter isn't always
+						// present in the non-desktop API.
+
+						// The kernel object namespace is session-local (not the same as app-local) so we have to choose a unique name.
+						wchar_t uniqueNameHi[64];
+						wchar_t uniqueNameLo[64];
+						DWORD   dwProcessId = GetCurrentProcessId();
+						EA::StdC::Sprintf(uniqueNameHi, L"SingleMgrHi%u", dwProcessId);
+						EA::StdC::Sprintf(uniqueNameLo, L"SingleMgrLo%u", dwProcessId);
+
+						// Create the high semaphore with a max of 31 bits of storage (0x7FFFFFFF).
+						ghOSGlobalManagerPtrSemaphoreHi = CreateSemaphoreExW(NULL, 0, 0x7FFFFFFF, uniqueNameHi, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE);
+
+						if(ghOSGlobalManagerPtrSemaphoreHi)
+						{
+							const bool bSemaphoreExists = (GetLastError() == ERROR_ALREADY_EXISTS);
+							LONG ptrValueHi;
+							LONG ptrValueLo;
+
+							if(bSemaphoreExists) // If somebody within our process already created it..
+							{
+								// Read the semaphore value.
+								if(ReleaseSemaphore(ghOSGlobalManagerPtrSemaphoreHi, 1, &ptrValueHi))
+								{
+									WaitForSingleObjectEx(ghOSGlobalManagerPtrSemaphoreHi, INFINITE, FALSE);     // Undo the +1 we added with ReleaseSemaphore.
+
+									// We still need to create our ghOSGlobalManagerPtrSemaphoreLo, which should also already exist, 
+									// since some other module in this process has already exected this function. Create it with a 
+									// max of 30 bits of storage (0x3FFFFFFF).
+									EA_ASSERT(ghOSGlobalManagerPtrSemaphoreLo == NULL);
+									ghOSGlobalManagerPtrSemaphoreLo = CreateSemaphoreExW(NULL, 0, 0x3FFFFFFF, uniqueNameLo, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE);
+									EA_ASSERT(GetLastError() == ERROR_ALREADY_EXISTS);
+
+									ReleaseSemaphore(ghOSGlobalManagerPtrSemaphoreLo, 1, &ptrValueLo);
+									WaitForSingleObjectEx(ghOSGlobalManagerPtrSemaphoreLo, INFINITE, FALSE);     // Undo the +1 we added with ReleaseSemaphore.
+
+									// Recover the allocator pointer from the semaphore's original value.
+									uintptr_t ptr = (((uintptr_t)ptrValueHi) << 30 | ptrValueLo) << 3; // Combine the pair into 61 bits, and shift left by 3. This is the reverse of the code below.
+									gpOSGlobalManager = (OSGlobalManager*)ptr;
+								}
+								else
+									EA_FAIL(); // In practice this cannot happen unless the machine is cripped beyond repair.
+							}
+							else // Else our CreateSemaphorExW call was the first one to create ghOSGlobalManagerPtrSemaphoreHi.
+							{
+								gpOSGlobalManager = CreateOSGlobalManager();
+								EA_ASSERT(gpOSGlobalManager && (((uintptr_t)gpOSGlobalManager & 7) == 0)); // All pointers on 64 bit platforms should have their lower 3 bits unused.
+
+								// Set the semaphore to the pointer value. It was created with
+								// zero as the initial value so we add the desired value here.
+								uintptr_t ptr = (uintptr_t)gpOSGlobalManager >> 3; // ptr now has the 61 significant bits of gpOSGlobalManager.
+
+								ptrValueHi = static_cast<LONG>(ptr >> 30);         // ptrValueHi has the upper 31 of the 61 bits.
+								ptrValueLo = static_cast<LONG>(ptr & 0x3FFFFFFF);  // ptrValueLo has the lower 30 of the 61 bits.
+
+								// We still need to create our ghOSGlobalManagerPtrSemaphoreLo, which should also not already exist.
+								// Create it with a max of 30 bits of storage (0x3FFFFFFF).
+								EA_ASSERT(ghOSGlobalManagerPtrSemaphoreLo == NULL);
+								ghOSGlobalManagerPtrSemaphoreLo = CreateSemaphoreExW(NULL, 0, 0x3FFFFFFF, uniqueNameLo, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE);
+								EA_ASSERT(GetLastError() != ERROR_ALREADY_EXISTS);
+
+								EA_ASSERT((ghOSGlobalManagerPtrSemaphoreHi != NULL) && (ghOSGlobalManagerPtrSemaphoreLo != NULL)); // Should always be true, due to the logic in this function.
+								ReleaseSemaphore(ghOSGlobalManagerPtrSemaphoreHi, ptrValueHi, NULL);
+								ReleaseSemaphore(ghOSGlobalManagerPtrSemaphoreLo, ptrValueLo, NULL);
+
+								// Now semaphoreHi has the upper 31 significant bits of the gpOSGlobalManager pointer value.
+								// and semaphoreLo has the lower 30 significant bits of the gpOSGlobalManager pointer value.
+							}
+						}
+						else
+						{
+							// We have a system failure we have no way of handling.
+							EA_FAIL();
+						}
+
+					#else
+
+						// Under Win32 and Win64, we use system environment variables to store the gpOSGlobalManager value.
+						char stringPtr[32];
+						const DWORD dwResult = GetEnvironmentVariableA(uniqueName, stringPtr, 32);
+
+						if((dwResult > 0) && dwResult < 32 && stringPtr[0]) // If the variable was found...
+							gpOSGlobalManager = (OSGlobalManager *)(uintptr_t)_strtoui64(stringPtr, NULL, 16); // _strtoui64 is a VC++ extension function.
+						else
+						{
+							// GetLastError() should be ERROR_ENVVAR_NOT_FOUND. But what do we do if it isn't?
+							gpOSGlobalManager = CreateOSGlobalManager();
+
+							EA::StdC::Sprintf(stringPtr, "%I64x", (uint64_t)(uintptr_t)gpOSGlobalManager);
+							SetEnvironmentVariableA(uniqueName, stringPtr); // There's not much we can do if this call fails.
+						}
+
+					#endif
+
+					EA_ASSERT(gpOSGlobalManager && (gpOSGlobalManager->mRefCount < UINT32_MAX));
+					EA::StdC::AtomicIncrement(&gpOSGlobalManager->mRefCount);
+
+					BOOL result = ReleaseMutex(hMutex);
+					EA_ASSERT(result); EA_UNUSED(result);
+				}
+
+				BOOL result = CloseHandle(hMutex);
+				EA_ASSERT(result); EA_UNUSED(result);
+
+				if (!gpOSGlobalManager)
+				{
+					ShutdownOSGlobalSystem();
+					return false;
+				}
+
+				EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+				EA::StdC::AtomicIncrement(&gOSGlobalRefs);  // Increment it once for the init of this system (InitOSGlobalSystem). This increment will be matched by a decrement in ShutdownOSGlobalSystem.
+			}
+
+			return true;
+		}
+
+		void ShutdownOSGlobalSystem()
+		{
+			if (EA::StdC::AtomicDecrement(&gOSGlobalRefs) == 0) // If the (atomic) integer decrement results in a refcount of zero...
+			{
+				if (gpOSGlobalManager)
+				{
+					if (EA::StdC::AtomicDecrement(&gpOSGlobalManager->mRefCount) == 0)
+						DestroyOSGlobalManager(gpOSGlobalManager);
+
+					gpOSGlobalManager = NULL;
+				}
+
+				#if !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) && (EA_PLATFORM_PTR_SIZE == 4)
+					if (ghOSGlobalManagerPtrSemaphore)
+					{
+						CloseHandle(ghOSGlobalManagerPtrSemaphore);
+						ghOSGlobalManagerPtrSemaphore = NULL;
+					}
+				#elif !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) && (EA_PLATFORM_PTR_SIZE == 8)
+					if (ghOSGlobalManagerPtrSemaphoreHi)
+					{
+						CloseHandle(ghOSGlobalManagerPtrSemaphoreHi);
+						ghOSGlobalManagerPtrSemaphoreHi = NULL;
+					}
+
+					if (ghOSGlobalManagerPtrSemaphoreLo)
+					{
+						CloseHandle(ghOSGlobalManagerPtrSemaphoreLo);
+						ghOSGlobalManagerPtrSemaphoreLo = NULL;
+					}
+				#else
+					// Clear the gpOSGlobalManager environment variable.
+					// This code needs to be called in a thread-safe way by the user, usually by calling it once on shutdown. 
+					// We have a problem if this function is executing at the same time some other entity in this process 
+					// is currently doing some new use of the OS global system, as that can cause an instance of gpOSGlobalManager
+					// to be created while we are in the process of destroying it.
+					char uniqueName[64]; uniqueName[0] = 0;
+
+					EA::StdC::Sprintf(uniqueName, EA_GLOBAL_UNIQUE_NAME_FORMAT, (unsigned)GetCurrentProcessId());
+					SetEnvironmentVariableA(uniqueName, NULL);
+				#endif
+			}
+		}
+
+	} // namespace
+
+#elif defined(EA_PLATFORM_SONY)
+
+	// On this platform we use sceKernelReserveVirtualRange with a specific predetermined address, and place our
+	// OSGlobalManager there. There's a bit of careful code below to deal with possible snafus while doing this,
+	// and even with that this code still has some caveats about its usage, as described below. Most of the time
+	// those caveats won't come into play, as they are relevant only for very unusual application usage patterns
+	// and can still be worked around if those patterns happen to come into play.
+ 
+    #include <sys/dmem.h>
+	#include <kernel.h>
+	#include <string.h>
+	#include <sceerror.h>
+	#include <eathread/eathread_sync.h>
+	#include <EAStdC/EAStopwatch.h>
+
+
+	struct OSGlobalManagerContainer
+	{
+		uint8_t         mMagicNumber[16];  // This is a unique value which guarantees that this is the address of the OSGlobalManager.
+		OSGlobalManager mOSGlobalManager;
+	};
+
+	const size_t   kMemSize = 16384;
+	const uint64_t kAddr64             = SCE_KERNEL_APP_MAP_AREA_END_ADDR - kMemSize;    // End of appication memory space. https://ps4.scedev.net/resources/documents/SDK/3.000/Kernel-Overview/0004.html
+	const uint8_t  kMagic[16]          = { 0xD1, 0x4B, 0x78, 0x49, 0x81, 0xF1, 0x45, 0x0D, 0x91, 0x08, 0x1B, 0xA8, 0xE7, 0x8A, 0xD1, 0xD3 }; // Random bytes.
+	const size_t   kDirectMemoryLength = 16 * 1024; // 16 KB minimum for sceKernelAllocateDirectMemory
+	bool gbKeepDirectMemory            = false;
+	off_t gDirectMemoryStartAddr           = 0;
+
+	bool InitOSGlobalSystem()
+	{
+		// The code below involves a number of careful steps to implement sharing a memory address between DLLs.
+		// We have this code because this platform provides no other means for sharing a global piece of memory
+		// between modules. On other platforms (e.g. Unix) there are environment variables we can use, and on 
+		// others (e.g. Windows) there are uniquely named synchronization primitives we can use. On still others
+		// there are writable semaphore disk files that can be used. On this platform there is no environment variable
+		// support, synchronization primitives have names but are not unique like on Windows, and disk files do 
+		// exist but only if you tag your application manifest to support the /download0 file mount. 
+
+		if(!gpOSGlobalManager)
+		{
+			uint64_t currentAddr = kAddr64;
+			
+			int32_t err = sceKernelAllocateDirectMemory(
+					0,
+					SCE_KERNEL_MAIN_DMEM_SIZE,
+					kDirectMemoryLength,
+					0,
+					SCE_KERNEL_WB_ONION,
+					&gDirectMemoryStartAddr);
+			EA_ASSERT(err == SCE_OK);
+			
+			if(err != SCE_OK)
+			{
+				return false;
+			}
+					
+			gbKeepDirectMemory = false;
+
+			while (!gpOSGlobalManager && (currentAddr >= (kAddr64 - EA::StdC::kKettleOSGlobalSearchSpace)))
+			{
+				void*                     addr   = reinterpret_cast<void*>(currentAddr);
+				OSGlobalManagerContainer* pOSGlobalManagerContainer = static_cast<OSGlobalManagerContainer*>(addr);
+				int                       result = SCE_OK; // Disabled because it doesn't work how we need it to: = sceKernelReserveVirtualRange(&addr, kMemSize, SCE_KERNEL_MAP_FIXED | SCE_KERNEL_MAP_NO_OVERWRITE, 0);
+				// Possible return values are SCE_OK, SCE_KERNEL_ERROR_EINVAL, and SCE_KERNEL_ERROR_ENOMEM.
+
+				if((result == SCE_KERNEL_ERROR_ENOMEM) || (result == SCE_OK))
+				{
+					// SCE_KERNEL_ERROR_ENOMEM occurs when the address has already been reserved, which may have been 
+					// done by a previous EAGlobal init occurred. With either that or SCE_OK, we call scekernelMapDirectMemory.
+					// We call scekernelMapDirectMemory even if we get SCE_KERNEL_ERROR_ENOMEM because another thread may 
+					// have called sceKernelReserveVirtualRange first, but we may be executing simultaneously and execute
+					// sceKernelMapDirectMemory first. 
+
+					result = sceKernelMapNamedDirectMemory(&addr, kMemSize, SCE_KERNEL_PROT_CPU_READ | SCE_KERNEL_PROT_CPU_WRITE, 
+														SCE_KERNEL_MAP_FIXED | SCE_KERNEL_MAP_NO_OVERWRITE, gDirectMemoryStartAddr, 0, "EAOSGlobal");
+					// Possible return values are SCE_OK, SCE_KERNEL_ERROR_EACCES, SCE_KERNEL_ERROR_EINVAL, and SCE_KERNEL_ERROR_ENOMEM.
+					// Normally we expect that SCE_KERNEL_ERROR_ENOMEM or SCE_OK will be returned. If the sceKernelReserveVirtualRange
+					// returned SCE_OK then usually sceKernalMapDirectMemory will return SCE_OK for us, because if we were the first
+					// to call the reserve function then we will probably be the first to call the map function.
+
+					if((result == SCE_KERNEL_ERROR_ENOMEM) || (result == SCE_OK))
+					{
+						// At this point the memory at addr is mapped to our address space and we can attempt to read and write it.
+						// To make sure this memory really is read/write for us, we query the kernel for its protection type.
+						bool weWereHereFirst = (result == SCE_OK); // IF we were here first then we take care of initializing the pOSGlobalManagerContainer->mOSGlobalManager instance.
+
+						if(weWereHereFirst)
+						{
+							// We use some low level synchronization primitives here to accomplish memory synchronization. We are unable
+							// to use a mutex to do this because there is no way to share a mutex anonymously between modules. The whole
+							// reason EAGlobal exists is to allow sharing variables anonymously between modules. So we have a chicken and
+							// egg problem: we can't share a mutex between modules until InitOSGlobalSystem has completed.
+
+							::new(&pOSGlobalManagerContainer->mOSGlobalManager) OSGlobalManager;
+							EA::StdC::AtomicIncrement(&pOSGlobalManagerContainer->mOSGlobalManager.mRefCount);
+							gpOSGlobalManager = &pOSGlobalManagerContainer->mOSGlobalManager;
+							EAWriteBarrier(); // Make sure this is seen as written before the memcpy is seen as written.
+							EACompilerMemoryBarrier(); // Don't let the compiler move the above code to after the below code.
+
+							memcpy(pOSGlobalManagerContainer->mMagicNumber, kMagic, sizeof(pOSGlobalManagerContainer->mMagicNumber));
+							EAWriteBarrier(); // Make sure other threads see this write.
+
+							gbKeepDirectMemory = true;
+						}
+						else // Else somebody before us mapped this memory. We need to validate it before trying to use it.
+						{
+							// We have a problem in that as we execute this code, another thread might have just started executing the 
+							// the weWereHere = true pathway above. So the magic value might not have been written yet. We don't currently
+							// have an easy means to deal with this other than to loop for N milliseconds while waiting for the other
+							// thread to complete. 
+							// Another potential problem can occur if two threads of differing priorities execute on the same CPU and 
+							// one blocks the other from completing this code. That would be a 
+
+							int protection = 0;
+							result = sceKernelQueryMemoryProtection(addr, NULL, NULL, &protection);
+
+							if((result == SCE_OK) && (protection & SCE_KERNEL_PROT_CPU_READ) && (protection & SCE_KERNEL_PROT_CPU_WRITE))
+							{
+								EA::StdC::LimitStopwatch limitStopwatch(EA::StdC::Stopwatch::kUnitsMilliseconds, 500, true);
+
+								while(!limitStopwatch.IsTimeUp())
+								{
+									EAReadBarrier();  // Make sure we see the previous writes of other threads prior to the read below.
+
+									if(memcmp(kMagic, pOSGlobalManagerContainer->mMagicNumber, sizeof(pOSGlobalManagerContainer->mMagicNumber)) == 0) // If it's our memory...
+									{
+										// At this point we were able to create a new shared memory area or acquire the shared memory area that
+										// some other InitOSGlobalSystem function call previously created.
+										EA::StdC::AtomicIncrement(&pOSGlobalManagerContainer->mOSGlobalManager.mRefCount);
+										gpOSGlobalManager = &pOSGlobalManagerContainer->mOSGlobalManager;
+										EAWriteBarrier(); // This isn't strictly needed, but it can theoretically make things go faster for other threads.
+
+										break;
+									}
+									// Else either the other thread is still initializing pOSGlobalManagerContainer or some other completely
+									// unrelated entity is using this memory for something else. We don't have a good means of detecting 
+									// the latter other than timing out. Maybe if sceKernelMapDirectMemory guarantees writing 0 to the memory
+									// then we can exit this loop much faster.
+
+									// There must be another thread executing the weWereHereFirst = true pathway. We sleep so that thread 
+									// can complete that pathway. Possibly are are a higher priority thread which could be blocking it.
+									SceKernelTimespec ts = { 0, 2000000 };
+									sceKernelNanosleep(&ts, NULL);
+								}
+							}
+						}
+					}
+				}
+
+				// Try another address. The logic above has determined that the address was used by some other entity. 
+				// That may well indicate that we need to pick a new starting address to try, as we really want this to 
+				// always succeed the first time through. Note that this bumping up is safe and works as intended, because
+				// if one module fails to work at the original address, all will (assuming that during startup some thread
+				// doesn't map it before we try to get it and then later unmap it before the other modules that use this have loaded).
+				currentAddr -= (1024 * 1024);
+
+			} // while ... 
+
+			if(!gbKeepDirectMemory)
+			{
+				sceKernelReleaseDirectMemory(gDirectMemoryStartAddr, kDirectMemoryLength);
+				gDirectMemoryStartAddr = 0;
+			}
+		} // if(!gpOSGlobalManager)
+
+		if(gpOSGlobalManager) // (We have an AddRef on gpOSGlobalManager from above, so gpOSGlobalManager can't possibly have become invalid at this point)
+		{
+			// gOSGlobalRefs measures the number of times InitOSGlobalSystem/ShutdowOSGlobalSystem was successfully called.
+			EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+			EA::StdC::AtomicIncrement(&gOSGlobalRefs);  // Increment it once for the init of this system (InitOSGlobalSystem). This increment will be matched by a decrement in ShutdownOSGlobalSystem.
+
+			return true;
+		}
+
+		return false;
+	}
+
+
+	void ShutdownOSGlobalSystem()
+	{
+		// gOSGlobalRefs measures the number of times InitOSGlobalSystem/ShutdowOSGlobalSystem was successfully called.
+		if(EA::StdC::AtomicDecrement(&gOSGlobalRefs) == 0) // If the (atomic) integer decrement results in a refcount of zero...
+		{ 
+			if(gpOSGlobalManager)
+			{
+				if(EA::StdC::AtomicDecrement(&gpOSGlobalManager->mRefCount) == 0) // mRefCount measures the use count of glOSGlobalManager.
+				{
+					// To consider: we can unmap the memory at gpOSGlobalManager - 16 bytes here. It doesn't buy us anything 
+					// aside from possibly making some tools see that we freed the mapped kernel memory.
+				}
+				if(gbKeepDirectMemory)
+				{
+					sceKernelReleaseDirectMemory(gDirectMemoryStartAddr, kDirectMemoryLength);
+					gDirectMemoryStartAddr = 0;
+				}
+				gpOSGlobalManager = NULL;
+			}
+		}
+	}
+
+#elif EASTDC_EAGLOBAL_UNIX
+
+	namespace {
+
+		#define EA_GLOBAL_UNIQUE_NAME_FORMAT "/SingleMgrMutex%llu"
+
+		OSGlobalManager* CreateOSGlobalManager();
+		bool             InitOSGlobalSystem();
+		void             ShutdownOSGlobalSystem();
+		
+
+		OSGlobalManager* CreateOSGlobalManager()
+		{
+			// Allocate the OSGlobal manager in shared memory. 
+			#if defined(__APPLE__)
+				void* pMemory = mmap(NULL, sizeof(OSGlobalManager), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,      -1, 0);
+			#else
+				void* pMemory = mmap(NULL, sizeof(OSGlobalManager), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+			#endif
+
+			if(pMemory) // Some Unix variants (e.g. mobile) can fail this call due to lack of support for it.
+			{
+				EA_ASSERT(((uintptr_t)pMemory & 15) == 0); // Make sure mmap returns at least 16 byte alignment.
+ 
+				// Placement-new the global manager into the new memory.
+				return new(pMemory) OSGlobalManager;
+			}
+
+			return NULL;
+		}
+
+
+		void DestroyOSGlobalManager(OSGlobalManager* pOSGlobalManager)
+		{
+			if(pOSGlobalManager)
+			{
+				gpOSGlobalManager->~OSGlobalManager();
+				munmap(pOSGlobalManager, sizeof(OSGlobalManager));
+			}
+		}
+
+
+		bool InitOSGlobalSystem()
+		{
+			// The following check is not thread-safe. On most platforms this isn't an 
+			// issue in practice because this function is called on application startup
+			// and other threads won't be active. The primary concern is if the 
+			// memory changes that result below are visible to other processors later.
+
+			if(!gpOSGlobalManager)
+			{
+				// We make a process-unique name based on the process id.
+				char  uniqueName[96];
+				pid_t processID = getpid();
+
+				EA::StdC::Sprintf(uniqueName, EA_GLOBAL_UNIQUE_NAME_FORMAT, (unsigned long long)processID);
+				sem_t* mutex = sem_open(uniqueName, O_CREAT, 0644, 1); // Unix has named semaphores but doesn't really have named mutexes, so we use a semaphore as a mutex.
+
+				if(mutex == SEM_FAILED)
+					return false;
+
+				if(sem_wait(mutex) == 0) // If locking the mutex was successful...
+				{
+					// As of this writing, we are using getenv/setenv to write a shared variable pointer. It turns out that this
+					// is not a good idea, because getenv/setenv is not thread-safe. getenv returns a pointer to static memory
+					// which another thread (who isn't using our mutex) might call setenv in a way that changes that memory.
+					// The opinion of the Linux people is that you just shouldn't ever call setenv during application runtime.
+					// A better solution for us is to use shared mapped memory (shm_open(), mmap()): http://www.ibm.com/developerworks/aix/library/au-spunix_sharedmemory/index.html
+
+					const char* pName = getenv(uniqueName);
+
+					if(pName && pName[0]) // If the variable was found...
+						gpOSGlobalManager = (OSGlobalManager*)(uintptr_t)strtoull(pName, NULL, 16);
+					else
+					{
+						gpOSGlobalManager = CreateOSGlobalManager();
+
+						if(gpOSGlobalManager)
+						{
+							char buffer[32];
+							EA::StdC::Sprintf(buffer, "%I64x", (uint64_t)(uintptr_t)gpOSGlobalManager);
+							/*int result =*/ setenv(uniqueName, buffer, 1);
+						}
+					}
+
+					if(gpOSGlobalManager)
+					{
+						EA_ASSERT(gpOSGlobalManager->mRefCount < UINT32_MAX);
+						EA::StdC::AtomicIncrement(&gpOSGlobalManager->mRefCount);
+					}
+
+					sem_post(mutex);
+					sem_close(mutex);
+					sem_unlink(uniqueName);
+				}
+
+				if(!gpOSGlobalManager)
+				{
+					ShutdownOSGlobalSystem();
+					return false;
+				}
+
+				EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+				EA::StdC::AtomicIncrement(&gOSGlobalRefs);  // Increment it once for the init of this system (InitOSGlobalSystem). This increment will be matched by a decrement in ShutdownOSGlobalSystem.
+			}
+
+			return true;
+		}
+
+		void ShutdownOSGlobalSystem()
+		{
+			if(EA::StdC::AtomicDecrement(&gOSGlobalRefs) == 0) // If the (atomic) integer decrement results in a refcount of zero...
+			{ 
+				if(gpOSGlobalManager)
+				{
+					if(EA::StdC::AtomicDecrement(&gpOSGlobalManager->mRefCount) == 0)
+						DestroyOSGlobalManager(gpOSGlobalManager);
+
+					gpOSGlobalManager = NULL;
+				}
+
+				// Clear the gpOSGlobalManager environment variable.
+				// This code needs to be called in a thread-safe way by the user, usually by calling it once on shutdown. 
+				// We have a problem if this function is executing at the same time some other entity in this process 
+				// is currently doing some new use of the OS global system, as that can cause an instance of gpOSGlobalManager
+				// to be created while we are in the process of destroying it.
+				char  uniqueName[96]; uniqueName[0] = 0;
+				pid_t processID = getpid();
+
+				EA::StdC::Sprintf(uniqueName, EA_GLOBAL_UNIQUE_NAME_FORMAT, (unsigned long long)processID);
+				 /*int result =*/ unsetenv(uniqueName);
+			}
+		}
+
+	} // namespace
+
+#else // #if defined(EA_PLATFORM_MICROSOFT)
+
+	namespace {
+
+		static uint64_t sOSGlobalMgrMemory[(sizeof(OSGlobalManager) + 1) / sizeof(uint64_t)];
+		
+		bool InitOSGlobalSystem()
+		{
+			// Theoretical problem: If you keep calling this function, eventually gOSGlobalRefs will overflow.
+			EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+			if(EA::StdC::AtomicIncrement(&gOSGlobalRefs) == 1)
+				gpOSGlobalManager = new(sOSGlobalMgrMemory) OSGlobalManager;
+			return true;
+		}
+
+		void ShutdownOSGlobalSystem()
+		{
+			if(EA::StdC::AtomicDecrement(&gOSGlobalRefs) == 0) // If the (atomic) integer decrement results in a refcount of zero...
+				gpOSGlobalManager = NULL;
+		}
+	}
+
+#endif // #if defined(EA_PLATFORM_MICROSOFT)
+
+
+
+
+EASTDC_API EA::StdC::OSGlobalNode* EA::StdC::GetOSGlobal(uint32_t id, OSGlobalFactoryPtr pFactory)
+{
+	// Initialize up the OSGlobal system if we are getting called before
+	// static init, i.e. allocator
+	if (!InitOSGlobalSystem())
+		return NULL;
+
+	gpOSGlobalManager->Lock();
+
+	EA::StdC::OSGlobalNode* p = gpOSGlobalManager->Find(id);
+
+	if (!p && pFactory)
+	{
+		p = pFactory();
+		p->mOSGlobalID = id;
+		AtomicSet(&p->mOSGlobalRefCount, 0);
+		gpOSGlobalManager->Add(p);
+	}
+
+	if (p)
+	{
+		EA_ASSERT(p->mOSGlobalRefCount < UINT32_MAX);
+		AtomicIncrement(&p->mOSGlobalRefCount);
+
+		EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+		AtomicIncrement(&gOSGlobalRefs);
+	}
+
+	gpOSGlobalManager->Unlock();
+
+	return p;
+}
+
+
+EASTDC_API bool EA::StdC::SetOSGlobal(uint32_t id, EA::StdC::OSGlobalNode *p)
+{
+	// Initialize up the OSGlobal system if we are getting called before
+	// static init, i.e. allocator
+	if (!InitOSGlobalSystem())
+		return false;
+
+	gpOSGlobalManager->Lock();
+
+	EA::StdC::OSGlobalNode* const pTemp = gpOSGlobalManager->Find(id);
+
+	if (pTemp == NULL) // If there isn't one already...
+	{
+		p->mOSGlobalID = id;
+		AtomicSet(&p->mOSGlobalRefCount, 0);
+		gpOSGlobalManager->Add(p);
+
+		EA_ASSERT(p->mOSGlobalRefCount < UINT32_MAX);
+		AtomicIncrement(&p->mOSGlobalRefCount);
+
+		EA_ASSERT(gOSGlobalRefs < UINT32_MAX);
+		AtomicIncrement(&gOSGlobalRefs);
+	}
+	
+	gpOSGlobalManager->Unlock();
+
+	return (pTemp == NULL);
+}
+
+
+EASTDC_API bool EA::StdC::ReleaseOSGlobal(EA::StdC::OSGlobalNode *p)
+{
+	gpOSGlobalManager->Lock();
+
+	const bool shouldDestroyManager  = AtomicDecrement(&gOSGlobalRefs) == 0;
+	const bool shouldDestroyOSGlobal = AtomicDecrement(&p->mOSGlobalRefCount) == 0;
+
+	if (shouldDestroyOSGlobal)
+		gpOSGlobalManager->Remove(p);
+
+	gpOSGlobalManager->Unlock();
+
+	// Note by Paul Pedriana (10/2009): It seems to me that shouldDestroyManager will never 
+	// be true here because InitOSGlobalSystem will have been called at app startup and 
+	// its gOSGlobalRefs increment will still be live. So only when that last explicit 
+	// call to ShutdownOSGlobalSystem is called will gOSGlobalRefs go to zero.
+	if (shouldDestroyManager)
+		ShutdownOSGlobalSystem(); // This function decrements gOSGlobalRefs.
+
+	return shouldDestroyOSGlobal;
+}
+
+
+// Force the OSGlobal manager to be available for the life of the app.
+// It's OK if this comes up too late for some uses because GetOSGlobal()
+// will bring it online earlier in that case.
+namespace 
+{
+		struct AutoinitOSGlobalManager
+		{
+			AutoinitOSGlobalManager()
+			{
+				bool result = InitOSGlobalSystem();
+				EA_ASSERT(result); EA_UNUSED(result);
+			}
+
+			~AutoinitOSGlobalManager()
+			{
+				ShutdownOSGlobalSystem();
+			}
+		};
+
+		AutoinitOSGlobalManager gAutoinitOSGlobalManager;
+}
+
+
+#endif // EASTDC_GLOBALPTR_SUPPORT_ENABLED
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#pragma warning(pop) // symmetric for pop for the above -->   #pragma warning(disable: 4355)          // warning C4355: 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+

+ 401 - 0
source/EAHashCRC.cpp

@@ -0,0 +1,401 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAHashCRC.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CRC16 
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API uint16_t crc16Table[256] =
+{
+	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 
+	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 
+	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 
+	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 
+	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 
+	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 
+	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 
+	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 
+	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 
+	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 
+	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 
+	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 
+	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 
+	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 
+	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 
+	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 
+	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 
+	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 
+	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 
+	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 
+	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 
+	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 
+	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 
+	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 
+	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 
+	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 
+	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 
+	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 
+	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 
+	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 
+	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 
+	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 
+};
+
+
+EASTDC_API uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue, bool bFinalize)
+{
+	const uint8_t*       pData8    = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+	unsigned             tempValue = nInitialValue;
+
+	while(pData8 < pData8End)
+		tempValue = ((tempValue >> 8) ^ crc16Table[(tempValue ^ *pData8++) & 0xff]);
+
+	if(bFinalize)
+		tempValue = ~tempValue;
+
+	return (uint16_t)tempValue;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CRC24 
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API uint32_t crc24Table[256] = 
+{
+	0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334, 
+	0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5, 
+	0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016, 
+	0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987, 
+	0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570, 
+	0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1, 
+	0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652, 
+	0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3, 
+	0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407, 
+	0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96, 
+	0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725, 
+	0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4, 
+	0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243, 
+	0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2, 
+	0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161, 
+	0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0, 
+	0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9, 
+	0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78, 
+	0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb, 
+	0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a, 
+	0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad, 
+	0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c, 
+	0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f, 
+	0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e, 
+	0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da, 
+	0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b, 
+	0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8, 
+	0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69, 
+	0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e, 
+	0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f, 
+	0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc, 
+	0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d 
+};
+
+
+EASTDC_API uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue, bool bFinalize)
+{
+	const uint8_t*       pData8    = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+		nInitialValue = (nInitialValue >> 8) ^ crc24Table[(nInitialValue ^ *pData8++) & 0xff];
+
+	if(bFinalize)
+	{
+		nInitialValue = ~nInitialValue;
+		nInitialValue &= 0x00ffffff;
+	}
+
+	return nInitialValue;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CRC32
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API uint32_t crc32Table[256] =
+{
+	0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+	0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 
+	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 
+	0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 
+	0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 
+	0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 
+	0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 
+	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 
+	0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 
+	0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 
+	0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 
+	0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 
+	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 
+	0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 
+	0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 
+	0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 
+	0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 
+	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 
+	0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 
+	0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 
+	0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 
+	0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 
+	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 
+	0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 
+	0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 
+	0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 
+	0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 
+	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 
+	0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 
+	0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 
+	0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 
+	0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+
+EASTDC_API uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue, bool bFinalize)
+{
+	const uint8_t*       pData8    = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+		nInitialValue = (nInitialValue << 8) ^ crc32Table[(nInitialValue >> 24) ^ *pData8++];
+
+	if(bFinalize)
+		nInitialValue = ~nInitialValue;
+
+	return nInitialValue;
+}
+
+
+
+
+
+
+EASTDC_API uint32_t crc32TableReverse[256] =
+{
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
+	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
+	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
+	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
+	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
+	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
+	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
+	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
+	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
+	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
+	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
+	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
+	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
+	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
+	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
+	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
+	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
+	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
+	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
+	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
+	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
+	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
+	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
+	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
+	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
+	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+EASTDC_API uint32_t CRC32Reverse(const void* pData, size_t nLength, uint32_t nInitialValue, bool bFinalize)
+{
+	const uint8_t* pData8 = (const uint8_t*)pData;
+
+	while(nLength >= 8)
+	{
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+		nLength -= 8;
+	}
+
+	while(nLength--)
+		nInitialValue = crc32TableReverse[(nInitialValue ^ *pData8++) & 0xff] ^ (nInitialValue >> 8);
+
+	if(bFinalize)
+		nInitialValue = ~nInitialValue;
+
+	return nInitialValue;
+}
+
+
+
+
+
+EASTDC_API uint32_t CRC32Rwstdc(const void* pData, size_t nLength)
+{
+	unsigned result;
+	const uint8_t* d = static_cast<const uint8_t*>(pData);
+
+	if(nLength < 4)
+	{
+		result = *d;
+
+		while(nLength--)
+			result = ((result << 8) | *d++) ^ crc32Table[result & 0xff];
+
+		return result;
+	}
+
+	result  = static_cast<unsigned>(*d++ << 24);
+	result |= *d++ << 16;
+	result |= *d++ << 8;
+	result |= *d++;
+	result  = ~result;
+
+	nLength -= 4;
+
+	for(size_t i = 0; i < nLength; i++)
+		result = ((result << 8) | *d++) ^ crc32Table[result >> 24];
+
+	return (uint32_t)~result;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CRC64 
+///////////////////////////////////////////////////////////////////////////////
+
+// We follow the same polynomial convention as the ECMA-182 tape drive standard.
+EASTDC_API uint64_t crc64Table[256] =
+{
+	UINT64_C(0x0000000000000000), UINT64_C(0x42f0e1eba9ea3693), UINT64_C(0x85e1c3d753d46d26), UINT64_C(0xc711223cfa3e5bb5), 
+	UINT64_C(0x493366450e42ecdf), UINT64_C(0x0bc387aea7a8da4c), UINT64_C(0xccd2a5925d9681f9), UINT64_C(0x8e224479f47cb76a), 
+	UINT64_C(0x9266cc8a1c85d9be), UINT64_C(0xd0962d61b56fef2d), UINT64_C(0x17870f5d4f51b498), UINT64_C(0x5577eeb6e6bb820b), 
+	UINT64_C(0xdb55aacf12c73561), UINT64_C(0x99a54b24bb2d03f2), UINT64_C(0x5eb4691841135847), UINT64_C(0x1c4488f3e8f96ed4), 
+	UINT64_C(0x663d78ff90e185ef), UINT64_C(0x24cd9914390bb37c), UINT64_C(0xe3dcbb28c335e8c9), UINT64_C(0xa12c5ac36adfde5a), 
+	UINT64_C(0x2f0e1eba9ea36930), UINT64_C(0x6dfeff5137495fa3), UINT64_C(0xaaefdd6dcd770416), UINT64_C(0xe81f3c86649d3285), 
+	UINT64_C(0xf45bb4758c645c51), UINT64_C(0xb6ab559e258e6ac2), UINT64_C(0x71ba77a2dfb03177), UINT64_C(0x334a9649765a07e4), 
+	UINT64_C(0xbd68d2308226b08e), UINT64_C(0xff9833db2bcc861d), UINT64_C(0x388911e7d1f2dda8), UINT64_C(0x7a79f00c7818eb3b), 
+	UINT64_C(0xcc7af1ff21c30bde), UINT64_C(0x8e8a101488293d4d), UINT64_C(0x499b3228721766f8), UINT64_C(0x0b6bd3c3dbfd506b), 
+	UINT64_C(0x854997ba2f81e701), UINT64_C(0xc7b97651866bd192), UINT64_C(0x00a8546d7c558a27), UINT64_C(0x4258b586d5bfbcb4), 
+	UINT64_C(0x5e1c3d753d46d260), UINT64_C(0x1cecdc9e94ace4f3), UINT64_C(0xdbfdfea26e92bf46), UINT64_C(0x990d1f49c77889d5), 
+	UINT64_C(0x172f5b3033043ebf), UINT64_C(0x55dfbadb9aee082c), UINT64_C(0x92ce98e760d05399), UINT64_C(0xd03e790cc93a650a), 
+	UINT64_C(0xaa478900b1228e31), UINT64_C(0xe8b768eb18c8b8a2), UINT64_C(0x2fa64ad7e2f6e317), UINT64_C(0x6d56ab3c4b1cd584), 
+	UINT64_C(0xe374ef45bf6062ee), UINT64_C(0xa1840eae168a547d), UINT64_C(0x66952c92ecb40fc8), UINT64_C(0x2465cd79455e395b), 
+	UINT64_C(0x3821458aada7578f), UINT64_C(0x7ad1a461044d611c), UINT64_C(0xbdc0865dfe733aa9), UINT64_C(0xff3067b657990c3a), 
+	UINT64_C(0x711223cfa3e5bb50), UINT64_C(0x33e2c2240a0f8dc3), UINT64_C(0xf4f3e018f031d676), UINT64_C(0xb60301f359dbe0e5), 
+	UINT64_C(0xda050215ea6c212f), UINT64_C(0x98f5e3fe438617bc), UINT64_C(0x5fe4c1c2b9b84c09), UINT64_C(0x1d14202910527a9a), 
+	UINT64_C(0x93366450e42ecdf0), UINT64_C(0xd1c685bb4dc4fb63), UINT64_C(0x16d7a787b7faa0d6), UINT64_C(0x5427466c1e109645), 
+	UINT64_C(0x4863ce9ff6e9f891), UINT64_C(0x0a932f745f03ce02), UINT64_C(0xcd820d48a53d95b7), UINT64_C(0x8f72eca30cd7a324), 
+	UINT64_C(0x0150a8daf8ab144e), UINT64_C(0x43a04931514122dd), UINT64_C(0x84b16b0dab7f7968), UINT64_C(0xc6418ae602954ffb), 
+	UINT64_C(0xbc387aea7a8da4c0), UINT64_C(0xfec89b01d3679253), UINT64_C(0x39d9b93d2959c9e6), UINT64_C(0x7b2958d680b3ff75), 
+	UINT64_C(0xf50b1caf74cf481f), UINT64_C(0xb7fbfd44dd257e8c), UINT64_C(0x70eadf78271b2539), UINT64_C(0x321a3e938ef113aa), 
+	UINT64_C(0x2e5eb66066087d7e), UINT64_C(0x6cae578bcfe24bed), UINT64_C(0xabbf75b735dc1058), UINT64_C(0xe94f945c9c3626cb), 
+	UINT64_C(0x676dd025684a91a1), UINT64_C(0x259d31cec1a0a732), UINT64_C(0xe28c13f23b9efc87), UINT64_C(0xa07cf2199274ca14), 
+	UINT64_C(0x167ff3eacbaf2af1), UINT64_C(0x548f120162451c62), UINT64_C(0x939e303d987b47d7), UINT64_C(0xd16ed1d631917144), 
+	UINT64_C(0x5f4c95afc5edc62e), UINT64_C(0x1dbc74446c07f0bd), UINT64_C(0xdaad56789639ab08), UINT64_C(0x985db7933fd39d9b), 
+	UINT64_C(0x84193f60d72af34f), UINT64_C(0xc6e9de8b7ec0c5dc), UINT64_C(0x01f8fcb784fe9e69), UINT64_C(0x43081d5c2d14a8fa), 
+	UINT64_C(0xcd2a5925d9681f90), UINT64_C(0x8fdab8ce70822903), UINT64_C(0x48cb9af28abc72b6), UINT64_C(0x0a3b7b1923564425), 
+	UINT64_C(0x70428b155b4eaf1e), UINT64_C(0x32b26afef2a4998d), UINT64_C(0xf5a348c2089ac238), UINT64_C(0xb753a929a170f4ab), 
+	UINT64_C(0x3971ed50550c43c1), UINT64_C(0x7b810cbbfce67552), UINT64_C(0xbc902e8706d82ee7), UINT64_C(0xfe60cf6caf321874), 
+	UINT64_C(0xe224479f47cb76a0), UINT64_C(0xa0d4a674ee214033), UINT64_C(0x67c58448141f1b86), UINT64_C(0x253565a3bdf52d15), 
+	UINT64_C(0xab1721da49899a7f), UINT64_C(0xe9e7c031e063acec), UINT64_C(0x2ef6e20d1a5df759), UINT64_C(0x6c0603e6b3b7c1ca), 
+	UINT64_C(0xf6fae5c07d3274cd), UINT64_C(0xb40a042bd4d8425e), UINT64_C(0x731b26172ee619eb), UINT64_C(0x31ebc7fc870c2f78), 
+	UINT64_C(0xbfc9838573709812), UINT64_C(0xfd39626eda9aae81), UINT64_C(0x3a28405220a4f534), UINT64_C(0x78d8a1b9894ec3a7), 
+	UINT64_C(0x649c294a61b7ad73), UINT64_C(0x266cc8a1c85d9be0), UINT64_C(0xe17dea9d3263c055), UINT64_C(0xa38d0b769b89f6c6), 
+	UINT64_C(0x2daf4f0f6ff541ac), UINT64_C(0x6f5faee4c61f773f), UINT64_C(0xa84e8cd83c212c8a), UINT64_C(0xeabe6d3395cb1a19), 
+	UINT64_C(0x90c79d3fedd3f122), UINT64_C(0xd2377cd44439c7b1), UINT64_C(0x15265ee8be079c04), UINT64_C(0x57d6bf0317edaa97), 
+	UINT64_C(0xd9f4fb7ae3911dfd), UINT64_C(0x9b041a914a7b2b6e), UINT64_C(0x5c1538adb04570db), UINT64_C(0x1ee5d94619af4648), 
+	UINT64_C(0x02a151b5f156289c), UINT64_C(0x4051b05e58bc1e0f), UINT64_C(0x87409262a28245ba), UINT64_C(0xc5b073890b687329), 
+	UINT64_C(0x4b9237f0ff14c443), UINT64_C(0x0962d61b56fef2d0), UINT64_C(0xce73f427acc0a965), UINT64_C(0x8c8315cc052a9ff6), 
+	UINT64_C(0x3a80143f5cf17f13), UINT64_C(0x7870f5d4f51b4980), UINT64_C(0xbf61d7e80f251235), UINT64_C(0xfd913603a6cf24a6), 
+	UINT64_C(0x73b3727a52b393cc), UINT64_C(0x31439391fb59a55f), UINT64_C(0xf652b1ad0167feea), UINT64_C(0xb4a25046a88dc879), 
+	UINT64_C(0xa8e6d8b54074a6ad), UINT64_C(0xea16395ee99e903e), UINT64_C(0x2d071b6213a0cb8b), UINT64_C(0x6ff7fa89ba4afd18), 
+	UINT64_C(0xe1d5bef04e364a72), UINT64_C(0xa3255f1be7dc7ce1), UINT64_C(0x64347d271de22754), UINT64_C(0x26c49cccb40811c7), 
+	UINT64_C(0x5cbd6cc0cc10fafc), UINT64_C(0x1e4d8d2b65facc6f), UINT64_C(0xd95caf179fc497da), UINT64_C(0x9bac4efc362ea149), 
+	UINT64_C(0x158e0a85c2521623), UINT64_C(0x577eeb6e6bb820b0), UINT64_C(0x906fc95291867b05), UINT64_C(0xd29f28b9386c4d96), 
+	UINT64_C(0xcedba04ad0952342), UINT64_C(0x8c2b41a1797f15d1), UINT64_C(0x4b3a639d83414e64), UINT64_C(0x09ca82762aab78f7), 
+	UINT64_C(0x87e8c60fded7cf9d), UINT64_C(0xc51827e4773df90e), UINT64_C(0x020905d88d03a2bb), UINT64_C(0x40f9e43324e99428), 
+	UINT64_C(0x2cffe7d5975e55e2), UINT64_C(0x6e0f063e3eb46371), UINT64_C(0xa91e2402c48a38c4), UINT64_C(0xebeec5e96d600e57), 
+	UINT64_C(0x65cc8190991cb93d), UINT64_C(0x273c607b30f68fae), UINT64_C(0xe02d4247cac8d41b), UINT64_C(0xa2dda3ac6322e288), 
+	UINT64_C(0xbe992b5f8bdb8c5c), UINT64_C(0xfc69cab42231bacf), UINT64_C(0x3b78e888d80fe17a), UINT64_C(0x7988096371e5d7e9), 
+	UINT64_C(0xf7aa4d1a85996083), UINT64_C(0xb55aacf12c735610), UINT64_C(0x724b8ecdd64d0da5), UINT64_C(0x30bb6f267fa73b36), 
+	UINT64_C(0x4ac29f2a07bfd00d), UINT64_C(0x08327ec1ae55e69e), UINT64_C(0xcf235cfd546bbd2b), UINT64_C(0x8dd3bd16fd818bb8), 
+	UINT64_C(0x03f1f96f09fd3cd2), UINT64_C(0x41011884a0170a41), UINT64_C(0x86103ab85a2951f4), UINT64_C(0xc4e0db53f3c36767), 
+	UINT64_C(0xd8a453a01b3a09b3), UINT64_C(0x9a54b24bb2d03f20), UINT64_C(0x5d45907748ee6495), UINT64_C(0x1fb5719ce1045206), 
+	UINT64_C(0x919735e51578e56c), UINT64_C(0xd367d40ebc92d3ff), UINT64_C(0x1476f63246ac884a), UINT64_C(0x568617d9ef46bed9), 
+	UINT64_C(0xe085162ab69d5e3c), UINT64_C(0xa275f7c11f7768af), UINT64_C(0x6564d5fde549331a), UINT64_C(0x279434164ca30589), 
+	UINT64_C(0xa9b6706fb8dfb2e3), UINT64_C(0xeb46918411358470), UINT64_C(0x2c57b3b8eb0bdfc5), UINT64_C(0x6ea7525342e1e956), 
+	UINT64_C(0x72e3daa0aa188782), UINT64_C(0x30133b4b03f2b111), UINT64_C(0xf7021977f9cceaa4), UINT64_C(0xb5f2f89c5026dc37), 
+	UINT64_C(0x3bd0bce5a45a6b5d), UINT64_C(0x79205d0e0db05dce), UINT64_C(0xbe317f32f78e067b), UINT64_C(0xfcc19ed95e6430e8), 
+	UINT64_C(0x86b86ed5267cdbd3), UINT64_C(0xc4488f3e8f96ed40), UINT64_C(0x0359ad0275a8b6f5), UINT64_C(0x41a94ce9dc428066), 
+	UINT64_C(0xcf8b0890283e370c), UINT64_C(0x8d7be97b81d4019f), UINT64_C(0x4a6acb477bea5a2a), UINT64_C(0x089a2aacd2006cb9), 
+	UINT64_C(0x14dea25f3af9026d), UINT64_C(0x562e43b4931334fe), UINT64_C(0x913f6188692d6f4b), UINT64_C(0xd3cf8063c0c759d8), 
+	UINT64_C(0x5dedc41a34bbeeb2), UINT64_C(0x1f1d25f19d51d821), UINT64_C(0xd80c07cd676f8394), UINT64_C(0x9afce626ce85b507)
+};
+
+
+EASTDC_API uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue, bool bFinalize)
+{
+	const uint8_t*       pData8    = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+	{
+		const size_t nTableIndex = ((size_t)(nInitialValue >> 56) ^ *pData8++) & 0xff;
+		nInitialValue = crc64Table[nTableIndex] ^ (nInitialValue << 8);
+	}
+
+	if(bFinalize)
+		nInitialValue ^= UINT64_C(0xffffffffffffffff);
+
+	return nInitialValue;
+}
+
+
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+
+

+ 326 - 0
source/EAHashString.cpp

@@ -0,0 +1,326 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAHashString.h>
+#include <EAStdC/EACType.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DJB2
+//
+// This function is deprecated, as FNV1 has been shown to be superior.
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API uint32_t DJB2(const void* pData, size_t nLength, uint32_t nInitialValue)
+{
+	const uint8_t* pData8 = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+		nInitialValue = ((nInitialValue << 5) + nInitialValue) + *pData8++;
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint32_t DJB2_String8(const char8_t* pData8, uint32_t nInitialValue, CharCase charCase)
+{
+	uint32_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + Tolower((char8_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + Toupper((char8_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint32_t DJB2_String16(const char16_t* pData16, uint32_t nInitialValue, CharCase charCase)
+{
+	uint32_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + Tolower((char16_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = ((nInitialValue << 5) + nInitialValue) + Toupper((char16_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// FNV1
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API uint32_t FNV1(const void* pData, size_t nLength, uint32_t nInitialValue)
+{
+	const uint8_t* pData8 = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+		nInitialValue = (nInitialValue * 16777619) ^ *pData8++;
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint32_t FNV1_String8(const char8_t* pData8, uint32_t nInitialValue, CharCase charCase)
+{
+	uint32_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Tolower((char8_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Toupper((char8_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint32_t FNV1_String16(const char16_t* pData16, uint32_t nInitialValue, CharCase charCase)
+{
+	uint32_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Tolower((char16_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Toupper((char16_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+EASTDC_API uint32_t FNV1_String32(const char32_t* pData32, uint32_t nInitialValue, CharCase charCase)
+{
+	uint32_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Tolower((char32_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * 16777619) ^ Toupper((char32_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint64_t FNV64(const void* pData, size_t nLength, uint64_t nInitialValue)
+{
+	const uint8_t* pData8 = (const uint8_t*)pData;
+	const uint8_t* const pData8End = pData8 + nLength;
+
+	while(pData8 < pData8End)
+		nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ *pData8++;
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint64_t FNV64_String8(const char8_t* pData8, uint64_t nInitialValue, CharCase charCase)
+{
+	uint64_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Tolower((char8_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint8_t)*pData8++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Toupper((char8_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+EASTDC_API uint64_t FNV64_String16(const char16_t* pData16, uint64_t nInitialValue, CharCase charCase)
+{
+	uint64_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Tolower((char16_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint16_t)*pData16++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Toupper((char16_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+EASTDC_API uint64_t FNV64_String32(const char32_t* pData32, uint64_t nInitialValue, CharCase charCase)
+{
+	uint64_t c;
+
+	switch (charCase)
+	{
+		case kCharCaseAny:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ c;
+			break;
+		}
+
+		case kCharCaseLower:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Tolower((char32_t)c);
+			break;
+		}
+
+		case kCharCaseUpper:
+		{
+			while((c = (uint32_t)*pData32++) != 0)
+				nInitialValue = (nInitialValue * UINT64_C(1099511628211)) ^ Toupper((char32_t)c);
+			break;
+		}
+	}
+
+	return nInitialValue;
+}
+
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+

+ 1010 - 0
source/EAMemory.cpp

@@ -0,0 +1,1010 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAMemory.h>
+#include <EAAssert/eaassert.h>
+
+
+
+// In optimized non-debug builds, we inline various functions.
+// We don't inline these functions in debug builds because in debug builds they
+// contain diagnostic code that can't be exposed in headers because that would the 
+// user of this header to #include all the debug functionality headers, which isn't 
+// feasible.
+#if !EASTDC_MEMORY_INLINE_ENABLED
+	#include <EAStdC/internal/EAMemory.inl>
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+///////////////////////////////////////////////////////////////////////////
+// Deprecated functions
+//
+#if EASTDC_MEMCPY16_ENABLED
+	// This function is deprecated. It was mistakenly created during a code migration.
+	// It is scheduled for removal in a future version of this package.
+	EASTDC_API char16_t* Memcpy(char16_t* pDestination, const char16_t* pSource, size_t nCharCount)
+	{
+		return (char16_t*)memcpy(pDestination, pSource, nCharCount * sizeof(char16_t));
+	}
+#endif
+
+#if EASTDC_MEMCPY16_ENABLED
+	// This function is deprecated. It was mistakenly created during a code migration.
+	// It is scheduled for removal in a future version of this package.
+	EASTDC_API char16_t* Memmove(char16_t* pDestination, const char16_t* pSource, size_t nCharCount)
+	{
+		return (char16_t*)memmove(pDestination, pSource, nCharCount * sizeof(char16_t));
+	}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////
+// rwstdc compatibility
+// These functions implement the same named function and argument types
+// as the corresponding functions from the rwstdc package.
+EASTDC_API void MemFill16(void* pDestination, uint16_t c, unsigned int byteCount)
+{
+	Memfill16(pDestination, c, (size_t)byteCount);
+}
+
+EASTDC_API void MemFill32(void* pDestination, unsigned int c, unsigned int byteCount)
+{
+	Memfill32(pDestination, (uint32_t)c, (size_t)byteCount);
+}
+
+EASTDC_API void MemFillSpecific(void* pDestination, const void* pSource, unsigned int destByteCount, unsigned int sourceByteCount)
+{
+	MemfillSpecific(pDestination, pSource, (size_t)destByteCount, (size_t)sourceByteCount);
+}
+
+
+
+
+
+
+
+EASTDC_API uint16_t* Memset16(void* pDest, uint16_t c, size_t count)
+{
+	// Instead of casting between types, we just create a union.
+	union PointerUnion
+	{
+		void*     mpVoid;
+		uint16_t* mp16;
+		uint32_t* mp32;
+		uintptr_t mU;
+	};
+
+	PointerUnion p;
+	p.mpVoid = pDest;
+
+	EA_ASSERT((p.mU & 1) == 0);
+
+	const uint16_t* const pEnd = (p.mp16 + count);
+
+	if(count <= 32)                 // For small sizes, we simply do a little loop.
+	{
+		while(p.mp16 < pEnd)
+			*p.mp16++ = c;
+	}
+	else
+	{
+		if(p.mU & 3)                // If the address is not aligned on a 32 bit boundary.
+		{
+			*p.mp16++ = c;          // Align it on a 32 bit boundary.
+			count--;
+		}
+
+		// From here on we copy in 32 bit chunks for speed.
+		count /= 2;
+		const uint32_t c32 = (uint32_t)(c | (c << 16));
+
+		while(count--)
+			*p.mp32++ = c32;
+
+		if(p.mp16 < pEnd)
+			*p.mp16 = c;
+	}
+
+	return (uint16_t*)pDest;
+}
+
+
+EASTDC_API uint32_t* Memset32(void* pDest, uint32_t c, size_t count)
+{
+	EA_ASSERT(((uintptr_t)pDest & 3) == 0);
+
+	#if (EA_PLATFORM_WORD_SIZE >= 8) || (EA_PLATFORM_PTR_SIZE >= 8) // If we are using a 64 bit system...
+		const uint32_t* const pEnd = (uint32_t*)pDest+count;
+		uint32_t* pDest32 = (uint32_t*)pDest;
+		uint64_t  c64;
+
+		if(count <= 16)               // For small sizes, we simply do a little loop.
+		{
+			while(pDest32 < pEnd)
+				*pDest32++ = c;
+		}
+		else
+		{
+			if(((uintptr_t)pDest32) & 7)    // If the address is not aligned on a 64 bit boundary.
+			{
+				*pDest32++ = c;             // Align it on a 64 bit boundary.
+				count--;
+			}
+
+			uint64_t* pDest64 = (uint64_t*)pDest32; // From here on we copy in 64 bit chunks for speed.
+			count /= 2;
+			c64 = ((uint64_t)c | ((uint64_t)c << 32));
+
+			while(count)
+			{
+				*pDest64++ = c64;
+				count--;
+			}
+
+			if((uint32_t*)pDest64 < pEnd)
+				*((uint32_t*)pDest64) = (uint32_t)c64;
+		}
+	#else
+		uint32_t*             cur = (uint32_t*)pDest;
+		const uint32_t* const end = (uint32_t*)pDest + count;
+		while(cur < end)
+			*cur++ = c;
+	#endif
+
+	return (uint32_t*)pDest;
+}
+
+
+EASTDC_API uint64_t* Memset64(void* pDest, uint64_t c, size_t count)
+{
+	EA_ASSERT(((uintptr_t)pDest & 7) == 0);
+
+	uint64_t*             cur = (uint64_t*)pDest;
+	const uint64_t* const end = (uint64_t*)pDest + count;
+
+	while(cur < end)
+		*cur++ = c;
+
+	return (uint64_t*)pDest;
+}
+
+
+EASTDC_API void* MemsetN(void* pDestination, const void* pSource, size_t sourceBytes, size_t count)
+{
+	// This is a generic implementation. Pathways optimized for 24 bits and/or 128 bits might be desired.
+
+	uint8_t*       pDestination8 = (uint8_t*)pDestination;
+	const uint8_t* pSource8      = (const uint8_t*)pSource;
+	const uint8_t* pSource8Temp  = pSource8;
+
+	if(((sourceBytes & 3) == 0) && (((uintptr_t)pDestination & 3) == 0) && (((uintptr_t)pSource & 3) == 0))
+	{
+		// Pathway for 32-bit aligned copy
+		size_t i = 0;
+
+		while(count >= 4)
+		{
+			 pSource8Temp = pSource8;
+
+			 for(i = 0; (i < sourceBytes) && (count >= 4); i += 4, count -= 4)
+			 {
+				 *((uint32_t*)pDestination8) = *(const uint32_t*)(pSource8Temp);
+				 pDestination8 += 4;
+				 pSource8Temp  += 4;
+			 }
+		}
+
+		if(i == sourceBytes)
+			i = 0;
+
+		pSource8Temp = pSource8 + i;
+		while(count-- >= 1)
+			 *pDestination8++ = *pSource8Temp++;
+	}
+	else // ((sourceBytes & 3) != 0)
+	{
+		// Pathway for non 32-bit aligned copy
+		while(count >= 1)
+		{
+			 pSource8Temp = pSource8;
+
+			 for(size_t i = 0; (i < sourceBytes) && (count >= 1); i++, count--)
+				 *pDestination8++ = *pSource8Temp++;
+		}
+	}
+
+	return pDestination;
+}
+
+
+EASTDC_API const void* Memcheck8(const void* p, uint8_t c, size_t byteCount)
+{
+	for(const uint8_t* p8 = (const uint8_t*)p; byteCount > 0; ++p8, --byteCount)
+	{
+		if(*p8 != c)
+			return p8;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API const void* Memcheck16(const void* p, uint16_t c, size_t byteCount)
+{
+	union U16 {
+		uint16_t c16;
+		uint8_t  c8[2];
+	};
+	const U16 u = { c };
+	size_t    i = (size_t)((uintptr_t)p % 2);
+
+	for(const uint8_t* p8 = (const uint8_t*)p, *p8End = (const uint8_t*)p + byteCount; p8 != p8End; ++p8, i ^= 1)
+	{
+		if(*p8 != u.c8[i])
+			return p8;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API const void* Memcheck32(const void* p, uint32_t c, size_t byteCount)
+{
+	union U32 {
+		uint32_t c32;
+		uint8_t  c8[4];
+	};
+	const U32 u = { c };
+	size_t    i = (size_t)((uintptr_t)p % 4);
+
+	// This code could be a little faster if it could work with an aligned 
+	// destination and do word compares. There are some pitfalls to be careful
+	// of which may make the effort not worth it in practice for typical uses 
+	// of this code. In particular we need to make sure that word compares are 
+	// done with word-aligned memory, and that may mean using a version of 
+	// the c argument which has bytes rotated from their current position.
+	for(const uint8_t* p8 = (const uint8_t*)p, *p8End = (const uint8_t*)p + byteCount; p8 != p8End; ++p8, i = (i + 1) % 4)
+	{
+		if(*p8 != u.c8[i])
+			return p8;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API const void* Memcheck64(const void* p, uint64_t c, size_t byteCount)
+{
+	union U64 {
+		uint64_t c64;
+		uint8_t  c8[8];
+	};
+	const U64 u = { c };
+	size_t    i = (size_t)((uintptr_t)p % 8);
+
+	for(const uint8_t* p8 = (const uint8_t*)p, *p8End = (const uint8_t*)p + byteCount; p8 != p8End; ++p8, i = (i + 1) % 8)
+	{
+		if(*p8 != u.c8[i])
+			return p8;
+	}
+
+	return NULL;
+}
+
+
+
+
+EASTDC_API const char8_t* Memchr(const char8_t* p, char8_t c, size_t nCharCount)
+{
+	for(const char8_t* p8 = (const char8_t*)p; nCharCount > 0; ++p8, --nCharCount)
+	{
+		if(*p8 == c)
+			return p8;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API const char16_t* Memchr16(const char16_t* pString, char16_t c, size_t nCharCount)
+{
+	for(; nCharCount > 0; ++pString, --nCharCount)
+	{
+		if(*pString == c)
+			return pString;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API const char32_t* Memchr32(const char32_t* pString, char32_t c, size_t nCharCount)
+{
+	for(; nCharCount > 0; ++pString, --nCharCount)
+	{
+		if(*pString == c)
+			return pString;
+	}
+
+	return NULL;
+}
+
+
+#if EASTDC_MEMCHR16_ENABLED
+	EASTDC_API const char16_t* Memchr(const char16_t* pString, char16_t c, size_t nCharCount)
+	{
+		return Memchr16(pString, c, nCharCount);
+	}
+#endif
+
+
+EASTDC_API int Memcmp(const void* pString1, const void* pString2, size_t nCharCount)
+{
+	const char8_t* p1 = (const char8_t*)pString1;
+	const char8_t* p2 = (const char8_t*)pString2;
+
+	for(; nCharCount > 0; ++p1, ++p2, --nCharCount)
+	{
+		if(*p1 != *p2)
+			return (*p1 < *p2) ? -1 : 1;
+	}
+
+	return 0;
+}
+
+
+#if EASTDC_MEMCPY16_ENABLED
+	EASTDC_API int Memcmp(const char16_t* pString1, const char16_t* pString2, size_t nCharCount)
+	{
+		for(; nCharCount > 0; ++pString1, ++pString2, --nCharCount)
+		{
+			if(*pString1 != *pString2)
+				return (*pString1 < *pString2) ? -1 : 1;
+		}
+
+		return 0;
+	}
+#endif
+
+
+// Search for pFind/findSize within pMemory/memorySize.
+EASTDC_API void* Memmem(const void* pMemory, size_t memorySize, const void* pFind, size_t findSize)
+{
+	EA_ASSERT((pMemory || !memorySize) && (pFind || !findSize)); // Verify that if pMemory or pFind is NULL, their respective size must be 0.
+
+	const uint8_t* const pMemory8 = static_cast<const uint8_t*>(pMemory);
+	const uint8_t* const pFind8   = static_cast<const uint8_t*>(pFind);
+	const uint8_t* const pEnd8    = (pMemory8 + memorySize) - findSize;
+
+	if(memorySize && (findSize <= memorySize))
+	{
+		if(findSize) // An empty pFind results in success, return pMemory.
+		{
+			for(const uint8_t* pCurrent8 = pMemory8; pCurrent8 <= pEnd8; ++pCurrent8)       // This looping algorithm is not the fastest possible way to 
+			{                                                                               // implement this function. A faster, but much more complex, algorithm
+				if(EA_UNLIKELY(pCurrent8[0] == pFind8[0])) // Do a quick first char check.  // might involve a two-way memory search (http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260).
+				{                                                                           // Another algorithm might be to start by searching for words instead of bytes, then use Memcmp.
+					if(Memcmp(pCurrent8 + 1, pFind8 + 1, findSize - 1) == 0)
+						return const_cast<uint8_t*>(pCurrent8);
+				}
+			}
+		}
+		else
+			return const_cast<void*>(pMemory);
+	}
+
+	return NULL;
+}
+
+
+// This is a local function called by MemfillSpecific.
+static void Memfill24(void* pD, const void* pS, size_t byteCount)
+{
+	unsigned char* pDestination = static_cast<unsigned char*>(pD);
+	const unsigned char* pSource = static_cast<const unsigned char*>(pS);
+	// Optimization wise, this function will assume that pDestination is already aligned 32-bit
+	// Construct the 3 32-bit values
+	unsigned int val8a = *(static_cast<const unsigned char*>(pSource));
+	unsigned int val8b = *(static_cast<const unsigned char*>(pSource+1));
+	unsigned int val8c = *(static_cast<const unsigned char*>(pSource+2));
+	unsigned int val32a,val32b,val32c;
+
+	#if defined(EA_SYSTEM_BIG_ENDIAN)
+		val32a=(val8a*256*256*256)+(val8b*256*256)+(val8c*256)+val8a;
+		val32b=(val8b*256*256*256)+(val8c*256*256)+(val8a*256)+val8b;
+		val32c=(val8c*256*256*256)+(val8a*256*256)+(val8b*256)+val8c;
+	#else
+		val32a=val8a+(val8b*256)+(val8c*256*256)+(val8a*256*256*256);
+		val32b=val8b+(val8c*256)+(val8a*256*256)+(val8b*256*256*256);
+		val32c=val8c+(val8a*256)+(val8b*256*256)+(val8c*256*256*256);
+	#endif
+
+	// time to copy
+	// we have to align the address to 32-bit, otherwise it's going to crash on the ps2!
+	while (((reinterpret_cast<uintptr_t>(pDestination) & 0x03)!=0) && (byteCount>0))
+	{
+		byteCount--;
+		// rotate the values over
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			*(pDestination++)=static_cast<uint8_t>(val32a >> 24);
+			unsigned int tmp = val32a;
+			val32a=(val32a << 8) + (val32b >> 24);
+			val32b=(val32b << 8) + (val32c >> 24);
+			val32c=(val32c << 8) + (tmp >> 24);
+		#else
+			*(pDestination++)=static_cast<uint8_t>(val32a);
+			unsigned int tmp = val32a;
+			val32a=(val32a >> 8) + (val32b << 24);
+			val32b=(val32b >> 8) + (val32c << 24);
+			val32c=(val32c >> 8) + (tmp << 24);
+		#endif
+	}
+
+	while (byteCount >= 12)
+	{
+		*(reinterpret_cast<unsigned int*>(pDestination)) = val32a;
+		*(reinterpret_cast<unsigned int*>(pDestination+4)) = val32b;
+		*(reinterpret_cast<unsigned int*>(pDestination+8)) = val32c;
+		pDestination+=12;
+		byteCount-=12;
+	}
+	while (byteCount >= 4)
+	{
+		*(reinterpret_cast<unsigned int*>(pDestination)) = val32a;
+		pDestination+=4;
+		byteCount-=4;
+		val32a=val32b;
+		val32b=val32c;
+	}
+	while (byteCount >= 1)
+	{
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			*pDestination = static_cast<uint8_t>(val32a >> 24);
+			val32a = val32a << 8;
+		#else
+			*pDestination = static_cast<uint8_t>(val32a);
+			val32a = val32a >> 8;
+		#endif
+		pDestination++;
+		byteCount--;
+	}
+}
+
+// This is a local function called by MemfillSpecific.
+static void MemfillAny(void* pD, const void* pS, size_t destByteCount, size_t sourceByteCount)
+{
+	union Memory // Use a union to avoid memory aliasing problems in the compiler.
+	{
+		void*     mpVoid;
+		uint8_t*  mp8;
+		uint32_t* mp32;
+		uint32_t  m32;
+	};
+
+	Memory d;
+	d.mpVoid = pD;
+
+	Memory s;
+	s.mpVoid = const_cast<void*>(pS);
+
+	if (((sourceByteCount & 0x03) == 0) && ((d.m32 & 0x03) == 0) && ((s.m32 & 0x03) == 0))
+	{
+		// Routine for 32-bit aligned copy
+		size_t i = 0;
+		
+		while (destByteCount >= 4)
+		{
+			s.mpVoid = const_cast<void*>(pS);
+
+			for (i = 0; (i < sourceByteCount) && (destByteCount >= 4); i += 4, destByteCount -= 4)
+				*d.mp32++ = *s.mp32++;
+		}
+
+		if (i == sourceByteCount)
+			i = 0;
+
+		s.mpVoid = const_cast<void*>(pS);
+		s.mp8   += i;
+
+		while (destByteCount >= 1)
+		{
+			*d.mp8++ = *s.mp8++;
+			destByteCount--;
+		}
+	}
+	else
+	{
+		// Routine for non 32-bit aligned copy
+		while (destByteCount)
+		{
+			s.mpVoid = const_cast<void*>(pS);
+
+			for (size_t i = 0; (i < sourceByteCount) && destByteCount; i++)
+			{
+				*d.mp8++ = *s.mp8++;
+				destByteCount--;
+			}
+		}
+	}
+}
+
+
+// This is a local function called by MemfillSpecific.
+static void Memfill128(void* pD, const void* pS, size_t byteCount)
+{
+	unsigned char* pDestination = static_cast<unsigned char*>(pD);
+	const unsigned char* pSource = static_cast<const unsigned char*>(pS);
+	unsigned int v1;
+	unsigned int v2;
+	unsigned int v3;
+	unsigned int v4;
+
+	if ((reinterpret_cast<uintptr_t>(pSource) & 0x3) != 0)
+	{
+		// If the source is not aligned, we need to retrieve the values on a byte by byte basis
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			v1 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource)))*256*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+1)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+2)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+3))));
+			v2 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+4)))*256*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+5)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+6)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+7))));
+			v3 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+8)))*256*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+9)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+10)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+11))));
+			v4 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+12)))*256*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+13)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+14)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+15))));
+		#else
+			v1 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource)))) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+1)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+2)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+3)))*256*256*256);
+			v2 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+4)))) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+5)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+6)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+7)))*256*256*256);
+			v3 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+8)))) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+9)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+10)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+11)))*256*256*256);
+			v4 =(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+12)))) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+13)))*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+14)))*256*256) +
+				(static_cast<uint32_t>(*(static_cast<const uint8_t*>(pSource+15)))*256*256*256);
+		#endif
+	}
+	else
+	{
+		v1 = *(reinterpret_cast<const uint32_t*>(pSource));
+		v2 = *(reinterpret_cast<const uint32_t*>(pSource+4));
+		v3 = *(reinterpret_cast<const uint32_t*>(pSource+8));
+		v4 = *(reinterpret_cast<const uint32_t*>(pSource+12));
+	}
+
+	// Alignment correction
+	if ((reinterpret_cast<uintptr_t>(pDestination) & 0xF) != 0)
+	{
+		// Perform 32-bit alignment (this is required for ps2, since it crashes when it writes a 32-bit
+		//   value to an unaligned 32-bit memory address)
+		while (((reinterpret_cast<uintptr_t>(pDestination) & 0x03) != 0) && (byteCount>0))
+		{
+			byteCount--;
+			// rotate the values over
+			#if defined(EA_SYSTEM_BIG_ENDIAN)
+				*(pDestination++)=static_cast<uint8_t>(v1 >> 24);
+				unsigned int tmp = v1;
+				v1=(v1 << 8) + (v2 >> 24);
+				v2=(v2 << 8) + (v3 >> 24);
+				v3=(v3 << 8) + (v4 >> 24);
+				v4=(v4 << 8) + (tmp >> 24);
+			#else
+				*(pDestination++)=static_cast<uint8_t>(v1);
+				unsigned int tmp = v1;
+				v1=(v1 >> 8) + (v2 << 24);
+				v2=(v2 >> 8) + (v3 << 24);
+				v3=(v3 >> 8) + (v4 << 24);
+				v4=(v4 >> 8) + (tmp << 24);
+			#endif
+		}
+
+		if (byteCount >=256)
+		{
+			// not really worth performing all these functions if byteCount isn't large
+			// Perform 128-bit alignment on 32-bit boundary
+			unsigned int tempval,tempval2;
+			switch (reinterpret_cast<uintptr_t>(pDestination)&0xC)
+			{
+			case 0xC:
+				*reinterpret_cast<uint32_t*>(pDestination) = v1;
+				pDestination+=4;
+				byteCount-=4;
+				tempval = v1;
+				v1 = v2;
+				v2 = v3;
+				v3 = v4;
+				v4 = tempval;
+				break;
+			case 0x8:
+				*reinterpret_cast<uint32_t*>(pDestination) = v1;
+				*reinterpret_cast<uint32_t*>(pDestination+4) = v2;
+				pDestination+=8;
+				byteCount-=8;
+				tempval = v1;
+				tempval2 = v3;
+				v1 = tempval2;
+				v3 = tempval;
+				tempval = v2;
+				tempval2 = v4;
+				v2 = tempval2;
+				v4 = tempval;
+				break;
+			case 0x4:
+				*reinterpret_cast<uint32_t*>(pDestination) = v1;
+				*reinterpret_cast<uint32_t*>(pDestination+4) = v2;
+				*reinterpret_cast<uint32_t*>(pDestination+8) = v3;
+				pDestination+=12;
+				byteCount-=12;
+				tempval = v4;
+				v4 = v3;
+				v3 = v2;
+				v2 = v1;
+				v1 = tempval;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	// Start copying the stuff
+	while (byteCount >= 16)
+	{
+		*(reinterpret_cast<uint32_t*>(pDestination)) = v1;
+		*(reinterpret_cast<uint32_t*>(pDestination+4)) = v2;
+		*(reinterpret_cast<uint32_t*>(pDestination+8)) = v3;
+		*(reinterpret_cast<uint32_t*>(pDestination+12)) = v4;
+		byteCount-=16;
+		pDestination+=16;
+	}
+	if (byteCount > 0)
+	{
+		// end of destination not aligned to 128-bit
+		unsigned int i = 0;
+		while (byteCount >= 4)
+		{
+			*(reinterpret_cast<unsigned int*>(pDestination)) = v1;
+			pDestination+=4;
+			byteCount-=4;
+			v1=v2;
+			v2=v3;
+			v3=v4;
+		}
+		#if defined(EA_SYSTEM_BIG_ENDIAN)
+			for (i=0;(i<4) && (byteCount);i++)
+			{
+				*pDestination++ = static_cast<uint8_t>(v1 >> 24);
+				v1 = v1 << 8;
+				byteCount--;
+			}
+		#else
+			// write the remainder for low-endian as long as the byteCount value allows it
+			for (i=0;(i<4) && (byteCount!=0);i++)
+			{
+				*pDestination++ = static_cast<uint8_t>(v1);
+				v1 = v1 >> 8;
+				byteCount--;
+			}
+		#endif
+	}
+}
+
+
+EASTDC_API void Memfill16(void* pDestination, uint16_t c, size_t byteCount)
+{
+	Memfill32(pDestination, (uint32_t)((c << 16) + c), byteCount);
+}
+
+
+EASTDC_API void Memfill24(void* pDestination, uint32_t c, size_t byteCount)
+{
+	const uint8_t c24[3] = { (uint8_t)(c >> 16), (uint8_t)(c >> 8), (uint8_t)c };
+	Memfill24(pDestination, c24, byteCount);
+}
+
+
+#if defined(EA_PROCESSOR_X86) &&  defined(_MSC_VER)
+
+	EASTDC_API __declspec(naked) void Memfill32(void* /*pDestination*/, uint32_t /*c*/, size_t /*byteCount*/)
+	{
+		__asm
+		{
+			mov     eax,dword ptr [esp+4]   ; pDestination
+			mov     edx,dword ptr [esp+8]   ; c
+			mov     ecx,dword ptr [esp+12]  ; byteCount
+
+			sub     ecx,32
+			jns     b32a
+			jmp     b32b
+
+			align   16
+
+		; 32 byte filler
+		b32a:
+			sub     ecx,32
+			mov     [eax],edx
+			mov     [eax+4],edx
+			mov     [eax+8],edx
+			mov     [eax+12],edx
+			mov     [eax+16],edx
+			mov     [eax+20],edx
+			mov     [eax+24],edx
+			mov     [eax+28],edx
+			lea     eax,[eax+32]
+			jns     b32a
+
+		b32b:
+			add     ecx,32-8
+			js      b8b
+
+		; 8 byte filler
+		b8a:
+			mov     [eax],edx
+			mov     [eax+4],edx
+			add     eax,8
+			sub     ecx,8
+			jns     b8a
+
+		b8b:
+			add     ecx,8
+			jne     bend
+			ret
+
+		; tail cleanup 4,2,1
+		bend:
+			cmp     ecx,4
+			jb      be4
+			mov     [eax],edx
+			add     eax,4
+			sub     ecx,4
+		be4:
+			cmp     ecx,2
+			jb      be2
+			mov     [eax],dx
+			ror     edx,16
+			add     eax,2
+			sub     ecx,2
+		be2:
+			cmp     ecx,1
+			jb      be1
+			mov     [eax],dl
+			inc     eax
+			dec     ecx
+		be1:
+			ret
+		}
+	}
+
+#else
+
+	EASTDC_API void Memfill32(void* pDestination, uint32_t c, size_t byteCount)
+	{
+		while (((reinterpret_cast<intptr_t>(pDestination) & 3) != 0) && (byteCount > 0))
+		{
+			#if defined(EA_SYSTEM_BIG_ENDIAN)
+				*static_cast<uint8_t*>(pDestination) = static_cast<uint8_t>(c >> 24);
+				pDestination = static_cast<void*>(static_cast<char *>(pDestination) + 1);
+				c = (c << 8) + (c >> 24);        // rotate the value
+			#else
+				*static_cast<uint8_t*>(pDestination) = static_cast<uint8_t>(c);
+				pDestination = static_cast<void*>(static_cast<char *>(pDestination) + 1);
+				c = (c << 24) + (c >> 8);        // rotate the value
+			#endif
+
+			--byteCount;
+		}
+
+		if ((byteCount >= 4) && ((reinterpret_cast<intptr_t>(pDestination) & 4) != 0))
+		{
+			*static_cast<uint32_t*>(pDestination) = static_cast<uint32_t>(c);
+			pDestination = static_cast<void*>(static_cast<char*>(pDestination) + 4);
+			byteCount -= 4;
+		}
+
+		if (byteCount >= 64)
+		{
+			uint64_t c64 = (static_cast<uint64_t>(c) << static_cast<uint64_t>(32)) | static_cast<uint64_t>(c);
+
+			do
+			{
+				(static_cast<uint64_t*>(pDestination))[0] = c64;
+				(static_cast<uint64_t*>(pDestination))[1] = c64;
+				(static_cast<uint64_t*>(pDestination))[2] = c64;
+				(static_cast<uint64_t*>(pDestination))[3] = c64;
+				(static_cast<uint64_t*>(pDestination))[4] = c64;
+				(static_cast<uint64_t*>(pDestination))[5] = c64;
+				(static_cast<uint64_t*>(pDestination))[6] = c64;
+				(static_cast<uint64_t*>(pDestination))[7] = c64;
+
+				pDestination = static_cast<void*> (static_cast<char*>(pDestination) + 64);
+				byteCount -= 64;
+			}
+			while (byteCount >= 64);
+		}
+
+		if (byteCount >= 16)
+		{
+			do
+			{
+				(reinterpret_cast<uint32_t*>(pDestination))[0] = c;
+				(reinterpret_cast<uint32_t*>(pDestination))[1] = c;
+				(reinterpret_cast<uint32_t*>(pDestination))[2] = c;
+				(reinterpret_cast<uint32_t*>(pDestination))[3] = c;
+
+				pDestination = static_cast<void*> (static_cast<char*>(pDestination) + 16);
+				byteCount -= 16;
+			}
+			while (byteCount >= 16);
+		}
+
+		if (byteCount >= 4)
+		{
+			do
+			{
+				*static_cast<uint32_t*>(pDestination) = static_cast<uint32_t> (c);
+				pDestination = static_cast<void*>(static_cast<char*>(pDestination) + 4);
+				byteCount -= 4;
+			}
+			while (byteCount >= 4);
+		}
+
+		while (byteCount >= 1)
+		{
+			#if defined(EA_SYSTEM_BIG_ENDIAN)
+				*static_cast<uint8_t*>(pDestination) = static_cast<uint8_t> (c >> 24);
+				pDestination = static_cast<void*>(static_cast<char*>(pDestination) + 1);
+				c = c << 8;
+			#else
+				*static_cast<uint8_t*>(pDestination) = static_cast<uint8_t> (c);
+				pDestination = static_cast<void*>(static_cast<char*>(pDestination) + 1);
+				c = c >> 8;
+			#endif
+
+			--byteCount;
+		}
+	}
+
+#endif
+
+
+
+EASTDC_API void Memfill64(void* pDestination, uint64_t c, size_t byteCount)
+{
+	MemfillAny(pDestination, &c, byteCount, sizeof(c));
+}
+
+EASTDC_API void Memfill8(void* pDestination, uint8_t c, size_t byteCount)
+{
+	Memset8(pDestination, c, byteCount);
+}
+
+
+EASTDC_API void MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount)
+{
+	switch (sourceByteCount)
+	{
+		case 1:
+		{
+			const uint8_t c = *static_cast<const uint8_t*>(pSource);
+			Memset8(pDestination, c, destByteCount);
+			break;
+		}
+
+		case 2:
+		{
+			const uint16_t c = *static_cast<const uint16_t*>(pSource);
+			Memfill16(pDestination, c, destByteCount);
+			break;
+		}
+
+		case 3:
+			Memfill24(pDestination, pSource, destByteCount);
+			break;
+
+		case 4:
+		{
+			uint32_t c = *static_cast<const uint32_t*>(pSource);
+			Memfill32(pDestination, c, destByteCount);
+			break;
+		}
+
+		case 8:
+		default:
+			MemfillAny(pDestination, pSource, destByteCount, sourceByteCount);
+			break;
+
+		case 16:
+			Memfill128(pDestination, pSource, destByteCount);
+			break;
+	}
+}
+
+
+// Has similar behavior to the Unix bcmp function but executes the same instructions every 
+// time and thus executes in the same amount of time for a given byteCount. Assumes that the 
+// CPU executes the instructions below equivalently for all input byte combinations,
+// as is usually the case for logical integer operations.
+EASTDC_API bool TimingSafeMemEqual(const void* pMem1, const void* pMem2, size_t byteCount)
+{
+	const char8_t* p1 = (const char8_t*)pMem1;
+	const char8_t* p2 = (const char8_t*)pMem2;
+	char8_t mask = 0;
+
+	for(; byteCount > 0; ++p1, ++p2, --byteCount)
+		mask |= (*p1 ^ *p2);    // Accumulate any differences between the memory.
+
+	return (mask == 0);  // Concern: If the compiler sees the contents of pMem1 and pMem2 then it may optimize away the code above. In practice the compiler won't be able to see that in the use cases that matter to users.
+}
+
+
+// Has the same behavior as memcmp, but executes the same instructions every time and
+// thus executes in the same amount of time for a given byteCount. Assumes that the 
+// CPU executes the instructions below equivalently for all input byte combinations,
+// as is usually the case for logical integer operations.
+EASTDC_API int TimingSafeMemcmp(const void* pMem1, const void* pMem2, size_t byteCount)
+{
+	const uint8_t* p1 = static_cast<const uint8_t*>(pMem1);
+	const uint8_t* p2 = static_cast<const uint8_t*>(pMem2);
+	int result = 0;
+
+	while(byteCount--) // Walk through the bytes from back to front and recalculate the difference if one is encountered.
+	{
+		const int c1   = p1[byteCount];
+		const int c2   = p2[byteCount];
+		const int mask = (((c1 ^ c2) - 1) >> 8); // The result of the following is that mask is -1 (*p1 == *p2) or 0 (*p1 != *p2).
+		result &= mask;                          // If (*p1 == *p2) then mask is 0xffffffff and result is unchanged. Else result will is reset to 0 (to be updated on the next line).
+		result += (c1 - c2);                     // If (*p1 == *p2) then this adds 0 and result is unchanged. Else result will be (*p1 - *p2).
+	}                                            // The return value will be equal to the difference of the first unequal bytes.
+
+	return result;
+}
+
+
+EASTDC_API bool TimingSafeMemIsClear(const void* pMem, size_t byteCount)
+{
+	uint32_t mask = 0;
+	const uint8_t* p = static_cast<const uint8_t*>(pMem);
+
+	while(byteCount--)
+		mask |= *p++;
+
+	return (mask == 0);   // Concern: If the compiler sees the contents of pMem then it may optimize away the code above. In practice the compiler won't be able to see that in the use cases that matter to users.
+}
+
+
+
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+

+ 1075 - 0
source/EAProcess.cpp

@@ -0,0 +1,1075 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This module defines functions for process spawning and query.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAProcess.h>
+#include <EAStdC/EAString.h>
+#include <string.h>
+#include <EAAssert/eaassert.h>
+
+#if   defined(EA_PLATFORM_MICROSOFT)
+	#pragma warning(push, 0)
+	#include <Windows.h>
+	#include <stdlib.h>
+	#include <process.h>
+
+	#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		#include <ShellAPI.h>
+		#pragma warning(pop)
+
+		#ifdef _MSC_VER
+			#pragma comment(lib, "shell32.lib") // Required for shellapi calls.
+		#endif
+	#endif
+
+#elif defined(EA_PLATFORM_UNIX)
+	#include <sys/types.h>
+	#if EASTDC_SYS_WAIT_H_AVAILABLE
+		#include <sys/wait.h>
+	#endif
+	#include <unistd.h>
+	#include <errno.h>
+	#include <stdio.h>
+
+#elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED
+	#include <libdbg.h>
+#endif
+
+
+#if defined(EA_PLATFORM_APPLE)
+	#include <mach-o/dyld.h>    // _NSGetExecutablePath
+	#include <sys/syslimits.h>  // PATH_MAX
+	#include <libgen.h>         // dirname
+
+namespace EA
+{
+	namespace StdC
+	{
+		//Used to determine if a given path is a bundle extension
+			const char8_t* kBundleExtensions[] = {
+			".app",
+			".bundle",
+			".plugin"
+		};
+	}
+}
+#endif
+
+
+namespace EA
+{
+
+namespace StdC
+{
+
+
+// EASTDC_SETCURRENTPROCESSPATH_REQUIRED
+//
+// Defined as 0 or 1.
+//
+#ifndef EASTDC_SETCURRENTPROCESSPATH_REQUIRED
+	#if defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_CONSOLE)
+		#define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 1
+	#else
+		#define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 0
+	#endif
+#endif
+
+#if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
+	static char8_t gCurrentProcessPath[kMaxPathLength] = { 0 };
+#endif
+
+
+EASTDC_API void SetCurrentProcessPath(const char8_t* pPath)
+{
+	#if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
+		Strlcpy(gCurrentProcessPath, pPath, EAArrayCount(gCurrentProcessPath));
+	#else
+		EA_UNUSED(pPath);
+	#endif
+}
+
+
+
+#if defined(EA_PLATFORM_MICROSOFT) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP | EA_WINAPI_PARTITION_TV_APP | EA_WINAPI_PARTITION_TV_TITLE | EA_WINAPI_PARTITION_GAMES)
+
+	// According to Microsoft documentation:
+	//   The GetModuleFileName function retrieves the full path and file name
+	//   for the file containing the specified module.
+	//   If the function succeeds, the return value is the length of the string 
+	//   copied to the buffer, in TCHARs. If the buffer is too small to hold the 
+	//   module name, the string is truncated to the user-supplied capacity, and 
+	//   the function returns that capacity.
+
+	EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pPath), (DWORD)pathCapacity);
+
+		if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
+			return (size_t)dwResult;
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		// We cannot use GetModuleFileNameA here, because the text encoding of 
+		// GetModuleFileNameA is arbitrary and in any case is usually not UTF8. 
+		char16_t path16[kMaxPathLength];
+		GetCurrentProcessPath(path16, EAArrayCount(path16), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pPath, path16, (size_t)pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
+			return (size_t)intendedStrlen;
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pDirectory), (DWORD)pathCapacity);
+
+		if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
+		{
+			DWORD dw;
+
+			for(dw = dwResult; dw > 0; --dw)
+			{
+				if((pDirectory[dw - 1] != '/') && (pDirectory[dw - 1] != '\\'))
+					pDirectory[dw - 1] = 0;
+				else
+					break;
+			}
+
+			return dw;
+		}
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		char16_t path16[kMaxDirectoryLength];
+		GetCurrentProcessDirectory(path16, EAArrayCount(path16), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pDirectory, path16, (size_t)pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
+			return (size_t)intendedStrlen;
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+#elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED // Debug time only, as the following code would not be allowed for use in retail kits.
+
+	// sceDbgGetExecutablePath
+	// Gets the application file path. The path can only be obtained for applications run 
+	// from the host PC (run by Visual Studio, Neighborhood or the -run command).
+
+	EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		size_t result;
+
+		// If the user has set the process path, use the setting instead of querying.
+		if (gCurrentProcessPath[0])
+		{
+			result = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
+		}
+		else
+		{
+			result = (size_t)sceDbgGetExecutablePath(pPath, (size_t)(unsigned)pathCapacity);
+		}
+
+		if(result < (size_t)pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
+		{
+			return result;
+		}
+		else
+		{
+			pPath[0] = 0;
+			return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
+		}
+	}
+
+	EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		char8_t path8[kMaxPathLength];
+		GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
+	{
+		int32_t result;
+		if (gCurrentProcessPath[0])
+		{
+			result = (int32_t)Strlcpy(pDirectory, gCurrentProcessPath, pathCapacity);
+		}
+		else
+		{
+			result = sceDbgGetExecutablePath(pDirectory, (size_t)(unsigned)pathCapacity);
+		}
+
+		if(result < pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
+		{
+			while (result > 0)
+			{
+				if (pDirectory[result - 1] == '/' || pDirectory[result - 1] == '\\')
+					break;
+					
+				pDirectory[--result] = '\0';
+			}
+
+			return (size_t)(uint32_t)(result);
+		}
+		else
+		{
+			pDirectory[0] = 0;
+			return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
+		}
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		char8_t path8[kMaxDirectoryLength];    // We don't have access to EAIO here.
+		GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+#elif defined(EA_PLATFORM_APPLE)
+
+	EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		char8_t path8[kMaxPathLength];
+		GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pPath[0] = 0;
+		return 0;
+	}
+	
+	static bool IsBundleFolder(char8_t* pPath, int pathCapacity)
+	{
+		for(size_t i = 0; i < EAArrayCount(kBundleExtensions); i++)
+		{
+			if(Striend(pPath, kBundleExtensions[i]))
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	// To consider: add a flag so user can specify if they want the path to the actual executable even if it is in a .extension
+	// EG: /path/to/MyApp.extension or /path/to/MyApp.extension/MyExecutable
+	// Currently /path/to/.extension is returned if it exists, otherwise it returns the executable path
+	EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		// https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
+		// http://lists.apple.com/archives/darwin-dev/2008/Dec/msg00037.html
+		
+		uint32_t capacityU32 = (uint32_t)pathCapacity;
+		int result = _NSGetExecutablePath(pPath, &capacityU32); // Returns -1 and sets capacityU32 if the capacity is not enough.
+	
+		if(result == 0)
+		{
+			EA_ASSERT(pathCapacity >= kMaxPathLength);
+			char8_t absolutePath[PATH_MAX];
+
+			if(realpath(pPath, absolutePath) != NULL) // Obtain canonicalized absolute pathname.
+			{
+				if(pathFlags & kPathFlagBundlePath)
+				{
+					// We recursively call dirname() until we find .extension
+					char8_t appPath[kMaxPathLength];
+					EA::StdC::Strlcpy(appPath, absolutePath, kMaxPathLength);
+					bool found = IsBundleFolder(appPath, kMaxPathLength);
+
+					while(!found &&
+							EA::StdC::Strncmp(appPath, ".", kMaxPathLength) != 0 &&
+							EA::StdC::Strncmp(appPath, "/", kMaxPathLength) != 0)
+					{
+						EA::StdC::Strlcpy(appPath,dirname(appPath), kMaxPathLength);
+						found = IsBundleFolder(appPath, kMaxPathLength);
+					}
+				
+					if(found)
+						EA::StdC::Strlcpy(pPath, appPath, pathCapacity);
+					else // If not found, we use the original executable absolute path.
+						EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
+				}
+				else
+					EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
+
+				return Strlen(pPath);
+			}
+		}
+		
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		char8_t path8[kMaxDirectoryLength];    // We don't have access to EAIO here.
+		GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		char   pAppPath[kMaxPathLength];
+		size_t n = GetCurrentProcessPath(pAppPath, kMaxPathLength, pathFlags);
+		
+		if(n > 0)
+		{
+			// argv[0]       pDirectory
+			// --------------------------------------------------
+			// ""      ->    ""          (Should never happen)
+			// "/"     ->    "/"         (Should never happen)
+			// "a"     ->    "a"         (Should never happen)
+			// "/a/b"  ->    /a/"
+
+			EA_COMPILETIME_ASSERT(kMaxDirectoryLength >= kMaxPathLength); // We assert this because argv[0] could concievably be as long as kMaxPathLength.
+			const size_t intendedStrlen = Strlcpy(pDirectory, pAppPath, pathCapacity);
+
+			if(intendedStrlen < (size_t)pathCapacity) // If succeeded...
+			{
+				for(char8_t* p = pDirectory + intendedStrlen; p > pDirectory; --p)
+				{
+					if(p[-1] == '/')
+					{
+						p[0] = 0; // e.g. /aaa/bbb/ccc => /aaa/bbb/
+						return (size_t)(p - pDirectory);
+					}
+				}
+
+				// Alternative implementation which we should validate, as it's simpler:
+				//char* p = strrchr(pDirectory, '/');
+				//if(p) // This should usually (always?) be valid.
+				//    *p = 0;   // e.g. /aaa/bbb/ccc => /aaa/bbb/
+
+				return Strlen(pDirectory);
+			}
+		}
+		
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+#elif defined(EA_PLATFORM_LINUX)
+
+	EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		ssize_t resultLen = readlink("/proc/self/exe", pPath, pathCapacity);
+		if( resultLen != -1 )
+		{
+			ssize_t terminatorLocation = resultLen < (pathCapacity-1) ? resultLen : (pathCapacity-1);
+			pPath[terminatorLocation] = '\0';
+			return terminatorLocation;
+		}
+		else
+		{
+			pPath[0] = 0;
+			return 0;
+		}
+	}
+
+	EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		char8_t path8[kMaxPathLength];
+		GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		ssize_t resultLen = readlink("/proc/self/exe", pDirectory, pathCapacity);
+		if( resultLen != -1 )
+		{
+			for(int pos = resultLen; pos > 0; --pos)
+			{
+				if(pDirectory[pos - 1] != '/')
+					pDirectory[pos - 1] = 0;
+				else
+					break;
+			}
+			return strlen(pDirectory);
+		}
+		else
+		{
+			pDirectory[0] = 0;
+			return 0;
+		}
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+		char8_t path8[kMaxDirectoryLength];    // We don't have access to EAIO here.
+		GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
+			return (size_t)intendedStrlen;
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+	#if 0
+	/*
+		// http://blog.linuxgamepublishing.com/2009/10/12/argv-and-argc-and-just-how-to-get-them/
+		// http://stackoverflow.com/questions/1585989/how-to-parse-proc-pid-cmdline
+		// https://www.google.com/search?q=%2Fproc%2Fself%2Fcmdline
+
+		char** get_argv()
+		{
+			static char   emptyNonConstString[1][1] = { { 0 } };
+			static char** savedArgv = NULL;
+		
+			if(!savedArgv)
+			{
+				FILE* pFile = fopen("/proc/self/cmdline", "r");
+		
+				if(pFile) // This should be true for at least all recent Linux versions.
+				{
+					const  size_t kBufferSize = 1024; // This should be dynamically allocated if we are to be able to ready any buffer.
+					char   buffer[kBufferSize];
+					size_t count = fread(buffer, 1, kBufferSize, pFile);
+		
+					if(ferror(pFile) == 0) // If succeeded...
+					{
+						buffer[kBufferSize - 1] = 0;
+		 
+						// To do.
+						// buffer has an arbitrary number of 0-terminated strings layed one after another.
+						// Allocate an array of char pointers or use a static array of arrays. We can simply copy buffer to a permanent buffer and index its strings.
+						// Need to free an allocated array on shutdown.
+						// Check to make sure that the strlen wasn't too long.
+					}
+					fclose(pFile);   
+				}
+			}
+		
+			savedArgv = emptyNonConstString;
+			return savedArgv;
+		}
+	*/
+	#endif
+
+/*
+#elif defined(EA_PLATFORM_BSD) || (defined(EA_PLATFORM_SONY) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)))
+	Need to make this debug-only for proprietary platforms.
+	// A way to read the current process path:
+	// http://linux.die.net/man/2/readlink
+
+	#if defined(EA_PLATFORM_SONY)
+		ssize_t readlink(char *path, char *buf, size_t count)
+		{
+			int result;
+			__asm__ __volatile__( 
+				"mov %%rcx, %%r10\n\t"
+				"syscall\n\t"
+				: "=a"(result) : "a"(58), "D"(path), "S"(buf), "d"(count));
+			return result;
+		}
+	#endif
+
+	char buf[1024];
+	char buff[1024];
+	sprintf(buff, "/dev/%d/file", getpid());
+	size_t s = readlink(buff, buf, sizeof(buf));
+*/
+
+#else
+
+	EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		char8_t path8[kMaxPathLength];
+		GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
+			return (size_t)intendedStrlen;
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		#if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
+			const size_t intendedStrlen = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
+
+			if(intendedStrlen < (size_t)pathCapacity) // If it completely fit...
+				return intendedStrlen;
+		#else
+			EA_UNUSED(pathCapacity);
+		#endif
+
+		pPath[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		char8_t dir8[kMaxDirectoryLength];
+		GetCurrentProcessDirectory(dir8, EAArrayCount(dir8), pathFlags);
+
+		const int intendedStrlen = Strlcpy(pDirectory, dir8, (size_t)pathCapacity);
+
+		if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
+			return (size_t)intendedStrlen;
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+	EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
+	{
+		EA_ASSERT(pathCapacity > 0);
+
+		size_t len = GetCurrentProcessPath(pDirectory, pathCapacity, pathFlags);
+
+		if(len > 0)
+		{
+			for(int pos = (int)len; pos > 0; --pos)
+			{
+				// We make a broad assumption that both / and \ are directory separators. On a number of unsual platforms
+				// we deal with, / is the norm but \ can still be used. e.g. /host/C:\SomeDir\SomeFile.txt
+				if((pDirectory[pos - 1] != '/') && (pDirectory[pos - 1] != '\\'))
+					pDirectory[pos - 1] = 0;
+				else
+					break;
+			}
+
+			return Strlen(pDirectory);
+		}
+
+		pDirectory[0] = 0;
+		return 0;
+	}
+
+#endif
+
+
+// The 32 bit versions of GetCurrentProcessPath and GetCurrentProcessDirectory are the same generic
+// versions for all platforms, as they just route to using the platform-specific versions.
+EASTDC_API size_t GetCurrentProcessPath(char32_t* pPath, int pathCapacity, int pathFlags)
+{
+	EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+	char8_t path8[kMaxPathLength];
+	GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
+
+	const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
+
+	if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
+		return (size_t)intendedStrlen;
+
+	pPath[0] = 0;
+	return 0;
+}
+
+EASTDC_API size_t GetCurrentProcessDirectory(char32_t* pDirectory, int pathCapacity, int pathFlags)
+{
+	EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
+
+	char8_t path8[kMaxDirectoryLength];    // We don't have access to EAIO here.
+	GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
+
+	const int intendedStrlen = Strlcpy(pDirectory, path8, (size_t)pathCapacity);
+
+	if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
+		return (size_t)intendedStrlen;
+
+	pDirectory[0] = 0;
+	return 0;
+}
+
+
+
+
+
+
+EASTDC_API size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		DWORD dwLength = GetEnvironmentVariableW(reinterpret_cast<const wchar_t *>(pName), reinterpret_cast<LPWSTR>(pValue), (DWORD)valueCapacity);
+
+		if(dwLength == 0)
+		{
+			if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
+				return (size_t)-1;
+		}
+		else if(dwLength > valueCapacity)
+			dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
+
+		return (size_t)dwLength;
+
+	#else
+		char8_t name8[260];    
+		char8_t value8[260];    
+
+		Strlcpy(name8, pName, 260);
+		const size_t len = GetEnvironmentVar(name8, value8, 260);
+
+		if(len < 260)
+			return (size_t)Strlcpy(pValue, value8, valueCapacity, len);
+
+		return len; // Note that the len here is for UTF8 chars, but the user is asking for 16 bit chars. So the returned len may be higher than the actual required len.
+	#endif
+}
+
+
+EASTDC_API size_t GetEnvironmentVar(const char8_t* pName, char8_t* pValue, size_t valueCapacity)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		DWORD dwLength = GetEnvironmentVariableA(pName, pValue, (DWORD)valueCapacity);
+
+		if(dwLength == 0)
+		{
+			if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
+				return (size_t)-1;
+		}
+		else if(dwLength > valueCapacity)
+			dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
+
+		return (size_t)dwLength;
+
+	#elif defined(EA_PLATFORM_UNIX)
+		const char8_t* const var = getenv(pName);
+		if (var)
+			return Strlcpy(pValue, var, valueCapacity);
+		return (size_t)-1;
+	#else
+		// To consider: Implement this manually for the given platform.
+		// Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
+		EA_UNUSED(pName);
+		EA_UNUSED(pValue);
+		EA_UNUSED(valueCapacity);
+		return (size_t)-1;
+	#endif
+}
+
+
+EASTDC_API bool SetEnvironmentVar(const char16_t* pName, const char16_t* pValue)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		const BOOL bResult = SetEnvironmentVariableW(reinterpret_cast<const wchar_t*>(pName), reinterpret_cast<const wchar_t*>(pValue)); // Windows has the same behavior as us: NULL pValue removes the variable.
+		return (bResult != 0);
+	#else
+		char8_t name8[260];
+		Strlcpy(name8, pName, 260);
+
+		char8_t value8[260];
+		Strlcpy(value8, pValue, 260);
+
+		return SetEnvironmentVar(name8, value8);
+	#endif
+}
+
+
+EASTDC_API bool SetEnvironmentVar(const char8_t* pName, const char8_t* pValue)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		const BOOL bResult = SetEnvironmentVariableA(pName, pValue); // Windows has the same behavior as us: NULL pValue removes the variable.
+		return (bResult != 0);
+	#elif defined(EA_PLATFORM_UNIX)
+		// The opinion of the Linux people is that you just shouldn't ever call setenv during application runtime.
+		// A better solution for us is to use shared mapped memory (shm_open(), mmap()): http://www.ibm.com/developerworks/aix/library/au-spunix_sharedmemory/index.html
+		if(pValue)
+			return setenv(pName, pValue, 1) == 0;
+		else
+			return unsetenv(pName) == 0;
+	#else
+		// To consider: Implement this manually for the given platform.
+		// Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
+		// The easiest way for us to implement this is with an stl/eastl map. But we don't currently have access to those
+		// from this package. So we are currently stuck using something simpler, like a key=value;key=value;key=value... string.
+		EA_UNUSED(pName);
+		EA_UNUSED(pValue);
+		return false;
+	#endif
+}
+
+
+
+
+EASTDC_API int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		return int(_wspawnv(wait ? _P_WAIT : _P_DETACH, reinterpret_cast<const wchar_t *>(pPath), reinterpret_cast<const wchar_t* const *>(pArgumentArray)));
+	#else
+		EA_UNUSED(pPath);
+		EA_UNUSED(pArgumentArray);
+		EA_UNUSED(wait);
+
+		// TODO: convert and call char8_t version
+		EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
+		return -1;
+	#endif
+}
+
+
+EASTDC_API int Spawn(const char8_t* pPath, const char8_t* const* pArgumentArray, bool wait)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		if(wait)
+			return int(_spawnv(_P_WAIT, pPath, pArgumentArray));
+		else
+			return int(_spawnv(_P_DETACH, pPath, pArgumentArray));
+
+	#elif defined(EA_PLATFORM_UNIX) && EASTDC_SYS_WAIT_H_AVAILABLE
+		pid_t id = fork();
+
+		if(id == 0) // child
+		{
+			//int result = 
+			execv(pPath, (char* const*)pArgumentArray);
+			exit(errno);
+		}
+
+		if(wait)
+		{
+			int status;
+			waitpid(id, &status, 0); // waitpid() is safer than wait(), and seems currently be available on all OSs that matter to us.
+
+			if(WIFEXITED(status))
+				return WEXITSTATUS(status); // exit value of child
+
+			// the child was killed due to a signal. we could find out
+			// which signal if we wanted, but we're not really doing unix signals.
+			return -1;
+		}
+		return 0;
+
+	#else
+		EA_UNUSED(pPath);
+		EA_UNUSED(pArgumentArray);
+		EA_UNUSED(wait);
+
+		EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
+		return -1;
+	#endif
+}
+
+
+EASTDC_API int ExecuteShellCommand(const char16_t* pCommand)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		// Todo: verify that newlines work here and support them if not.
+		return _wsystem(reinterpret_cast<const wchar_t*>(pCommand)); // We could do this via the shell api as well.
+	#else
+		char8_t command8[260];   
+		Strlcpy(command8, pCommand, 260);
+
+		return ExecuteShellCommand(command8);
+	#endif
+}
+
+
+int ExecuteShellCommand(const char8_t* pCommand)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		// Todo: verify that newlines work here and support them if not.
+		return system(pCommand); // We could do this via the shell api as well.
+	#elif defined(EA_PLATFORM_UNIX)
+		return system(pCommand);
+	#else
+		EA_UNUSED(pCommand);
+		return false;
+	#endif
+}
+
+
+
+#if defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
+	EASTDC_API bool SearchEnvPathWithMode(const char8_t* pathListVar, const char8_t* fileName, int mode, eastl::string8* fullPath)
+	{
+		if (*fileName == '/' || *fileName == '\\')
+		{
+			fullPath->assign(fileName);
+			return access(fileName, F_OK) == 0;
+		}
+
+		const char* pathList = getenv(pathListVar);
+
+		if (pathList)
+		{
+			const char* pathStart = pathList;
+			const char* pathEnd   = pathStart;
+
+			while (true)
+			{
+				while ((*pathEnd != ':') && (*pathEnd != 0))
+					++pathEnd;
+
+				if (pathEnd > pathStart)
+				{
+					fullPath->assign(pathStart, pathEnd - pathStart);
+
+					if ((*pathEnd != '/') && (*pathEnd != '\\'))
+						*fullPath += '/';
+
+					*fullPath += fileName;
+
+					if (access(fullPath->c_str(), F_OK) == 0)
+						return true;
+				}
+
+				if (*pathEnd == 0)  // end explicitly so we don't access outside pathList.
+					break;
+
+				pathEnd++;
+				pathStart = pathEnd;
+			}          
+		}
+
+		return false;
+	}
+
+#endif // EA_PLATFORM_UNIX
+
+
+
+EASTDC_API bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		if(!pEnvironmentVar)
+			pEnvironmentVar = EA_CHAR16("PATH");
+		_wsearchenv(reinterpret_cast<const wchar_t*>(pFileName), reinterpret_cast<const wchar_t*>(pEnvironmentVar), reinterpret_cast<wchar_t*>(pPath));
+		return (*pPath != 0);
+
+	#else 
+		char8_t path8    [260]; 
+		char8_t fileName8[260]; 
+
+		Strlcpy(path8,     pPath,     260);
+		Strlcpy(fileName8, pFileName, 260);
+
+		bool success;
+
+		if (pEnvironmentVar)
+		{
+			char8_t environmentVariable8[260]; 
+			Strlcpy(environmentVariable8, pEnvironmentVar, 260);
+
+			success = EA::StdC::SearchEnvironmentPath(fileName8, path8, environmentVariable8);
+		}
+		else
+			success = EA::StdC::SearchEnvironmentPath(fileName8, path8);
+
+		Strlcpy(pPath, path8, 260);
+		return success;
+	#endif
+}
+
+
+EASTDC_API bool SearchEnvironmentPath(const char8_t* pFileName, char8_t* pPath, const char8_t* pEnvironmentVar)
+{
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		if(!pEnvironmentVar)
+			pEnvironmentVar = "PATH";
+		_searchenv(pFileName, pEnvironmentVar, pPath);
+		return (*pPath != 0);
+
+	#elif defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
+		eastl::string8 path8(EASTLAllocatorType(UTFFOUNDATION_ALLOC_PREFIX "EAProcess"));
+		bool success;
+
+		if (pEnvironmentVar)
+			success = SearchEnvPathWithMode(pEnvironmentVar, pFileName, F_OK, &path8); // Just require existence.
+		else
+			success = SearchEnvPathWithMode("PATH", pFileName, X_OK, &path8); // Require executability.
+			
+		if (success)
+		{
+			Strcpy(pPath, path8.c_str());
+			return true;
+		}
+		return false;
+
+	#else
+		EA_UNUSED(pFileName); 
+		EA_UNUSED(pPath); 
+		EA_UNUSED(pEnvironmentVar);
+		return false;
+	#endif
+}
+
+
+#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+	namespace // anonymous namespace for this file.
+	{
+		typedef HINSTANCE (APIENTRY* ShellExecuteWFunctionType)(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
+		static ShellExecuteWFunctionType ShellExecuteWFunction = NULL;
+		static HINSTANCE hShellExecuteWFunctionLibrary = NULL;
+
+		struct ShellExecuteWFunctionEntryPointFinder
+		{
+			ShellExecuteWFunctionEntryPointFinder()
+			{
+				hShellExecuteWFunctionLibrary = LoadLibraryW(EA_WCHAR("shell32.dll"));
+				if(hShellExecuteWFunctionLibrary)
+					ShellExecuteWFunction = (ShellExecuteWFunctionType)(void*)::GetProcAddress(hShellExecuteWFunctionLibrary, "ShellExecuteW");
+			}
+			~ShellExecuteWFunctionEntryPointFinder()
+			{
+				if(hShellExecuteWFunctionLibrary)
+					::FreeLibrary(hShellExecuteWFunctionLibrary);
+			}
+		};
+	}
+
+	EASTDC_API bool OpenFile(const char16_t* pPath)
+	{
+		HINSTANCE hInstance = 0;
+
+		ShellExecuteWFunctionEntryPointFinder sShellExecuteWFunctionEntryPointFinder;
+
+		if(ShellExecuteWFunction)
+		{
+			if(Strstr(pPath, EA_CHAR16("http://")) == pPath) // If the path begins with "http://" and is thus a URL...
+			{
+				wchar_t pathMod[260 + 4];
+				Strcpy(pathMod, EA_WCHAR("url:"));
+				Strlcat(pathMod, reinterpret_cast<const wchar_t*>(pPath), 260 + 4); // ShellExecute wants the path to look like this: "url:http://www.bozo.com"
+				hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), pathMod, NULL, NULL, SW_SHOWNORMAL);
+			}
+			else
+			{
+				hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), reinterpret_cast<const wchar_t*>(pPath), NULL, NULL, SW_SHOWNORMAL);
+			}
+		}
+
+		return ((uintptr_t)hInstance > 32);
+	}
+
+	EASTDC_API bool OpenFile(const char8_t* pPath)
+	{
+		char16_t path16[260]; 
+		Strlcpy(path16, pPath, 260);
+
+		return OpenFile(path16);
+	}
+
+#else
+
+	EASTDC_API bool OpenFile(const char16_t* pPath)
+	{
+		char8_t path8[260];
+		Strlcpy(path8, pPath, 260);
+
+		return OpenFile(path8);
+	}
+
+	EASTDC_API bool OpenFile(const char8_t* pPath)
+	{
+		#if defined (EA_PLATFORM_OSX)
+			const char8_t* args[] =
+			{
+				"open",
+				pPath,
+				0
+			};
+			return Spawn("/usr/bin/open", args) != -1;
+		#else
+			EA_UNUSED(pPath);
+			return false;
+		#endif
+	}
+
+#endif // EA_PLATFORM_WINDOWS
+
+
+
+} // namespace StdC
+
+} // namespace EA
+
+
+
+
+
+
+

+ 431 - 0
source/EARandom.cpp

@@ -0,0 +1,431 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// This file implements a basic set of random number generators suitable for game
+// development usage.
+/////////////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EARandomDistribution.h>
+#include <EAStdC/EAStopwatch.h>
+#include <string.h>
+#include <EAAssert/eaassert.h>
+
+
+#if defined(EASTDC_EASTOPWATCH_H)   
+	#define EARandomGetCPUCycle EA::StdC::Stopwatch::GetCPUCycle
+#elif EASTDC_TIME_H_AVAILABLE
+	#include <time.h>
+
+	static inline uint64_t EARandomGetCPUCycle()
+	{
+		return (uint64_t)clock();
+	}
+#else
+	#error Must define a way to get some pseudorandom bits with EARandomGetCPUCycle.
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+
+namespace Internal
+{
+	// this constant is designed to produce a maximum double value that when cast to a float does not fall outside the
+	// valid range of [0..1).
+	static const float_t RAND_FLOAT_MAX = 1.0f - 1.0f / 1048576.0f; // 1 - 2^-20
+}
+
+EASTDC_API void GetRandomSeed(void* pSeed, size_t nLength)
+{
+	// We get a 64 bit value to work with and copy it repeatedly into 
+	// the bytes of the seed.
+	const uint64_t nSeed64 = EARandomGetCPUCycle();
+
+	for(size_t i = 0; i < nLength; i++)
+		((unsigned char*)pSeed)[i] = (unsigned char)(nSeed64 >> ((i % sizeof(uint64_t)) * sizeof(uint64_t)));
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// RandomLinearCongruential
+///////////////////////////////////////////////////////////////////////////////
+
+void RandomLinearCongruential::SetSeed(uint32_t nSeed)
+{
+	if(nSeed == 0xffffffff)
+		nSeed = (uint32_t)(EARandomGetCPUCycle() & 0xffffffff);
+	else if(nSeed == 0)     // Test for seed == 0 because that's an illegal value for us.
+		nSeed = 0xaaaaaaaa; // Convert it to some other constant. The actual value of the constant doesn't matter much.
+
+	mnSeed = nSeed;
+}
+
+
+uint32_t RandomLinearCongruential::RandomUint32Uniform(uint32_t nLimit)
+{
+	return EA::StdC::RandomLimit(*this, nLimit);
+}
+
+
+double RandomLinearCongruential::RandomDoubleUniform()
+{
+	// All powers of two (such as this) are exact in floating point
+	static const double kDoubleUniformScaleFactor = 2.32830643653870e-10f; // = (1 / 4294967296)
+
+
+	// Unsigned conversions to float are often slow in due to store-to-load 
+	// mismatch stalls (well, at least on some architectures), so we do a 
+	// signed conversion.
+	int32_t randInt = int32_t(RandomUint32Uniform());
+	double dResult = (kDoubleUniformScaleFactor * randInt) + 0.5;
+
+	if(dResult > Internal::RAND_FLOAT_MAX)  // Due to precision issues, we need to clamp this.
+		dResult = Internal::RAND_FLOAT_MAX;
+
+	return dResult;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// RandomTaus
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators",
+// Mathematics of Computation, 65, 213 (1996), 203-213.
+// 
+// This generator has a period of approximately 2^88. This should be
+// preferred over simple linear congruential generators which fail to
+// produce uniformly distributed k-tuplets of numbers.
+//
+// Approved for EA use by EA Legal: 
+// http://easites.ea.com/legal/Lists/OpenSourceDealSheet/DispForm.aspx?ID=589
+///////////////////////////////////////////////////////////////////////////////
+
+const uint32_t kTausSeed0 = UINT32_C(3719485138);
+const uint32_t kTausSeed1 = UINT32_C(840184915);
+const uint32_t kTausSeed2 = UINT32_C(2586639250);
+
+uint32_t RandomTaus::GetSeed() const
+{
+	return (mState[0] ^ kTausSeed0);
+}
+
+void RandomTaus::SetSeed(uint32_t nSeed)
+{
+	if(nSeed == 0xffffffff)
+		nSeed = (uint32_t)(EARandomGetCPUCycle() & 0xffffffff);
+
+	const uint32_t newState[3] = { kTausSeed0 ^ nSeed, kTausSeed1 ^ nSeed, kTausSeed2 ^ nSeed };
+	SetSeed(newState);
+}
+
+void RandomTaus::SetSeed(const uint32_t* pSeedArray)
+{
+	if(pSeedArray)
+	{
+		mState[0] = pSeedArray[0];
+		mState[1] = pSeedArray[1];
+		mState[2] = pSeedArray[2];
+
+		if (mState[0] < 2)
+			mState[0] += kTausSeed0; // bad seed -- fix it
+
+		if (mState[1] < 8)
+			mState[1] += kTausSeed1; // bad seed -- fix it
+
+		if (mState[2] < 16)
+			mState[2] += kTausSeed2; // bad seed -- fix it
+	}
+	else
+		SetSeed(0xffffffff); // Set seed automatically.
+}
+
+uint32_t RandomTaus::RandomUint32Uniform()
+{
+	mState[0] = ((mState[0] & 0xfffffffe) << 12) ^ (((mState[0] << 13) ^ mState[0]) >> 19);
+	mState[1] = ((mState[1] & 0xfffffff8) <<  4) ^ (((mState[1] <<  2) ^ mState[1]) >> 25);
+	mState[2] = ((mState[2] & 0xfffffff0) << 17) ^ (((mState[2] <<  3) ^ mState[2]) >> 11);
+
+	return (mState[0] ^ mState[1] ^ mState[2]);
+}
+
+uint32_t RandomTaus::RandomUint32Uniform(uint32_t nLimit)
+{
+	return EA::StdC::RandomLimit(*this, nLimit);
+}
+
+double RandomTaus::RandomDoubleUniform()
+{
+	const uint32_t nRandNoLimit = RandomUint32Uniform();
+
+	// All powers of two (such as this) are exact in floating point
+	static const float kDoubleUniformScaleFactor = 2.32830643653870e-10f;
+
+	// Unsigned conversions to float are often slow in due to store-to-load 
+	// mismatch stalls (well, at least on some architectures), so we do a 
+	// signed conversion.
+	double dResult = (kDoubleUniformScaleFactor * (int32_t)nRandNoLimit) + 0.5;
+
+	if(dResult > Internal::RAND_FLOAT_MAX)  // Due to precision issues, we need to clamp this.
+		dResult = Internal::RAND_FLOAT_MAX;
+
+	return dResult;
+}
+
+
+double RandomTaus::RandomDoubleUniform(double limit)
+{
+	EA_ASSERT(limit > 0);
+
+	const uint32_t nRandNoLimit = RandomUint32Uniform();
+
+	// All powers of two (such as this) are exact in floating point
+	static const float kDoubleUniformScaleFactor = 2.32830643653870e-10f;
+
+	// Unsigned conversions to float are often slow in due to store-to-load 
+	// mismatch stalls (well, at least on some architectures), so we do a 
+	// signed conversion.
+	double dResult = (kDoubleUniformScaleFactor * limit * (int32_t)nRandNoLimit) + 0.5;
+
+	if(dResult > Internal::RAND_FLOAT_MAX)  // Due to precision issues, we need to clamp this.
+		dResult = Internal::RAND_FLOAT_MAX;
+
+	return dResult;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// RandomMersenneTwister
+///////////////////////////////////////////////////////////////////////////////
+
+RandomMersenneTwister::RandomMersenneTwister(uint32_t nSeed)
+{
+	mpNextState = NULL;
+	mnCountRemaining = kStateCount;
+	SetSeed(nSeed);
+}
+
+
+RandomMersenneTwister::RandomMersenneTwister(const uint32_t seedArray[], unsigned nSeedArraySize)
+{
+	mpNextState = NULL;
+	mnCountRemaining = kStateCount;
+	SetSeed(seedArray, nSeedArraySize);
+}
+
+
+RandomMersenneTwister& RandomMersenneTwister::operator=(const RandomMersenneTwister& randomMT)
+{
+	::memcpy(mState, randomMT.mState, sizeof(mState));
+	mpNextState      = &mState[0] + (randomMT.mpNextState - randomMT.mState);
+	mnCountRemaining = randomMT.mnCountRemaining;
+	return *this;
+}
+
+
+#define LOCAL_MIN(x, y) (x) < (y) ? (x) : (y)
+
+
+unsigned RandomMersenneTwister::GetSeed(uint32_t seedArray[], unsigned nSeedArraySize) const
+{
+	if(nSeedArraySize >= 1)
+	{
+		// Get mnCountRemaining
+		seedArray[0] = (uint32_t)mnCountRemaining;
+
+		// Get mState
+		unsigned i, copyCount = LOCAL_MIN((unsigned)kStateCount, nSeedArraySize - 1);
+
+		for(i = 0; i < copyCount; i++)
+			seedArray[i + 1] = mState[i];
+
+		for(i = copyCount; i < (nSeedArraySize - 1); i++)
+			seedArray[i + 1] = 0;
+
+		return copyCount + 1;
+	}
+
+	return 0;
+}
+
+
+void RandomMersenneTwister::SetSeed(const uint32_t seedArray[], unsigned nSeedArraySize)
+{
+	if(nSeedArraySize >= 1)
+	{
+		// Set mnCountRemaining
+		mnCountRemaining = (int32_t)seedArray[0];
+		if(mnCountRemaining > kStateCount)
+			mnCountRemaining = kStateCount;
+
+		// Set mpNextState
+		mpNextState = mState + (kStateCount - mnCountRemaining);
+
+		// Set mState
+		const uint32_t* pStateInput     = seedArray + 1;  // +1 because seedArray[0] stores mnCountRemaining.
+		uint32_t*       pStateOutput    = &mState[0];
+		uint32_t*       pStateOutputEnd = pStateOutput + kStateCount;
+
+		while(pStateOutput < pStateOutputEnd)
+		{
+			if(pStateInput >= (seedArray + 1 + nSeedArraySize))
+				pStateInput = (seedArray + 1); // Go back to the beginning.
+
+			*pStateOutput++ = *pStateInput++;
+		}
+	}
+}
+
+
+void RandomMersenneTwister::SetSeed(uint32_t nSeed)
+{
+	uint32_t* pState = &mState[0];
+	int i = kStateCount;
+
+	if(nSeed == 0xffffffff)
+		nSeed = (uint32_t)(EARandomGetCPUCycle() & 0xffffffff);
+
+	// Even seeds for the Mersenne Twister are known to be bad,
+	// where bad means a non-maximal period and striping.
+	nSeed |= 1; 
+					
+	while(i--)
+	{
+		*pState  = nSeed & 0xffff0000;
+		*pState |= ((nSeed *= 69069)++ & 0xffff0000) >> 16;
+		 pState++;
+		(nSeed *= 69069)++;
+	}
+	Reload();
+}
+
+
+uint32_t RandomMersenneTwister::RandomUint32Uniform()
+{
+	uint32_t nValue;
+
+	if(--mnCountRemaining < 0)
+	{
+		Reload();
+		--mnCountRemaining;
+	}
+
+	nValue  = *mpNextState++;
+	nValue ^= (nValue >> 11);
+	nValue ^= (nValue <<  7) & 0x9d2c5680;
+	nValue ^= (nValue << 15) & 0xefc60000;
+	return nValue ^ (nValue >> 18);
+}
+
+
+uint32_t RandomMersenneTwister::RandomUint32Uniform(uint32_t nLimit)
+{
+	return EA::StdC::RandomLimit(*this, nLimit);
+}
+
+
+double RandomMersenneTwister::RandomDoubleUniform()
+{
+	double dResult = (int32_t)RandomUint32Uniform() * 2.3283064365386963e-10 + 0.5;
+
+	if(dResult > Internal::RAND_FLOAT_MAX)  // Due to precision issues, we need to clamp this.
+		dResult = Internal::RAND_FLOAT_MAX;
+
+	return dResult;
+}
+
+
+static inline uint32_t LoBit(uint32_t n)
+{
+	return (n & 0x00000001);
+}
+
+
+static inline uint32_t MixBits(uint32_t n, uint32_t m)
+{
+	return ((n & 0x80000000) | (m & 0x7FFFFFFF));
+}
+
+
+void RandomMersenneTwister::Reload()
+{
+	const     uint32_t kMagicNumber = 0x9908b0df;  // Needs to be used as unsigned.
+	const     int kPeriodValue = 397;
+	uint32_t *p0 = &mState[0], *p2 = &mState[1], *pM = &mState[kPeriodValue];
+	uint32_t  s0 =  mState[0],  s1 =  mState[1];
+	int i;
+
+	for(i = kStateCount - kPeriodValue; i--; s0 = s1, s1 = *++p2)
+		*p0++ = *pM++ ^ (MixBits(s0, s1) >> 1) ^ (LoBit(s1) ? kMagicNumber : 0);
+
+	for(pM = &mState[0], i = kPeriodValue; --i; s0 = s1, s1 = *++p2 )
+		*p0++ = *pM++ ^ (MixBits(s0, s1) >> 1) ^ (LoBit(s1) ? kMagicNumber : 0);
+
+	s1 = mState[0];
+	*p0 = *pM ^ (MixBits(s0, s1) >> 1) ^ (LoBit(s1) ? kMagicNumber : 0);
+	
+	mnCountRemaining = kStateCount;
+	mpNextState = &mState[0];
+}
+
+
+uint32_t RandomMersenneTwister::Hash(int t, int c)
+{
+	static uint32_t nIncrementor = 0;
+
+	uint32_t       h1 = 0;
+	uint32_t       h2 = 0;
+	unsigned char* p = (unsigned char*)&t;
+	unsigned       i;
+
+	for(i=0; i < sizeof(t); ++i)
+	{
+		h1 *= UINT8_MAX + 2;
+		h1 += p[i];
+	}
+
+	p = (unsigned char*)&c;
+
+	for(i=0; i < sizeof(c); ++i)
+	{
+		h2 *= UINT8_MAX + 2;
+		h2 += p[i];
+	}
+
+	return (h1 + nIncrementor++) ^ h2;
+}
+
+
+// For unity build friendliness, undef all local #defines.
+#undef EARandomGetCPUCycle
+#undef LOCAL_MIN
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+
+
+
+
+

+ 292 - 0
source/EAScanf.cpp

@@ -0,0 +1,292 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAScanf.h>
+#include <EAStdC/internal/ScanfCore.h>
+
+
+namespace EA
+{
+namespace StdC
+{
+
+///////////////////////////////////////////////////////////////////////////////
+// char8_t functions
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Cscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(pReadFunction8, pContext, pFormat, arguments); 
+}
+
+EASTDC_API int Fscanf(FILE* pFile, const char8_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader8, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Scanf(const char8_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader8, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Sscanf(const char8_t* pDestination, const char8_t* pFormat, ...)
+{
+	ScanfLocal::SscanfContext8 sc(pDestination);
+
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader8, &sc, pFormat, arguments); 
+}
+
+
+EASTDC_API int Vcscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(pReadFunction8, pContext, pFormat, arguments); 
+}
+
+EASTDC_API int Vfscanf(FILE* pFile, const char8_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader8, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Vscanf(const char8_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader8, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Vsscanf(const char8_t* pDestination, const char8_t* pFormat, va_list arguments)
+{
+	ScanfLocal::SscanfContext8 sc(pDestination);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader8, &sc, pFormat, arguments); 
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// char16_t functions
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Cscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(pReadFunction16, pContext, pFormat, arguments);
+}
+
+EASTDC_API int Fscanf(FILE* pFile, const char16_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader16, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Scanf(const char16_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader16, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Sscanf(const char16_t* pDestination, const char16_t* pFormat, ...)
+{
+	ScanfLocal::SscanfContext16 sc(pDestination);
+
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader16, &sc, pFormat, arguments); 
+}
+
+
+EASTDC_API int Vcscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(pReadFunction16, pContext, pFormat, arguments); 
+}
+
+EASTDC_API int Vfscanf(FILE* pFile, const char16_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader16, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Vscanf(const char16_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader16, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Vsscanf(const char16_t* pDestination, const char16_t* pFormat, va_list arguments)
+{
+	ScanfLocal::SscanfContext16 sc(pDestination);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader16, &sc, pFormat, arguments); 
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// char32_t functions
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Cscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(pReadFunction32, pContext, pFormat, arguments);
+}
+
+EASTDC_API int Fscanf(FILE* pFile, const char32_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader32, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Scanf(const char32_t* pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader32, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Sscanf(const char32_t* pDestination, const char32_t* pFormat, ...)
+{
+	ScanfLocal::SscanfContext32 sc(pDestination);
+
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader32, &sc, pFormat, arguments); 
+}
+
+
+EASTDC_API int Vcscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(pReadFunction32, pContext, pFormat, arguments); 
+}
+
+EASTDC_API int Vfscanf(FILE* pFile, const char32_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader32, pFile, pFormat, arguments); 
+}
+
+EASTDC_API int Vscanf(const char32_t* pFormat, va_list arguments)
+{
+	return ScanfLocal::VscanfCore(ScanfLocal::FILEReader32, stdin, pFormat, arguments); 
+}
+
+EASTDC_API int Vsscanf(const char32_t* pDestination, const char32_t* pFormat, va_list arguments)
+{
+	ScanfLocal::SscanfContext32 sc(pDestination);
+
+	return ScanfLocal::VscanfCore(ScanfLocal::StringReader32, &sc, pFormat, arguments); 
+}
+
+#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+	EASTDC_API int Cscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vcscanf(pReadFunctionW, pContext, pFormat, arguments);
+
+		va_end(arguments);
+		return result;
+	}
+
+	EASTDC_API int Fscanf(FILE* pFile, const wchar_t* pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vfscanf(pFile, pFormat, arguments);
+
+		va_end(arguments);
+		return result;
+	}
+
+	EASTDC_API int Scanf(const wchar_t* pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vscanf(pFormat, arguments);
+
+		va_end(arguments);
+		return result;
+	}
+
+	EASTDC_API int Sscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vsscanf(pTextBuffer, pFormat, arguments);
+
+		va_end(arguments);
+		return result;
+	}
+
+	EASTDC_API int Vcscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vcscanf(reinterpret_cast<ReadFunction16>(pReadFunctionW), pContext, reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vcscanf(reinterpret_cast<ReadFunction32>(pReadFunctionW), pContext, reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vfscanf(FILE* pFile, const wchar_t* pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vfscanf(pFile, reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vfscanf(pFile, reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vscanf(const wchar_t* pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vscanf(reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vscanf(reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vsscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vsscanf(reinterpret_cast<const char16_t *>(pTextBuffer), reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vsscanf(reinterpret_cast<const char32_t *>(pTextBuffer), reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+#endif
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+

+ 1947 - 0
source/EAScanfCore.cpp

@@ -0,0 +1,1947 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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()
+
+
+

+ 657 - 0
source/EASprintf.cpp

@@ -0,0 +1,657 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/SprintfCore.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EACType.h>
+#include <EAAssert/eaassert.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <math.h>
+#include <float.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+EA_DISABLE_VC_WARNING(4127) // conditional expression is constant.
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// char8_t 
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Vcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(pWriteFunction8, pContext, pFormat, arguments);
+}
+
+EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	#if EASTDC_PRINTF_DEBUG_ENABLED
+		if((pFile == stdout) || (pFile == stderr))
+		{
+			SprintfLocal::PlatformLogWriterContext8 context;
+			return SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+		}
+	#endif
+
+	return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter8, pFile, pFormat, arguments);
+}
+
+EASTDC_API int Vprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	#if EASTDC_PRINTF_DEBUG_ENABLED
+		SprintfLocal::PlatformLogWriterContext8 context;
+		return SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+	#else
+		return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter8, stdout, pFormat, arguments);
+	#endif
+}
+
+EASTDC_API int Vsprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return Vsnprintf(pDestination, (size_t)-1, pFormat, arguments);
+}
+
+EASTDC_API int Vsnprintf(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::VprintfCore(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 nRequiredLength;
+		}
+		else if((n > 0) && pDestination)
+			pDestination[n - 1] = 0;
+		else if(n == 0)
+			return nRequiredLength;
+		return -1;
+	#endif
+}
+
+EASTDC_API int Vscprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	// vscprintf returns the number of chars that are needed for a printf operation.
+	return Vsnprintf(NULL, 0, pFormat, arguments);
+}
+
+EASTDC_API int Vdprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	SprintfLocal::PlatformLogWriterContext8 context;
+	return SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+}
+
+
+
+
+EASTDC_API int Cprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(pWriteFunction, pContext, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, ...)
+{
+	int result;
+
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	#if EASTDC_PRINTF_DEBUG_ENABLED
+		if((pFile == stdout) || (pFile == stderr))
+		{
+			SprintfLocal::PlatformLogWriterContext8 context;
+			result = SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+		}
+		else
+	#endif
+
+	result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter8, pFile, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Printf(const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	#if EASTDC_PRINTF_DEBUG_ENABLED
+		SprintfLocal::PlatformLogWriterContext8 context;
+		int result = SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+	#else
+		int result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter8, stdout, pFormat, arguments);
+	#endif
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Sprintf(char8_t* EA_RESTRICT pDestination, const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Snprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, n, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Dprintf(const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	SprintfLocal::PlatformLogWriterContext8 context;
+	int result = SprintfLocal::VprintfCore(SprintfLocal::PlatformLogWriter8, &context, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// char16_t 
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Vcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(pWriteFunction16, pContext, pFormat, arguments);
+}
+
+EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter16, pFile, pFormat, arguments);
+}
+
+EASTDC_API int Vprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter16, stdout, pFormat, arguments);
+}
+
+EASTDC_API int Vsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return Vsnprintf(pDestination, (size_t)-1, pFormat, arguments);
+}
+
+EASTDC_API int Vsnprintf(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::VprintfCore(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 nRequiredLength;
+		}
+		else if((n > 0) && pDestination)
+			pDestination[n - 1] = 0;
+		else if(n == 0)
+			return nRequiredLength;
+		return -1;
+	#endif
+}
+
+EASTDC_API int Vscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	// vscprintf returns the number of chars that are needed for a printf operation.
+	return Vsnprintf(NULL, 0, pFormat, arguments);
+}
+
+EASTDC_API int Cprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(pWriteFunction, pContext, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter16, pFile, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Printf(const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter16, stdout, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Sprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Snprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, n, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// char32_t 
+///////////////////////////////////////////////////////////////////////////////
+
+EASTDC_API int Vcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(pWriteFunction32, pContext, pFormat, arguments);
+}
+
+EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter32, pFile, pFormat, arguments);
+}
+
+EASTDC_API int Vprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return SprintfLocal::VprintfCore(SprintfLocal::FILEWriter32, stdout, pFormat, arguments);
+}
+
+EASTDC_API int Vsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return Vsnprintf(pDestination, (size_t)-1, pFormat, arguments);
+}
+
+EASTDC_API int Vsnprintf(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::VprintfCore(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 nRequiredLength;
+		}
+		else if((n > 0) && pDestination)
+			pDestination[n - 1] = 0;
+		else if(n == 0)
+			return nRequiredLength;
+		return -1;
+	#endif
+}
+
+EASTDC_API int Vscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	// vscprintf returns the number of chars that are needed for a printf operation.
+	return Vsnprintf(NULL, 0, pFormat, arguments);
+}
+
+EASTDC_API int Cprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(pWriteFunction, pContext, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter32, pFile, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Printf(const char32_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = SprintfLocal::VprintfCore(SprintfLocal::FILEWriter32, stdout, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Sprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, (size_t)SprintfLocal::kNoPrecision, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+EASTDC_API int Snprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+
+	int result = Vsnprintf(pDestination, n, pFormat, arguments);
+
+	va_end(arguments);
+
+	return result;
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// Deprecated functionality
+///////////////////////////////////////////////////////////////////////////
+
+struct Bridge8
+{
+	WriteFunction8Old mpOldWriteFunction;
+	void*             mpUserContext;
+};
+
+static int WriteFunctionBridge8(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState /*wfs*/)
+{
+	Bridge8* pBridge8 = (Bridge8*)pContext8;
+	return pBridge8->mpOldWriteFunction(pData, nCount, pBridge8->mpUserContext);
+}
+
+struct Bridge16
+{
+	WriteFunction16Old mpOldWriteFunction;
+	void*              mpUserContext;
+};
+
+static int WriteFunctionBridge16(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState /*wfs*/)
+{
+	Bridge16* pBridge16 = (Bridge16*)pContext16;
+	return pBridge16->mpOldWriteFunction(pData, nCount, pBridge16->mpUserContext);
+}
+
+EASTDC_API int Cprintf(WriteFunction8Old pWriteFunction, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+	int result = Vcprintf(pWriteFunction, pContext, pFormat, arguments);
+	va_end(arguments);
+	return result;
+}
+
+EASTDC_API int Cprintf(WriteFunction16Old pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...)
+{
+	va_list arguments;
+	va_start(arguments, pFormat);
+	int result = Vcprintf(pWriteFunction, pContext, pFormat, arguments);
+	va_end(arguments);
+	return result;
+}
+
+EASTDC_API int Vcprintf(WriteFunction8Old pWriteFunction8, void* EA_RESTRICT pContext, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	Bridge8 bridge8 = { pWriteFunction8, pContext };
+	return SprintfLocal::VprintfCore(WriteFunctionBridge8, &bridge8, pFormat, arguments);
+}
+
+EASTDC_API int Vcprintf(WriteFunction16Old pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	Bridge16 bridge16 = { pWriteFunction16, pContext };
+	return SprintfLocal::VprintfCore(WriteFunctionBridge16, &bridge16, pFormat, arguments);
+}
+
+#if EASTDC_VSNPRINTF8_ENABLED
+	EASTDC_API int Vsnprintf8(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		return Vsnprintf(pDestination, n, pFormat, arguments);
+	}
+
+	EASTDC_API int Vsnprintf16(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		return Vsnprintf(pDestination, n, pFormat, arguments);
+	}
+
+	EASTDC_API int Vsnprintf32(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		return Vsnprintf(pDestination, n, pFormat, arguments);
+	}
+
+	#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+		EASTDC_API int VsnprintfW(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+		{
+			return Vsnprintf(pDestination, n, pFormat, arguments);
+		}
+	#endif
+
+#endif
+
+#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
+	EASTDC_API int Vcprintf(WriteFunctionW pWriteFunctionW, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vcprintf(reinterpret_cast<WriteFunction16>(pWriteFunctionW), pContext, reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vcprintf(reinterpret_cast<WriteFunction32>(pWriteFunctionW), pContext, reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vfprintf(pFile, reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vfprintf(pFile, reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vprintf(reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vprintf(reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vsprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vsprintf(reinterpret_cast<char16_t *>(pDestination), reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vsprintf(reinterpret_cast<char32_t *>(pDestination), reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vsnprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vsnprintf(reinterpret_cast<char16_t *>(pDestination), n, reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vsnprintf(reinterpret_cast<char32_t *>(pDestination), n, reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Vscprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		#if (EA_WCHAR_SIZE == 2)
+			return Vscprintf(reinterpret_cast<const char16_t *>(pFormat), arguments);
+		#else
+			return Vscprintf(reinterpret_cast<const char32_t *>(pFormat), arguments);
+		#endif
+	}
+
+	EASTDC_API int Cprintf(WriteFunctionW pWriteFunction, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vcprintf(pWriteFunction, pContext, pFormat, arguments);
+
+		va_end(arguments);
+
+		return result;
+	}
+
+	EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vfprintf(pFile, pFormat, arguments);
+
+		va_end(arguments);
+
+		return result;
+	}
+
+	EASTDC_API int Printf(const wchar_t* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vprintf(pFormat, arguments);
+
+		va_end(arguments);
+
+		return result;
+	}
+
+	EASTDC_API int Sprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vsprintf(pDestination, pFormat, arguments);
+
+		va_end(arguments);
+
+		return result;
+	}
+
+	EASTDC_API int Snprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, ...)
+	{
+		va_list arguments;
+		va_start(arguments, pFormat);
+
+		int result = Vsnprintf(pDestination, n, pFormat, arguments);
+
+		va_end(arguments);
+
+		return result;
+	}
+
+#endif
+
+} // namespace StdC
+} // namespace EA
+
+
+EA_RESTORE_VC_WARNING()
+
+
+

+ 1959 - 0
source/EASprintfCore.cpp

@@ -0,0 +1,1959 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/internal/SprintfCore.h>
+#include <EAStdC/EAMathHelp.h>
+#include <EAStdC/EAString.h>
+#include <EAAssert/eaassert.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+#if defined(EA_PLATFORM_ANDROID)
+	#include <android/log.h>
+#endif
+
+//include for OutputDebugStringA
+#if defined(EASTDC_OUTPUTDEBUGSTRING_ENABLED) && EASTDC_OUTPUTDEBUGSTRING_ENABLED
+		EA_DISABLE_ALL_VC_WARNINGS();
+		#ifndef WIN32_LEAN_AND_MEAN
+			#define WIN32_LEAN_AND_MEAN
+			#define EASTDC_WIN32_LEAN_AND_MEAN_TEMP_DEF
+		#endif
+		#include <windows.h>
+		#if defined(EASTDC_WIN32_LEAN_AND_MEAN_TEMP_DEF)
+			#undef WIN32_LEAN_AND_MEAN
+			#undef EASTDC_WIN32_LEAN_AND_MEAN_TEMP_DEF
+		#endif
+		EA_RESTORE_ALL_VC_WARNINGS();
+#endif
+
+#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4127)  // conditional expression is constant.
+#endif
+
+// To do: switch this to instead use <EABase/eastdarg.h>'s va_list_reference at about Summer of 2015:
+#if (defined(__GNUC__) && (defined(__x86__) || defined(__x86_64__)))
+	#define VA_LIST_REFERENCE(arguments) ((va_list*) arguments)
+#else
+	#define VA_LIST_REFERENCE(arguments) ((va_list*)&arguments)
+#endif
+
+
+
+namespace EA
+{
+namespace StdC
+{
+namespace SprintfLocal
+{
+
+
+int StringWriter8(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState /*wfs*/)
+{
+	using namespace SprintfLocal;
+
+	SnprintfContext8* const pSnprintfContext8 = (SnprintfContext8*)pContext8;
+
+	if(nCount && !pSnprintfContext8->mbMaxCountReached) // If there is anything to write and if we haven't already exhausted the limit for this context...
+	{
+		if(nCount > (pSnprintfContext8->mnMaxCount - pSnprintfContext8->mnCount)) // If nCount results in an exhausting of the limit...
+		{
+			pSnprintfContext8->mbMaxCountReached = true; // Note that it is possible due to non-breakable multi-byte sequences that mnCount will be < mnMaxCount.
+
+			// We must check for (UTF8) MBCS sequences here. We cannot write a
+			// partial multi-byte sequence, but can only write a contiguous sequence.
+			const size_t nRoom = pSnprintfContext8->mnMaxCount - pSnprintfContext8->mnCount;
+			size_t i = 0;
+
+			while (i < nCount)
+			{
+				size_t nClusterSize;
+
+				if((uint8_t)pData[i] < 0xc2)
+					nClusterSize = 1;                
+				else if((uint8_t)pData[i] < 0xe0)
+					nClusterSize = 2;
+				else if((uint8_t)pData[i] < 0xf0)
+					nClusterSize = 3;
+				else
+					break; // Unknown size. Fail the cluster.
+
+				if (i + nClusterSize > nRoom)
+					break; // Out of room in our destination buffer.
+
+				i += nClusterSize;
+			}
+
+			nCount = i;
+		}
+
+		memcpy(pSnprintfContext8->mpDestination + pSnprintfContext8->mnCount, pData, nCount * sizeof(*pData));
+		pSnprintfContext8->mnCount += nCount;
+
+		return (int)(unsigned)nCount;
+	}
+
+	return 0;
+}
+
+int StringWriter16(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState /*wfs*/)
+{
+	using namespace SprintfLocal;
+
+	SnprintfContext16* const pSnprintfContext16 = (SnprintfContext16*)pContext16;
+
+	if(nCount > (pSnprintfContext16->mnMaxCount - pSnprintfContext16->mnCount))  // If nCount results in an exhausting of the limit...
+		nCount = pSnprintfContext16->mnMaxCount - pSnprintfContext16->mnCount;
+
+	memcpy(pSnprintfContext16->mpDestination + pSnprintfContext16->mnCount, pData, nCount * sizeof(char16_t));
+	pSnprintfContext16->mnCount += nCount;
+
+	return (int)(unsigned)nCount;
+}
+
+int StringWriter32(const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState /*wfs*/)
+{
+	using namespace SprintfLocal;
+
+	SnprintfContext32* const pSnprintfContext32 = (SnprintfContext32*)pContext32;
+
+	if(nCount > (pSnprintfContext32->mnMaxCount - pSnprintfContext32->mnCount))  // If nCount results in an exhausting of the limit...
+		nCount = pSnprintfContext32->mnMaxCount - pSnprintfContext32->mnCount;
+
+	memcpy(pSnprintfContext32->mpDestination + pSnprintfContext32->mnCount, pData, nCount * sizeof(char32_t));
+	pSnprintfContext32->mnCount += nCount;
+
+	return (int)(unsigned)nCount;
+}
+
+
+
+
+int FILEWriter8(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState /*wfs*/)
+{
+	FILE* const pFile = (FILE*)pContext8;
+	const size_t nResult = fwrite(pData, sizeof(char8_t), nCount, pFile); // It's OK if nCount == 0.
+	if(nResult == nCount)
+		return (int)(unsigned)nResult;
+	return -1;
+}
+
+int FILEWriter16(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState /*wfs*/)
+{
+	FILE* const pFile = (FILE*)pContext16;
+	const size_t nResult = fwrite(pData, sizeof(char16_t), nCount, pFile); // It's OK if nCount == 0.
+	if(nResult == nCount)
+		return (int)(unsigned)nResult;
+	return -1;
+}
+
+int FILEWriter32(const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState /*wfs*/)
+{
+	FILE* const pFile = (FILE*)pContext32;
+	const size_t nResult = fwrite(pData, sizeof(char32_t), nCount, pFile); // It's OK if nCount == 0.
+	if(nResult == nCount)
+		return (int)(unsigned)nResult;
+	return -1;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// PlatformLogWriter8
+//
+int PlatformLogWriter8(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState)
+{
+	#if defined(EA_PLATFORM_ANDROID)
+		// The __android_log_write function appends a \n to every call you make to it. This is a problem for 
+		// us because during a sprintf of a single string we call our Writer multiple times. If we just called
+		// __android_log_write for every Writer call, a single sprintf would be split across multiple trace lines,
+		// and \n are also (I think) ignored.
+
+
+		size_t countOriginal = nCount;
+
+		if(nCount)
+		{
+			const size_t kBufferSize               = EAArrayCount(PlatformLogWriterContext8::mBuffer);
+			const size_t kBufferSizeActual         = kBufferSize - 1;           // -1 because we need to save space for a terminating 0 char that __android_log_write wants.
+			const size_t kPlatformBufferSize       = 512;                       // This is the size that's the max size the platform's log-writing function can handle.
+			const size_t kPlatformBufferSizeActual = kPlatformBufferSize - 1;   // -1 because we need to save space for a terminating 0 char that __android_log_write wants.
+			const size_t kMaxCountActual           = (kBufferSizeActual < kPlatformBufferSizeActual) ? kBufferSizeActual : kPlatformBufferSizeActual;
+			PlatformLogWriterContext8 *pWriteInfo  = reinterpret_cast<PlatformLogWriterContext8*>(pContext8);
+
+			// Pick the smaller of the two buffers.
+
+			for(size_t i = 0; i < nCount; i++)
+			{
+				pWriteInfo->mBuffer[pWriteInfo->mPosition] = pData[i];
+
+				if((pData[i] == '\n') || (pWriteInfo->mPosition == kMaxCountActual)) // If the user is requesting a newline or if we have exhausted the most we can write in a single line...
+				{
+					if(pWriteInfo->mPosition == kMaxCountActual)
+						pWriteInfo->mPosition++;
+					pWriteInfo->mBuffer[pWriteInfo->mPosition] = 0;
+					__android_log_write(ANDROID_LOG_INFO, "EAStdC.Printf", pWriteInfo->mBuffer);
+					pWriteInfo->mPosition  = 0;
+					pWriteInfo->mBuffer[0] = 0;
+				}
+				else
+					pWriteInfo->mPosition++;
+			}
+		}
+
+		return (int)(unsigned)countOriginal;
+	#else
+		EA_UNUSED(pContext8);
+		// To do: buffer debug writes and flush the buffer at WriteFunctionState == kWFSEnd, because otherwise a 
+		// single Dprintf call could result in numerous calls to (for example) OutputDebugString instead of just one call.
+		// A good way to do this is to have the buffer be part of the void* context; that way we don't have to worry about
+		// having thread-local storage.
+		if(nCount)
+		{
+			if(pData[nCount] == 0) // If already 0-terminated...
+			{
+				#if EASTDC_OUTPUTDEBUGSTRING_ENABLED
+					OutputDebugStringA(pData);
+				#else
+					fputs(pData, stdout);
+
+					#if defined(EA_PLATFORM_MOBILE)
+						fflush(stdout); // Mobile platforms need this because otherwise you can easily lose output if the device crashes while not all the output has been written.
+					#endif
+				#endif
+			}
+			else
+			{
+				// Copy to a buffer first, taking into account that nCount may be larger than our buffer size.
+				size_t i, iEnd, charIndex = 0;
+				char   buffer[512];
+
+				while(charIndex < nCount)
+				{
+					for(i = 0, iEnd = (EAArrayCount(buffer) - 1); (i < iEnd) && (charIndex < nCount); ++i, ++charIndex)
+						buffer[i] = pData[charIndex];
+					buffer[i] = 0;
+
+					#if EASTDC_OUTPUTDEBUGSTRING_ENABLED
+						OutputDebugStringA(buffer);
+					#else
+						fputs(buffer, stdout);
+
+						#if defined(EA_PLATFORM_MOBILE)
+							fflush(stdout); // Mobile platforms need this because otherwise you can easily lose output if the device crashes while not all the output has been written.
+						#endif
+					#endif
+				}
+			}
+		}
+
+		return static_cast<int>(nCount);
+
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EASprintfInit
+//
+void EASprintfInit()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// EASprintfShutdown
+//
+void EASprintfShutdown()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper template to avoid including EASTL
+//
+template<typename T, typename U>
+struct IsSameType
+{
+	enum { value = 0 };
+};
+
+template<typename T>
+struct IsSameType<T, T>
+{
+	enum { value = 1 };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteLeftPadding 
+// 
+// If the formatted data is right aligned, then this function prefixes the 
+// output with the appropriate data.
+//
+template <typename CharT>
+static int WriteLeftPadding(int(*pWriteFunction)(const CharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+	void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, const CharT*& pBufferData, int nWriteCount)
+{
+	if(fd.mAlignment == kAlignmentLeft || fd.mnWidth <= nWriteCount)
+		return 0;
+
+	CharT nFill;
+
+	if(fd.mAlignment == kAlignmentZeroFill)
+	{
+		nFill = '0';
+
+		if(pBufferData && ((*pBufferData == '+') || (*pBufferData == '-') || (*pBufferData == ' ')))
+		{    // Skip zero fill for leading sign character.
+			if(pWriteFunction(pBufferData, 1, pWriteFunctionContext, kWFSIntermediate) == -1)
+				return -1; // This is an error; not the same as running out of space.
+			--nWriteCount;
+			++pBufferData;
+		}
+	}
+	else
+		nFill = ' ';
+
+	int nFillCount = fd.mnWidth - nWriteCount;
+	for(int i = 0; i < nFillCount; ++i)
+	{
+		// Consider making an optimization which writes more than one fill character at a time.
+		// We can do this by using the space at the beginning of pBuffer;
+		if(pWriteFunction(&nFill, 1, pWriteFunctionContext, kWFSIntermediate) == -1)
+			return -1; // This is an error; not the same as running out of space.
+	}
+	return nFillCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteRightPadding 
+// 
+// If the formatted data is left aligned, then this function suffixes the 
+// output with the appropriate data.
+//
+template <typename CharT>
+static int WriteRightPadding(int(*pWriteFunction)(const CharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+	void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, int nWriteCount)
+{
+	if(fd.mAlignment != kAlignmentLeft || fd.mnWidth <= nWriteCount)
+		return 0;
+
+	const CharT nSpace = ' ';
+	int nFillCount = fd.mnWidth - nWriteCount;
+	for(int i = 0; i < nFillCount; ++i)
+	{
+		if(pWriteFunction(&nSpace, 1, pWriteFunctionContext, kWFSIntermediate) == -1)
+			return -1; // This is an error; not the same as running out of space.
+	}
+	return nFillCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteBuffer 
+// 
+// Write the given buffer with the required left and right padding
+//
+template <typename CharT>
+static int WriteBuffer(int(*pWriteFunction)(const CharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+	void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, const CharT* pBufferData, int nWriteCount)
+{
+	const CharT* pBufferDataEnd = pBufferData + nWriteCount;
+	int nWriteCountCurrent = nWriteCount; // We will write at least as much nWriteCount, possibly more if the format specified a specific width.
+
+	int nFillCount = WriteLeftPadding(pWriteFunction, pWriteFunctionContext, fd, pBufferData, nWriteCount);
+	if(nFillCount < 0)
+		return -1; // This is an error; not the same as running out of space.
+	nWriteCountCurrent += nFillCount;
+
+	if(pBufferData != pBufferDataEnd && (pWriteFunction(pBufferData, pBufferDataEnd - pBufferData, pWriteFunctionContext, kWFSIntermediate) == -1))
+		return -1; // This is an error; not the same as running out of space.
+
+	nFillCount = WriteRightPadding(pWriteFunction, pWriteFunctionContext, fd, nWriteCountCurrent);
+	if(nFillCount < 0)
+		return -1; // This is an error; not the same as running out of space.
+	nWriteCountCurrent += nFillCount;
+	return nWriteCountCurrent;
+}
+	
+///////////////////////////////////////////////////////////////////////////////
+// StringNull 
+// 
+// Based on the character type, return the appropriate (null) string.
+//
+template <typename CharT>
+const CharT* StringNull();
+
+template <>
+const char8_t* StringNull<char8_t>() { return kStringNull8; }
+
+template <>
+const char16_t* StringNull<char16_t>() { return kStringNull16; }
+
+template <>
+const char32_t* StringNull<char32_t>() { return kStringNull32; }
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StringFormatLength
+//
+template <typename CharT>
+int StringFormatLength(const SprintfLocal::FormatData& fd, const CharT* pInBufferData)
+{
+
+	// For strings, the precision modifier refers to the number of chars to display.
+	if(fd.mnPrecision != kNoPrecision) // If the user specified some precision...
+	{
+		// Find which is shorter, the length or the precision.
+		const CharT*       pBufferCurrent = pInBufferData;
+		const CharT* const pBufferMax = pInBufferData + fd.mnPrecision;
+
+		while((pBufferCurrent < pBufferMax) && *pBufferCurrent)
+			++pBufferCurrent;
+		return (int)(pBufferCurrent - pInBufferData);
+	}
+	else
+	{
+		// Set the write count to be the string length. 
+		// Don't call strlen, as that would jump outside this module.
+		const CharT* pBufferCurrent = pInBufferData;
+
+		while(*pBufferCurrent) // Implement a small inline strlen.
+			++pBufferCurrent;
+		return (int)(pBufferCurrent - pInBufferData);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringFormatHelper
+//
+// This is a structure template since we want to do partial specialization and it could be the case that some 
+// compilers don't support it.  There are two forms of the implementation.  The first is when the
+// in and out types are the same.  In that case we can use the input buffer as the output buffer and do
+// no conversions.  When they are different, we are required to convert from one encoding to another.
+// 
+template <bool IsSameType, typename InCharT, typename OutCharT>
+struct StringFormatHelper
+{};
+
+template <typename InCharT, typename OutCharT>
+struct StringFormatHelper<true, InCharT, OutCharT>
+{
+	int operator()(int(*pWriteFunction)(const OutCharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+		void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, OutCharT* pScratchBuffer, const InCharT* pInBufferData)
+	{
+		int nWriteCount = StringFormatLength(fd, pInBufferData);
+		return WriteBuffer(pWriteFunction, pWriteFunctionContext, fd, pInBufferData, nWriteCount);
+	}
+};
+
+template <typename InCharT, typename OutCharT>
+struct StringFormatHelper<false, InCharT, OutCharT>
+{
+	int operator()(int(*pWriteFunction)(const OutCharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+		void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, OutCharT* pScratchBuffer, const InCharT* pInBufferData)
+	{
+		// We have a problem here. We are converting from one encoding to another.
+		// The only encoding that are supported are UTF-8, UCS-2 and UCS-4.  Any 
+		// other encoding can result in a loss of data or failure to reencode.
+
+		// Compute the input number of code units
+		int nInCodeUnits;
+		{
+			const InCharT* pInBufferCurrent = pInBufferData;
+			while(*pInBufferCurrent) // Implement a small inline strlen.
+				++pInBufferCurrent;
+			nInCodeUnits = (int)(pInBufferCurrent - pInBufferData);
+		}
+		const InCharT* pInBufferDataEnd = pInBufferData + nInCodeUnits;
+		
+		int nPrecision = fd.mnPrecision == kNoPrecision ? INT_MAX : fd.mnPrecision; // Don't assume the value of kNoPrecision
+																					
+		// If the input is empty or precision is zero, then write an empty buffer and return
+		if(nInCodeUnits == 0 || nPrecision == 0)
+		{
+			return WriteBuffer(pWriteFunction, pWriteFunctionContext, fd, pScratchBuffer, 0);
+		}
+
+		int nWriteCountSum = 0;
+		bool bFirstTime = true;
+		while(nPrecision != 0 && pInBufferData != pInBufferDataEnd)
+		{
+
+			// Compute the required output size.  Truncate if we have a precision
+			size_t outSize = (size_t)(unsigned)kConversionBufferSize;
+			if((size_t)nPrecision < outSize)
+			{
+				outSize = (size_t)(unsigned)fd.mnPrecision + 1;
+				nPrecision = 0;
+			}
+			else
+				nPrecision -= kConversionBufferSize - 1;
+
+			// Convert the string
+			size_t nOutUsed;
+			size_t nInUsed;
+			if(!Strlcpy(pScratchBuffer, pInBufferData, outSize, pInBufferDataEnd - pInBufferData, nOutUsed, nInUsed))
+				break;
+
+			// If we haven't done the left padding
+			if(bFirstTime)
+			{
+				int nWriteCount = static_cast<int>(nOutUsed);
+				if(nPrecision != 0 && nInUsed < (size_t)nInCodeUnits)
+				{
+					int nRemaining = Strlcpy((OutCharT*)nullptr, pInBufferData + nInUsed, 0, nInCodeUnits - nInUsed);
+					if(nRemaining < 0)
+						break;
+					nWriteCount += nRemaining;
+					if(fd.mnPrecision != kNoPrecision && fd.mnPrecision < nWriteCount)
+						nWriteCount = fd.mnPrecision;
+				}
+
+				const OutCharT* pTemp = pScratchBuffer;
+				int nFillCount = WriteLeftPadding(pWriteFunction, pWriteFunctionContext, fd, pTemp, nWriteCount);
+				EA_ASSERT(pTemp == pScratchBuffer); // should not get adjusted
+				if(nFillCount < 0)
+					return -1;
+				nWriteCountSum += nFillCount;
+
+				bFirstTime = false;
+			}
+
+			// Write the converted buffer
+			if(nOutUsed != 0 && (pWriteFunction(pScratchBuffer, nOutUsed, pWriteFunctionContext, kWFSIntermediate) == -1))
+				return -1; // This is an error; not the same as running out of space.
+			nWriteCountSum += static_cast<int>(nOutUsed);
+
+			// Advance the input
+			pInBufferData += nInUsed;
+		}
+
+		// If required, write the end
+		if(!bFirstTime)
+		{
+			int nFillCount = WriteRightPadding(pWriteFunction, pWriteFunctionContext, fd, nWriteCountSum);
+			if(nFillCount < 0)
+				return -1;
+			nWriteCountSum += nFillCount;
+		}
+
+		return nWriteCountSum;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StringFormat
+//
+// Check for a null input and/or reencode the input as requred.  The output string
+// length is returned and pointed to by ppOutBufferData.
+// 
+template <typename InCharT, typename OutCharT>
+int StringFormat(int(*pWriteFunction)(const OutCharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs),
+	void* EA_RESTRICT pWriteFunctionContext, const SprintfLocal::FormatData& fd, OutCharT* pScratchBuffer, const InCharT* pInBufferData)
+{
+	// I don't see the C99 standard specifying the behaviour for a NULL string pointer, 
+	// but both GCC and MSVC use "(null)" when such a NULL pointer is encountered.
+	if(pInBufferData == nullptr)
+	{
+		StringFormatHelper<true, OutCharT, OutCharT> helper;
+		return helper(pWriteFunction, pWriteFunctionContext, fd, pScratchBuffer, StringNull<OutCharT>());
+	}
+	else
+	{
+		StringFormatHelper<IsSameType<InCharT, OutCharT>::value, InCharT, OutCharT> helper;
+		return helper(pWriteFunction, pWriteFunctionContext, fd, pScratchBuffer, pInBufferData);
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ReadFormat
+//
+// Reads the current format into FormatData. Return value is pointer to first
+// char8_t after the format data.
+//
+// To know how printf truly needs to work, see the ISO C 1999 standard, section 7.19.6.1.
+// See http://www.cplusplus.com/ref/cstdio/printf.html or http://www.opengroup.org/onlinepubs/007908799/xsh/fprintf.html 
+// for decent basic online documentation about how printf is supposed to work.
+// 
+// Argument pFormat is a string pointing to a % format specification of the form:
+//        %[flags][width][.precision][modifiers]type
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+template <typename CharT>
+const CharT* ReadFormat(const CharT* EA_RESTRICT pFormat, SprintfLocal::FormatData* EA_RESTRICT pFormatData, va_list* EA_RESTRICT pArguments)
+{
+	using namespace SprintfLocal;
+
+	const CharT*   pFormatCurrent = pFormat;
+	Alignment      alignmentNonZeroFill = kAlignmentLeft; // We have a chicken and egg problem in that the zero-fill alignment may or may not be ignored. So we save the value here for what the alignment would be if zero-fill needs to be ignored.
+	FormatData     fd;
+	CharT          c;
+
+	// Check for "%%". This is a quick test for early exit.
+	if((c = *++pFormatCurrent) == '%')    // If the user specified "%%"...
+	{
+		fd.mnType = '%';
+		*pFormatData = fd;    // pFormatData->mnType = '%'; Consider instead using just this line of code.
+		return pFormatCurrent + 1;
+	}
+
+	// Check for flags field
+	for(; ; (c = *++pFormatCurrent)) // Check for one or more flags ('-', '+', ' ', '#', or '0')
+	{
+		switch(c)
+		{
+			case '-': // '-' controls alignment, not the +/- sign before numbers.
+				fd.mAlignment = kAlignmentLeft;
+				break;
+
+			case '+':
+				fd.mSign = kSignMinusPlus;
+				break;
+
+			case ' ':    // The C99 language states (7.19.6.1.6): "If the space and + flags both appear, the space flag is ignored."
+				if(fd.mSign != kSignMinusPlus)
+				   fd.mSign  = kSignSpace;
+				break;
+
+			case '#':    // The C99 standard states (7.19.6.1.6): The result is converted to an "alternative form."
+				fd.mbAlternativeForm = true; 
+				break;
+
+			case '\'':   // Non-standard but common extension. e.g. http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/printf.3.html
+				fd.mbDisplayThousands = true;
+				break;
+
+			case '0':    // The C99 standard states (7.19.6.1.6): If the 0 and - flags both appear, the 0 flag is ignored. For d, i, o, u, x, and X conversions, if a precision is specified, the 0 flag is ignored. For other conversions, the behavior is undefined.
+				if(fd.mAlignment != kAlignmentLeft)     // Note that '0' is a flag (which can appear in any order) and not part of the number. This is a common misconception.
+				{
+					if(fd.mAlignment != kAlignmentZeroFill) // The C99 standard says that for string fields, 0 fill means space fill.
+						alignmentNonZeroFill = fd.mAlignment;
+					fd.mAlignment  = kAlignmentZeroFill;
+				}
+				break;
+
+			default:
+				goto EndFlagCheck; // C/C++ unfortunately don't provide an efficient mechanism to break from multiple loops other than 'goto'. We could avoid the goto with alternative logic, but that would be less efficient.
+		}
+	}
+	EndFlagCheck:
+
+	// Check for width field. 
+	// The C99 standard states (7.19.6.1.5): A field width, or precision, or both, may be 
+	// indicated by an asterisk. In this case, an int argument supplies the field width or 
+	// precision. The arguments specifying field width, or precision, or both, shall appear 
+	// (in that order) before the argument (if any) to be converted. A negative field 
+	// width argument is taken as a - flag followed by a positive field width. 
+	// A negative precision argument is taken as if the precision were omitted.
+	if(c == '*')
+	{
+		fd.mnWidth = va_arg(*pArguments, int);
+		if(fd.mnWidth < 0)
+		{
+			fd.mAlignment = kAlignmentLeft; // Pretend that a '-' flag was applied, as specified by the standard.
+			fd.mnWidth    = -fd.mnWidth;
+		}
+		c = *++pFormatCurrent;
+	}
+	else
+	{
+		// Read the width numerical value. We don't do error checking here as it 
+		// would incur a performance penalty that just isn't worth it to us.
+		while(((unsigned)(c - '0')) < 10)    // Don't call isdigit() here because that might cause a data cache miss.
+		{
+			fd.mnWidth = (int)((fd.mnWidth * 10) + (c - '0')); // Consider if there is any way to make this loop more 
+			c = *++pFormatCurrent;                             // efficient by reducing multiplies, etc.
+		}
+	}
+
+	if(fd.mnWidth > kMaxWidth)
+	{
+		// Note that we leave fd.mnType as zero, indicating an error.
+		*pFormatData = fd; // pFormatData->mnType = 0; Consider instead using just this line of code.
+		return pFormatCurrent + 1;
+	}
+
+	// Check for precision field.
+	// An optional precision that gives the minimum number of digits to appear for the 
+	// d, i, o, u, x, and X conversions, the number of digits to appear after the decimal-point
+	// character for a, A, e, E, f, and F conversions, the maximum number of significant
+	// digits for the g and G conversions, or the maximum number of bytes to be written for
+	// s conversions. The precision takes the form of a period (.) followed either by an
+	// asterisk * (described later) or by an optional decimal integer; if only the period 
+	// is specified, the precision is taken as zero. If a precision appears with any other
+	// conversion specifier, the behavior is undefined.
+	if(c == (CharT)pFormatData->mDecimalPoint) // If precision is specified...
+	{
+		if((c = *++pFormatCurrent) == '*') // If the precision is set as a value passed in as an argument...
+		{
+			fd.mnPrecision = va_arg(*pArguments, int);
+			if(fd.mnPrecision < 0)
+				fd.mnPrecision = 0;
+			c = *++pFormatCurrent;
+		}
+		else
+		{
+			fd.mnPrecision = 0;
+			while(((unsigned)(c - '0')) < 10) // Don't call isdigit() here because that might cause a data cache miss.
+			{
+				// Consider doing error checking 
+				fd.mnPrecision = (int)((fd.mnPrecision * 10) + (c - '0'));
+				c = *++pFormatCurrent;
+			}
+		}
+	}
+
+	// Check for length modifier field. C99 standard section 7.19.6.1.7.
+	// We support the following modifiers, which include non-standard integer size-specific modifiers.
+	//     hh, h, l, ll, I8, I16, I32, I64, I128
+	bool bModifierPresent = true; // True until proven false below.
+
+	switch(c)
+	{
+		case 'h':
+			if(pFormatCurrent[1] == 'h') // If the user specified %hh ...
+			{
+				// Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char8_t or unsigned char8_t before printing); or that a following n conversion specifier applies to a pointer to a signed char8_t argument.
+				fd.mModifier = kModifierChar;
+				c = *++pFormatCurrent;
+			}
+			else // Else the user specified just %h
+			{
+				// Specifies that a following d, i, o, u, x, or X conversion specifier applies to a short int or unsigned short int argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to short int or unsigned short int before printing); or that a following n conversion specifier applies to a pointer to a short int argument.
+				fd.mModifier = kModifierShort;
+			}
+			break;
+
+		case 'l': // Check for ell (not one).
+			if(pFormatCurrent[1] == 'l') // If the user specified %ll ...
+			{
+				// Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long long int or unsigned long long int argument; or that a following n conversion specifier applies to a pointer to a long long int argument.
+				fd.mModifier = kModifierLongLong;
+				c = *++pFormatCurrent;
+			}
+			else // Else the user specified just %l
+			{
+				// Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long int or unsigned long int argument; that a following n conversion specifier applies to a pointer to a long int argument; that a following c conversion specifier applies to a wint_t argument; that a following s conversion specifier applies to a pointer to a wchar_t argument; or has no effect on a following a, A, e, E, f, F, g, or G conversion specifier.
+				fd.mModifier = kModifierLong;
+			}
+			break;
+
+		case 'q': 
+			// BSD-based OS's use %q to indicate "quad int", which is the same as "long long". We need to support it because iPhone's C99 headers define PRId64 as "qd". 
+			fd.mModifier = kModifierLongLong;
+			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 a long double argument.
+			fd.mModifier = kModifierLongDouble;
+			break;
+
+		case 'I':
+			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.
+			{
+				// Note that we leave fd.mnType as zero, indicating an error.
+				*pFormatData = fd; // pFormatData->mnType = kFormatError; // Consider instead using just this line of code.
+				return pFormatCurrent + 1;
+			}
+			break;
+
+		default:
+			bModifierPresent = false;
+	}
+
+	if(bModifierPresent)
+		c = *++pFormatCurrent; // Move the pointer past the format (e.g. the 'f' in "%3.1f")
+
+	// Read the conversion type. This must be present.
+	fd.mnType = (int)c;
+
+	switch(c)
+	{
+		case 'b': // unsigned binary. This is a convenient extension that we add.
+		case 'd': // signed
+		case 'i': // signed
+		case 'u': // unsigned
+		case 'o': // unsigned
+		case 'x': // unsigned
+		case 'X': // unsigned
+			if(fd.mnPrecision == kNoPrecision)
+				fd.mnPrecision = 1;
+			else if(fd.mAlignment == kAlignmentZeroFill)
+				fd.mAlignment = kAlignmentRight;
+			break;
+
+		case 'g':
+		case 'G':
+			if(fd.mnPrecision == 0) // For %g, if the precision is zero, it is taken as 1.
+			   fd.mnPrecision = 1;
+			// fall through
+		case 'e':
+		case 'E':
+		case 'f':
+		case 'F':
+		case 'a':  // See the C99 standard, section 7.19.6.1.8, for details on 'a' formatting.
+		case 'A':
+			if(fd.mnPrecision == kNoPrecision)
+			   fd.mnPrecision = 6;    // The C99 standard explicitly states that this defaults to 6.
+			break;
+
+		case 'p':
+			if(sizeof(void*) == 2)
+				fd.mModifier = kModifierInt16;
+			else if(sizeof(void*) == 4)
+				fd.mModifier = kModifierInt32;
+			else
+				fd.mModifier = kModifierInt64;
+			fd.mnPrecision = sizeof(void*) / 4;  // This is 8 for a 32 bit pointer, 16 for a 64 bit pointer.
+			fd.mnType      = 'x';
+
+			// For the "alternative form" of x (or X) conversion, a nonzero result has 0x (or 0X) prefixed to it.
+			// So if the user uses %#p, then the user gets something like 0x12345678, whereas otherwise the 
+			// user gets just 12345678.
+
+			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)
+			// If the user specified zero-fill above, then it is a mistake and we 
+			// need to use spaces instead. So we restore the fallback alignment.
+			if(fd.mAlignment == kAlignmentZeroFill)
+				fd.mAlignment = alignmentNonZeroFill;
+
+			// Microsoft's library goes against the standard: %s is 
+			// interpreted to mean char8_t string but instead is interpreted 
+			// to be either char8_t or wchar_t depending on what the output
+			// text format 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 EASPRINTF_MS_STYLE_S_FORMAT to control this.
+			if(fd.mModifier == kModifierShort)
+				fd.mModifier = kModifierChar;
+			else if(fd.mModifier == kModifierLong)
+				fd.mModifier = kModifierWChar;
+			else if(fd.mModifier == kModifierNone) // If the user hasn't already specified, for example %I16.
+			{
+				#if EASPRINTF_MS_STYLE_S_FORMAT
+					if((c == 's') || (c == 'c'))
+						fd.mModifier = (sizeof(*pFormat) == sizeof(char)) ? kModifierChar : kModifierWChar;
+
+					else
+						fd.mModifier = (sizeof(*pFormat) == sizeof(char)) ? 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.
+			//{
+			//    EA_FAIL_MSG("Printf: Invalid %s modifier");
+			//}
+			break;
+
+		case 'n':
+			// The argument shall be a pointer to signed integer into which is written the 
+			// number of characters written to the output stream so far by this call to printf. 
+			// No argument is converted, but one is consumed. If the conversion specification 
+			// includes any flags, a field width, or a precision, the behavior is undefined.
+
+			// We don't really have anything to do here, as this function is merely reading
+			// the format and not acting on it. The caller will act on this appropriately.
+			break;
+	}
+
+	// If the field width is too long and it's not a string field...
+	if((fd.mnPrecision > kMaxPrecision) && (fd.mnPrecision != kNoPrecision) && ((fd.mnType != 's') && (fd.mnType != 'S')))
+		fd.mnType = kFormatError;
+
+	// Note that at his point we haven't read the argument corresponding to the formatted value. 
+	// We save this for the parent function, as otherwise we'd need some kind of union here to
+	// hold all value types.
+	*pFormatData = fd;
+	return pFormatCurrent + 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteLongHelper
+//
+// Writes the given lValue to the given buffer and returns the start of the 
+// data or returns NULL for error. Note that the buffer is passed in as the 
+// end of the buffer and not the beginning. This is a common trick used when
+// converting integer values to strings, as the conversion algorithm needs
+// to work backwards as it is and it's quicker to simply start with the end
+// of the buffer and move backwards.
+//
+template <typename CharT, typename ValueT, typename UValueT>
+CharT* WriteLongHelper(const SprintfLocal::FormatData& fd, ValueT lValue, CharT* EA_RESTRICT pBufferEnd)
+{
+	using namespace SprintfLocal;
+
+	UValueT       ulValue = (UValueT)lValue;
+	unsigned int  nBase;
+	unsigned int  nShift = 0;
+	unsigned int  nAnd = 0;
+	Sign          sign = kSignNone;
+	CharT*        pCurrent = pBufferEnd;
+	int           nDigitCount = 0;
+	int           nDigitCountSum = fd.mnPrecision;
+	bool          bNegative = false;
+
+	*--pCurrent = 0;
+
+	if((lValue > 0) || (fd.mnPrecision > 0) || fd.mbAlternativeForm)
+	{
+		// Do initial setup. 
+		switch(fd.mnType)
+		{
+			case 'b': // Binary (this is non-standard, though many would like it to be so)
+				nBase  = 2;
+				nShift = 0x01;
+				nAnd   = 0x01;
+				break;
+
+			case 'o': // Octal
+				nBase  = 8;
+				nShift = 0x03;
+				nAnd   = 0x07;
+				break;
+
+			case 'd': // Decimal (signed)
+			case 'i': // i and d are defined by the standard to be the same.
+			default:
+				nBase = 10;
+				sign  = fd.mSign;
+				if(lValue < 0)
+				{
+					ulValue   = (UValueT)-lValue;
+					bNegative = true;
+				}
+				break;
+
+			case 'u': // Decimal (unsigned)
+				nBase = 10;
+				break;
+
+			case 'x': // Hexidecimal
+			case 'X':
+				nBase  = 16;
+				nShift = 0x04;
+				nAnd   = 0x0f;
+				break;
+		}
+
+		// Write the individual digits.
+		// To do: Use the optimization in EAString.cpp's X64toaCommon10 function 
+		// to significantly improve writing base 10 strings. 
+		// Optimization: Replace the % and / below with >> and & for cases where
+		// we can do this (i.e. nBase = even power of two).
+		do
+		{
+			int nDigit;
+
+			if(nBase == 10)
+			{
+				nDigit = (int)(ulValue % nBase);
+				ulValue /= nBase;
+			}
+			else // Else take faster pathway.
+			{
+				nDigit = (int)(ulValue & nAnd);
+				ulValue >>= nShift;
+			}
+
+			if(nDigit < 10)
+				nDigit = '0' + nDigit;
+			else
+			{
+				nDigit -= 10;
+				if(fd.mnType == 'x')
+					nDigit = 'a' + nDigit;
+				else
+					nDigit = 'A' + nDigit;
+			}
+
+			*--pCurrent = (CharT)nDigit;
+			++nDigitCount;
+
+			if((nBase == 10) && (fd.mbDisplayThousands) && (ulValue > 0) && (((nDigitCount + 1) % 4) == 0))
+			{
+				*--pCurrent = (CharT)fd.mThousandsSeparator;
+				++nDigitCount; // Even though the thousands separator isn't strictly a digit, it counts towards the space used by the number, which is what matters here.
+			}
+		} while(ulValue > 0);
+
+		// For octal mode, the standard specifies that when 'alternative form' is enabled, 
+		// the number is prefixed with a zero. This is like how the C language interprets 
+		// integers that begin with zero as octal. 
+		if((nBase == 8) && fd.mbAlternativeForm && (*pCurrent != (CharT)'0'))
+		{
+			*--pCurrent = (CharT)'0';
+			++nDigitCount;
+		}
+
+		// Calculate any leading zeroes required by the 'zero fill' alignment option.
+		if(fd.mAlignment == kAlignmentZeroFill) // If we are to precede the number with zeroes...
+		{
+			if(bNegative || (sign != kSignNone))
+				nDigitCountSum = fd.mnWidth - 1; // Take into account the leading sign that we'll need to write.
+			else if(fd.mbAlternativeForm && ((nBase == 2) || (nBase == 16)))
+				nDigitCountSum = fd.mnWidth - 2; // Take into account the leading "0x" that we'll need to write.
+			else
+				nDigitCountSum = fd.mnWidth;
+		}
+
+		// Write in any leading zeroes as required by the precision specifier(or the zero fill alignment option). 
+		while(nDigitCount < nDigitCountSum)
+		{
+			*--pCurrent = (CharT)'0';
+			++nDigitCount;
+		}
+
+		// Potentially add the sign prefix, which might be either nothing, '-', '+', or ' '.
+		if(nBase == 10) // Signs can only apply to decimal types.
+		{
+			if((fd.mnType == 'd') || (fd.mnType == 'i')) // The standard seems to say that only signed decimal types can have signs.
+			{
+				if(bNegative)
+					*--pCurrent = (CharT)'-';
+				else if(fd.mSign == kSignMinusPlus)
+					*--pCurrent = (CharT)'+';
+				else if(fd.mSign == kSignSpace)
+					*--pCurrent = (CharT)' ';
+			}
+		}
+		else if(fd.mbAlternativeForm && ((nBase == 2) || (nBase == 16)))
+		{
+			// Add the leading 0x, 0X, 0b, or 0B (for our binary extension).
+			*--pCurrent = (CharT)fd.mnType;
+			*--pCurrent = (CharT)'0';
+		}
+	}
+
+	return pCurrent;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteLong
+//
+// Writes the given lValue to the given buffer and returns the start of the 
+// data or returns NULL for error. Note that the buffer is passed in as the 
+// end of the buffer and not the beginning. This is a common trick used when
+// converting integer values to strings, as the conversion algorithm needs
+// to work backwards as it is and it's quicker to simply start with the end
+// of the buffer and move backwards.
+//
+template <typename CharT>
+CharT* WriteLong(const SprintfLocal::FormatData& fd, long lValue, CharT* EA_RESTRICT pBufferEnd)
+{
+	return WriteLongHelper<CharT, long, unsigned long>(fd, lValue, pBufferEnd);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteLongLong
+//
+// This function is identical to WriteLong except that it works with long long
+// instead of long. It is a separate function because on many platforms the
+// long long data type is larger than the (efficient) machine word size and is
+// thus to be avoided if possible.
+//
+template <typename CharT>
+CharT* WriteLongLong(const SprintfLocal::FormatData& fd, long long lValue, CharT* EA_RESTRICT pBufferEnd)
+{
+	return WriteLongHelper<CharT, long long, unsigned long long> (fd, lValue, pBufferEnd);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteDouble
+//
+template <typename CharT>
+CharT* WriteDouble(const SprintfLocal::FormatData& fd, double dValue, CharT* EA_RESTRICT pBufferEnd)
+{
+	using namespace SprintfLocal;
+
+	// Check for nan or inf strings.
+	if(IsNAN(dValue))
+	{
+		*--pBufferEnd = 0;
+		if(fd.mnType >= 'a') // If type is f, e, g, a, and not F, E, G, A.
+		{
+			*--pBufferEnd = 'n';
+			*--pBufferEnd = 'a';
+			*--pBufferEnd = 'n';
+		}
+		else
+		{
+			*--pBufferEnd = 'N';
+			*--pBufferEnd = 'A';
+			*--pBufferEnd = 'N';
+		}
+		if(IsNeg(dValue))
+			*--pBufferEnd = '-';
+		return pBufferEnd;
+	}
+	else if(IsInfinite(dValue))
+	{
+		*--pBufferEnd = 0;
+		if(fd.mnType >= 'a') // If type is f, e, g, a, and not F, E, G, A.
+		{
+			*--pBufferEnd = 'f';
+			*--pBufferEnd = 'n';
+			*--pBufferEnd = 'i';
+		}
+		else
+		{
+			*--pBufferEnd = 'F';
+			*--pBufferEnd = 'N';
+			*--pBufferEnd = 'I';
+		}
+		if(IsNeg(dValue))
+			*--pBufferEnd = '-';
+		return pBufferEnd;
+	}
+
+	// Regular processing.
+	int       nType = fd.mnType;
+	int       nPrecision = fd.mnPrecision;
+	bool      bStripTrailingZeroes = false;      // If true, then don't write useless trailing zeroes, a with the three at the end of: "1.23000"
+	bool      bStripPointlessDecimal = false;    // If true, then don't write a decimal point that has no digits after it, as with: "13."
+	CharT*    pResult = NULL;
+
+	*--pBufferEnd = 0;
+
+	if (nPrecision <= kConversionBufferSize) // If there is enough space for the data...
+	{
+		int      nDecimalPoint, nSign, nExponent;
+		CharT    pBufferCvt[kFcvtBufMaxSize]; pBufferCvt[0] = 0;
+		int      nBufferLength;
+		CharT*   pCurrent = pBufferEnd;
+
+		switch(nType)
+		{
+			default :
+			case 'g':
+			case 'G':
+			{
+				// From section 7.19.6.1.8:
+				// A double argument representing a floating-point number is converted in
+				// style f or e (or in style F or E in the case of a G conversion specifier), with
+				// the precision specifying the number of significant digits. If the precision is
+				// zero, it is taken as 1. The style used depends on the value converted; style e
+				// (or E) is used only if the exponent resulting from such a conversion is less
+				// than -4 or greater than or equal to the precision. Trailing zeros are removed
+				// from the fractional portion of the result unless the # flag is specified; a
+				// decimal-point character appears only if it is followed by a digit.
+				// 
+				// A double argument representing an infinity or NaN is converted in the style
+				// of an f or F conversion specifier.
+
+				// %g differs from %e how we pass nPrecision to EcvtBuf.
+				EcvtBuf(dValue, nPrecision, &nDecimalPoint, &nSign, pBufferCvt);
+				nExponent = nDecimalPoint - 1; // Exponent can be a positive, zero, or negative value.
+
+				if(!fd.mbAlternativeForm)
+					bStripTrailingZeroes = true;
+				bStripPointlessDecimal = true;
+
+				if(!((nExponent < -4) || (nExponent >= nPrecision)))
+				{
+					if(!IsSameType<CharT, char8_t>::value)
+					{
+						if(nExponent >= 0) // If there are any digits to the left of the decimal...
+							nPrecision -= (nExponent + 1);
+					}
+					goto FType;
+				}
+
+				if(nType == 'g')
+					nType = 'e';
+				else
+					nType = 'E';
+				goto EContinuation;
+			} // case g:
+
+			case 'e':
+			case 'E':
+			{
+				// From section 7.19.6.1.8:
+				// A double argument representing a floating-point number is converted in the
+				// style [-]d.ddd edd, where there is one digit (which is nonzero if the
+				// argument is nonzero) before the decimal-point character and the number of
+				// digits after it is equal to the precision; if the precision is missing, it is 
+				// taken as 6; if the precision is zero and the # flag is not specified, no decimal-point
+				// character appears. The value is rounded to the appropriate number of digits.
+				// The E conversion specifier produces a number with E instead of e
+				// introducing the exponent. The exponent always contains at least two digits,
+				// and only as many more digits as necessary to represent the exponent. If the
+				// value is zero, the exponent is zero.
+				// 
+				// A double argument representing an infinity or NaN is converted in the style
+				// of an f or F conversion specifier.
+
+				EcvtBuf(dValue, nPrecision + 1, &nDecimalPoint, &nSign, pBufferCvt);
+				nExponent = nDecimalPoint - 1; // Exponent can be a positive, zero, or negative value.
+				if(dValue == 0) // Should we instead compare dValue to a very small value?
+					nExponent = 0; // In this case we are working with the value 0, which is always displayed as 0.???e+00
+
+			EContinuation:
+				nBufferLength = (int)Strlen(pBufferCvt);
+
+				// Write the exponent digits, at least two of them.
+				int nExponentAbs = abs(nExponent);
+				while(nExponentAbs > 0)
+				{
+					*--pCurrent = (CharT)('0' + (nExponentAbs % 10));
+					nExponentAbs /= 10;
+				}
+				if(pCurrent >= (pBufferEnd - 1)) // The C99 standard states that at least two digits are present in the exponent, even if they are either or both zeroes.
+					*--pCurrent = '0';
+				if(pCurrent >= (pBufferEnd - 1))
+					*--pCurrent = '0';
+
+				// Write the decimal point sign, always + or -.
+				if(nExponent >= 0)
+					*--pCurrent = '+';
+				else
+					*--pCurrent = '-';
+
+				// Write 'e' or 'E'.
+				*--pCurrent = (CharT)nType;
+
+				// Write all digits but the first one.
+				for(CharT* pTemp = pBufferCvt + nBufferLength; pTemp > pBufferCvt + 1; )
+				{
+					const CharT c = *--pTemp;
+
+					if(c != '0') // As we move right to left, as soon as we encounter a non-'0', we are done with trialing zeroes.
+						bStripTrailingZeroes = false;
+					if((c != '0') || !bStripTrailingZeroes)
+						*--pCurrent = c;
+				}
+
+				// Write the decimal point.
+				if((*pCurrent != (CharT)nType) || !bStripPointlessDecimal) // If bStripPointlessDecimal is true, then draw decimal only if there are digits after it.
+				{
+					if((nBufferLength > 1) || fd.mbAlternativeForm) // If the 'alternative form' is set, then always show a decimal.
+						*--pCurrent = (CharT)fd.mDecimalPoint;
+				}
+
+				// Write the first digit.
+				*--pCurrent = pBufferCvt[0];
+				break;
+			} // case e:
+
+			case 'f':
+			case 'F':
+			FType:
+			{
+				// From section 7.19.6.1.8:
+				// A double argument representing a floating-point number is converted to
+				// decimal notation in the style [-]ddd.ddd, where the number of digits after
+				// the decimal-point character is equal to the precision specification. 
+				// If the precision is missing, it is taken as 6; if the precision is zero 
+				// and the # flag is not specified, no decimal-point character appears. 
+				// If a decimal-point character appears, at least one digit appears before it. 
+				// The value is rounded to the appropriate number of digits.
+				//
+				// A double argument representing an infinity is converted in one of the 
+				// styles [-]inf or [-]infinity  which style is implementation-defined. 
+				// A double argument representing a NaN is converted in one of the styles
+				// [-]nan or [-]nan(n-char-sequence)  which style, and the meaning of
+				// any n-char-sequence, is implementation-defined. The F conversion specifier
+				// produces INF, INFINITY, or NAN instead of inf, infinity, or nan,
+				// respectively.)
+
+				FcvtBuf(dValue, nPrecision, &nDecimalPoint, &nSign, pBufferCvt);
+				nBufferLength = (int)Strlen(pBufferCvt);
+
+				// If the 'alternative form' is set, then always show a decimal.
+				if(fd.mbAlternativeForm && (nDecimalPoint >= nBufferLength) && !bStripPointlessDecimal)
+					*--pCurrent = (CharT)fd.mDecimalPoint;
+
+				// Write the values that are after the decimal point.
+				const CharT* const pDecimalPoint = pBufferCvt + nDecimalPoint - 1;
+				const CharT*       pCurrentSource = pBufferCvt + nBufferLength - 1;
+
+				if((pCurrentSource - pDecimalPoint) > nPrecision) // If dValue is very small, this may be true.
+					pCurrentSource = pDecimalPoint + nPrecision;
+
+				while(pCurrentSource > pDecimalPoint)
+				{
+					CharT c;
+
+					if((pCurrentSource >= pBufferCvt) && (pCurrentSource <= (pBufferCvt + nBufferLength)))
+						c = *pCurrentSource;
+					else
+						c = '0';
+
+					if(c != '0') // As we move right to left, as soon as we encounter a non-'0', we are done with trialing zeroes.
+						bStripTrailingZeroes = false;
+
+					if((c != '0') || !bStripTrailingZeroes)
+						*--pCurrent = c;
+					--pCurrentSource;
+				}
+
+				// Write the decimal point.
+				if(*pCurrent || !bStripPointlessDecimal) // If bStripPointlessDecimal is true, then draw decimal only if there is something after it.
+				{
+					if(nDecimalPoint < nBufferLength) // The standard specifies to not write the decimal point if the precision is zero. 
+						*--pCurrent = (CharT)fd.mDecimalPoint;
+				}
+
+				// Write the values that are before the decimal point.
+				if(nDecimalPoint > 0) // If there is any integral part to this number...
+				{
+					int nDigitCount = 0;
+					pCurrentSource = pBufferCvt + nDecimalPoint;
+
+					while(pCurrentSource > pBufferCvt)
+					{
+						*--pCurrent = *--pCurrentSource;
+						++nDigitCount;
+
+						if((fd.mbDisplayThousands) && (pCurrentSource > pBufferCvt) && ((nDigitCount % 3) == 0))
+							*--pCurrent = (CharT)fd.mThousandsSeparator;
+					}
+				}
+				else
+					*--pCurrent = '0'; // Write the "0" before the decimal point, as in "0.1234".
+				break;
+			} // case f:
+		} // switch
+
+		// Write a sign character.
+		if(nSign)
+			*--pCurrent = '-';
+		else if(fd.mSign == kSignMinusPlus)
+			*--pCurrent = '+';
+		else if(fd.mSign == kSignSpace)
+			*--pCurrent = ' ';
+
+		// Write leading spaces.
+		if(fd.mAlignment == kAlignmentRight)
+		{
+			// Write in any leading spaces as required by the width specifier (or the zero fill alignment option). 
+			int nWidth = (int)(intptr_t)(pBufferEnd - pCurrent);
+
+			while(nWidth < fd.mnWidth)
+			{
+				*--pCurrent = (CharT)' ';
+				++nWidth;
+			}
+		}
+
+		pResult = pCurrent;
+	}
+
+	return pResult;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// VprintfCore
+//
+
+template <typename CharT>
+int VprintfCoreInternal(int(*pWriteFunction)(const CharT* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs), void* EA_RESTRICT pWriteFunctionContext, const CharT* EA_RESTRICT pFormat, va_list arguments)
+{
+	using namespace SprintfLocal;
+
+	const CharT*        pFormatCurrent = pFormat;   // Current position within entire format string.
+	const CharT*        pFormatSpec;                // Current format specification (e.g. "%3.2f").
+	FormatData          fd;
+	int                 nWriteCount;
+	int                 nWriteCountSum = 0;
+	int                 nWriteCountCurrent;
+	CharT               pBuffer[kConversionBufferSize + 1]; // The +1 isn't necessary but placates code analysis tools.
+	CharT* const        pBufferEnd = pBuffer + kConversionBufferSize;
+	const CharT*        pBufferData = NULL;       // Where within pBuffer8 the data we are interested in begins.
+	long                lValue = 0;                // All known supported platforms have 'long' support in hardware. 'int' is always only 32 bits (even on 64 bit systems).
+	unsigned long       ulValue = 0;               // 
+	long long           llValue = 0;               // Most compilers support 'long long' at this point. VC++ v6 and earlier are notable exceptions.
+	unsigned long long  ullValue = 0;              // 
+
+	pWriteFunction(NULL, 0, pWriteFunctionContext, kWFSBegin);
+
+	// We walk through the format string and echo characters to the output until we 
+	// come across a % specifier, at which point we process it and then move on as before.
+	while(*pFormatCurrent)
+	{
+		// Find the next format specification (or end of the string).
+		pFormatSpec = pFormatCurrent;
+		while(*pFormatSpec && (*pFormatSpec != '%'))
+			++pFormatSpec;
+
+		// Write out non-formatted text
+		nWriteCount = (int)(pFormatSpec - pFormatCurrent);
+		if(nWriteCount)
+		{
+			if(pWriteFunction(pFormatCurrent, (size_t)nWriteCount, pWriteFunctionContext, kWFSIntermediate) == -1)
+				goto FunctionError; // This is an error; not the same as running out of space.
+			nWriteCountSum += nWriteCount;
+			pFormatCurrent = pFormatSpec;
+		}
+
+		if(*pFormatSpec)
+		{
+			pFormatCurrent = ReadFormat(pFormatSpec, &fd, VA_LIST_REFERENCE(arguments));
+
+			switch(fd.mnType)
+			{
+				case 'd':    // These are signed values.
+				case 'i':    // The standard specifies that 'd' and 'i' are identical.
+				{
+					if(fd.mModifier == kModifierLongLong)
+						llValue = va_arg(arguments, long long); // If the user didn't pass a long long, unexpected behaviour will occur.
+					else if((fd.mModifier == kModifierLong) || (fd.mModifier == kModifierLongDouble)) // It is questionable whether we should allow %Ld here as we do. The standard doesn't define this behaviour.
+						lValue = va_arg(arguments, long);
+					else if(fd.mModifier == kModifierInt64)
+					{
+						if(sizeof(int64_t) == sizeof(long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							lValue = va_arg(arguments, long);
+						}
+						else if(sizeof(int64_t) == sizeof(long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							llValue = va_arg(arguments, long long);
+						}
+					}
+					else if (fd.mModifier == kModifierMax_t)
+					{
+						if (sizeof(intmax_t) == sizeof(long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							lValue = va_arg(arguments, long);
+						}
+						else if (sizeof(intmax_t) == sizeof(long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							llValue = va_arg(arguments, long long);
+						}
+					}
+					else if (fd.mModifier == kModifierSize_t)
+					{
+						if (sizeof(size_t) == sizeof(long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							lValue = (long) va_arg(arguments, unsigned long);
+						}
+						else if (sizeof(size_t) == sizeof(long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							llValue = (long) va_arg(arguments, unsigned long long);
+						}
+					}
+					else if (fd.mModifier == kModifierPtrdiff_t)
+					{
+						if (sizeof(ptrdiff_t) == sizeof(long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							lValue = va_arg(arguments, long);
+						}
+						else if (sizeof(ptrdiff_t) == sizeof(long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							llValue = va_arg(arguments, long long);
+						}
+					}
+					else if(fd.mModifier == kModifierInt128)
+					{
+						if(sizeof(int64_t) < sizeof(long long)) // If long long is 128 bits... (we don't test sizeof(int128_t) because there may be no such thing. Hopefully there is no int96_t.
+							llValue = va_arg(arguments, long long);
+						else
+						{
+							// We have a problem here. The user wants to print a 128 bit value but 
+							// there is no built-in type to support this. For the time being, we 
+							// simply use only 64 bits of data. If we really need this, we can
+							// add the functionality later. We have the EAStdC int128_t type we can use.
+							// I don't think calling two 64 bit va_args is the same as what a single
+							// 128 bit arg would be. If we are using EAStdC int128_t then we handle the 
+							// value the same as passing a struct by value. And that's compiler/ABI-specific.
+							llValue = va_arg(arguments, long long);
+							llValue = va_arg(arguments, long long);
+						}
+					}
+					else // else we have kModifierChar, kModifierShort, kModifierInt8, kModifierInt16, kModifierInt32.
+					{
+						// COMPILE_TIME_ASSERT(sizeof(int32_t) <= sizeof(int));
+						lValue = va_arg(arguments, int);
+						if((fd.mModifier == kModifierShort) || (fd.mModifier == kModifierInt16))
+							lValue = (long)(signed short)lValue; // We carefully do our casting here in order to preserve intent.
+						else if((fd.mModifier == kModifierChar) || (fd.mModifier == kModifierInt8))
+							lValue = (long)(signed char)lValue;  // We carefully do our casting here in order to preserve intent.
+					}
+
+					if(fd.mModifier == kModifierLongLong)
+					{
+						pBufferData = WriteLongLong(fd, llValue, pBufferEnd);
+						if(!pBufferData)
+							goto FormatError;
+					}
+					else
+					{
+						pBufferData = WriteLong(fd, lValue, pBufferEnd);
+						if(!pBufferData)
+							goto FormatError;
+					}
+
+					nWriteCount = (int)((pBufferEnd - pBufferData) - 1); // -1 because the written string is 0-terminated and we don't want to write the final 0.
+					break;
+				}
+
+				case 'b': // 'b' means binary. This is a convenient extension that we provide.
+				case 'o': // These are unsigned values.
+				case 'u':
+				case 'x':
+				case 'X':
+				{
+					if(fd.mModifier == kModifierLong)
+						ulValue = va_arg(arguments, unsigned long);
+					else if(fd.mModifier == kModifierLongLong)
+						ullValue = va_arg(arguments, unsigned long long);
+					else if(fd.mModifier == kModifierInt64)
+					{
+						if(sizeof(uint64_t) == sizeof(unsigned long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							ulValue = va_arg(arguments, unsigned long);
+						}
+						else if(sizeof(uint64_t) == sizeof(unsigned long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							ullValue = va_arg(arguments, unsigned long long);
+						}
+					}
+					else if (fd.mModifier == kModifierMax_t)
+					{
+						if (sizeof(uintmax_t) == sizeof(unsigned long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							ulValue = va_arg(arguments, unsigned long);
+						}
+						else if (sizeof(uintmax_t) == sizeof(unsigned long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							ullValue = va_arg(arguments, unsigned long long);
+						}
+					}
+					else if (fd.mModifier == kModifierSize_t)
+					{
+						if (sizeof(size_t) == sizeof(unsigned long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							ulValue = va_arg(arguments, unsigned long);
+						}
+						else if (sizeof(size_t) == sizeof(unsigned long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							ullValue = va_arg(arguments, unsigned long long);
+						}
+					}
+					else if (fd.mModifier == kModifierPtrdiff_t)
+					{
+						if (sizeof(ptrdiff_t) == sizeof(unsigned long))
+						{
+							// fd.mModifier == kModifierLong; -- Not necessary, as the logic below doesn't need this.
+							ulValue = (unsigned long long) va_arg(arguments, long);
+						}
+						else if (sizeof(ptrdiff_t) == sizeof(unsigned long long))
+						{
+							fd.mModifier = kModifierLongLong;
+							ullValue = (unsigned long long) va_arg(arguments, long long);
+						}
+					}
+					else if(fd.mModifier == kModifierInt128)
+					{
+						if(sizeof(uint64_t) < sizeof(unsigned long long)) // If long long is 128 bits... (we don't test sizeof(int128_t) because there may be no such thing. Hopefully there is no int96_t.
+							ullValue = va_arg(arguments, unsigned long long);
+						else
+						{
+							// We have a problem here. The user wants to print a 128 bit value but 
+							// there is no built-in type to support this. For the time being, we 
+							// simply use only 64 bits of data. If we really need this, we can
+							// add the functionality later.
+							#ifdef EA_SYSTEM_BIG_ENDIAN
+									 (void)va_arg(arguments, unsigned long long);
+								ullValue = va_arg(arguments, unsigned long long);
+							#else
+								ullValue = va_arg(arguments, unsigned long long);
+									 (void)va_arg(arguments, unsigned long long);
+							#endif
+						}
+					}
+					else // else we have kModifierChar, kModifierShort, kModifierInt8, kModifierInt16, kModifierInt32.
+					{
+						ulValue = va_arg(arguments, unsigned int);
+						if((fd.mModifier == kModifierShort) || (fd.mModifier == kModifierInt16))
+							ulValue = (unsigned long)(unsigned short)ulValue; // We carefully do our casting here in order to preserve intent.
+						else if((fd.mModifier == kModifierChar) || (fd.mModifier == kModifierInt8))
+							ulValue = (unsigned long)(unsigned char)ulValue;  // We carefully do our casting here in order to preserve intent.
+					}
+
+					// Now do the actual writing of the data.
+					if(fd.mModifier == kModifierLongLong)
+					{
+						pBufferData = WriteLongLong(fd, (long long)ullValue, pBufferEnd);
+						if(!pBufferData)
+							goto FormatError;
+					}
+					else
+					{
+						pBufferData = WriteLong(fd, (long)ulValue, pBufferEnd);
+						if(!pBufferData)
+							goto FormatError;
+					}
+
+					nWriteCount = (int)((pBufferEnd - pBufferData) - 1); // -1 because the written string is 0-terminated and we don't want to write the final 0.
+					break;
+				}
+
+				case 'e':
+				case 'E':
+				case 'f':
+				case 'F':
+				case 'g':
+				case 'G':
+				case 'a': // See the C99 standard, section 7.19.6.1.8, for details on 'a' formatting.
+				case 'A':
+				{
+					// Since on most systems long double is the same as double, it's really no big deal to just work
+					// with long double, much like we do with long int instead of int above.
+					if(fd.mModifier == kModifierLongDouble)
+					{
+						long double ldValue = va_arg(arguments, long double);
+						pBufferData = WriteDouble(fd, static_cast<double>(ldValue), pBufferEnd);
+					}
+					else
+					{
+						double dValue = va_arg(arguments, double);
+						pBufferData = WriteDouble(fd, dValue, pBufferEnd);
+					}
+
+					if(!pBufferData)
+						goto FormatError;
+
+					nWriteCount = (int)((pBufferEnd - pBufferData) - 1); // -1 because the written string is 0-terminated and we don't want to write the final 0.
+					break;
+				}
+
+				case 's':
+				case 'S':
+				{
+					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.
+							goto FormatError;
+					}
+
+					switch (stringTypeSize)
+					{
+						case 1:
+						{
+							const char8_t* pBufferData8 = va_arg(arguments, char8_t*);
+							nWriteCount = StringFormat<char8_t, CharT>(pWriteFunction, pWriteFunctionContext, fd, pBuffer, pBufferData8);
+							if (nWriteCount < 0)
+								goto FormatError;
+							nWriteCountSum += nWriteCount;
+							break;
+						} // case 1
+
+						case 2:
+						{
+							const char16_t* pBufferData16 = va_arg(arguments, char16_t*);
+							nWriteCount = StringFormat<char16_t, CharT>(pWriteFunction, pWriteFunctionContext, fd, pBuffer, pBufferData16);
+							if (nWriteCount < 0)
+								goto FormatError;
+							nWriteCountSum += nWriteCount;
+							break;
+						} // case 2
+
+						case 4:
+						{
+							const char32_t* pBufferData32 = va_arg(arguments, char32_t*);
+							nWriteCount = StringFormat<char32_t, CharT>(pWriteFunction, pWriteFunctionContext, fd, pBuffer, pBufferData32);
+							if (nWriteCount < 0)
+								goto FormatError;
+							nWriteCountSum += nWriteCount;
+							break;
+						} // case 4
+
+					} // switch (stringTypeSize)
+
+					continue; // Skip the appending of the buffer.  This is being done inside the StringFormat routine
+				} // case 's'
+
+				case 'n': // %n %hn %ln %lln %I64n %I32n %I16n, %I8n, %jn, %tn, %zn etc.
+				{
+					// In this case, we write the number of chars written so far to the passed in argument.
+					void* pCountBufferData = va_arg(arguments, void*);
+
+					switch (fd.mModifier)
+					{
+						case kModifierInt8:
+						case kModifierChar:
+							*(char8_t*)pCountBufferData = (char8_t)nWriteCountSum;
+							break;
+
+						case kModifierInt16:
+						case kModifierShort:
+							*(int16_t*)pCountBufferData = (int16_t)nWriteCountSum;
+							break;
+
+						case kModifierInt32:
+							*(int32_t*)pCountBufferData = (int32_t)nWriteCountSum;
+							break;
+
+						case kModifierInt64:
+							*(int64_t*)pCountBufferData = (int32_t)nWriteCountSum;
+							break;
+
+						case kModifierLong:
+							*(long*)pCountBufferData = (long)nWriteCountSum;
+							break;
+
+						case kModifierLongLong:
+							*(long long*)pCountBufferData = (long long)nWriteCountSum;
+							break;
+
+						case kModifierPtrdiff_t:
+							*(ptrdiff_t*)pCountBufferData = (ptrdiff_t)nWriteCountSum;
+							break;
+
+						case kModifierSize_t:
+							*(size_t*)pCountBufferData = (size_t)nWriteCountSum;
+							break;
+
+						case kModifierMax_t:
+							*(intmax_t*)pCountBufferData = (intmax_t)nWriteCountSum;
+							break;
+
+							//case kModifierInt128:       // We really should generate an error with this. It's nearly pointless to support it.
+							//    // Fall through
+							//
+							//case kModifierWChar:        // This should be impossible to encounter.
+							//case kModifierLongDouble:   // This should be impossible to encounter.
+							//    // Fall through
+
+						case kModifierNone:
+						default:
+							*(int*)pCountBufferData = (int)nWriteCountSum;
+							break;
+					}
+					continue; // Intentionally continue instead break.
+				} // case 'n'
+
+				case 'c':
+				case 'C':
+				{
+					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.
+							goto FormatError;
+					}
+
+					// In some cases, we are losing data here or doing an improper or incomplete encoding conversion.
+					switch (charTypeSize)
+					{
+					case 1:
+					{
+						const char8_t c8 = (char8_t)va_arg(arguments, int); // We make the assumption here that sizeof(char16_t) is <= sizeof(int) and thus that a char16_t argument was promoted to int.
+						pBuffer[0] = (CharT)(uint32_t)c8;
+						pBufferData = pBuffer;
+						nWriteCount = 1;
+						break;
+					}
+					case 2:
+					{
+						const char16_t c16 = (char16_t)va_arg(arguments, unsigned int);
+						pBuffer[0] = (CharT)(uint32_t)c16;
+						pBufferData = pBuffer;
+						nWriteCount = 1;
+						break;
+					}
+					case 4:
+					{
+						const char32_t c32 = (char32_t)va_arg(arguments, unsigned int);
+						pBuffer[0] = (CharT)(uint32_t)c32; 
+						pBufferData = pBuffer;
+						nWriteCount = 1;
+						break;
+					}
+					}
+
+					break;
+				} // case 'c'
+
+				case '%':
+				{
+					// In this case we just write a single '%' char8_t to the output.
+					pBuffer[0] = '%';
+					pBufferData = pBuffer;
+					nWriteCount = 1;
+					break;
+				}
+
+				case kFormatError:
+				default:
+				FormatError:
+					// We do what many printf implementations do here and simply print out the text
+					// as-is -- as if there wasn't any formatting specifier. This at least lets the
+					// user see what was (incorrectly) specified.
+					nWriteCount = (int)(pFormatCurrent - pFormatSpec);
+					nWriteCountSum += nWriteCount;
+
+					if(nWriteCount && (pWriteFunction(pFormatSpec, (size_t)nWriteCount, pWriteFunctionContext, kWFSIntermediate) == -1))
+						goto FunctionError; // This is an error; not the same as running out of space.
+					continue; // Try to continue displaying further data.
+			}
+
+			nWriteCountCurrent = WriteBuffer(pWriteFunction, pWriteFunctionContext, fd, pBufferData, nWriteCount);
+			if (nWriteCountCurrent < 0)
+				goto FunctionError; // This is an error; not the same as running out of space.
+
+			nWriteCountSum += nWriteCountCurrent;
+
+		} // if(*pFormatSpec)
+
+	} // while(*pFormatCurrent)
+
+	pWriteFunction(NULL, 0, pWriteFunctionContext, kWFSEnd);
+	return nWriteCountSum;
+
+FunctionError:
+	pWriteFunction(NULL, 0, pWriteFunctionContext, kWFSEnd);
+	return -1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// VprintfCore
+//
+int VprintfCore(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pWriteFunctionContext8, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return VprintfCoreInternal(pWriteFunction8, pWriteFunctionContext8, pFormat, arguments);
+}
+
+int VprintfCore(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pWriteFunctionContext16, const char16_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return VprintfCoreInternal(pWriteFunction16, pWriteFunctionContext16, pFormat, arguments);
+}
+
+int VprintfCore(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pWriteFunctionContext32, const char32_t* EA_RESTRICT pFormat, va_list arguments)
+{
+	return VprintfCoreInternal(pWriteFunction32, pWriteFunctionContext32, pFormat, arguments);
+}
+
+
+} // namespace SprintfLocal
+} // namespace StdC
+} // namespace EA
+
+
+// For bulk build friendliness, undef all local #defines.
+#undef IsNeg
+#undef EASPRINTF_MIN
+#undef EASPRINTF_MAX
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+
+
+
+

+ 1560 - 0
source/EASprintfOrdered.cpp

@@ -0,0 +1,1560 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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
+
+
+
+
+
+
+

+ 44 - 0
source/EAStdC.cpp

@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAStdC.h>
+#include <EAStdC/internal/SprintfCore.h>
+
+
+namespace EA
+{
+	namespace StdC
+	{
+		EASTDC_API void Init()
+		{
+			SprintfLocal::EASprintfInit();
+		}
+
+		EASTDC_API void Shutdown()
+		{
+			SprintfLocal::EASprintfShutdown();
+		}
+
+
+		// Disabled by default, for compatibility with C99 behavior.
+		bool gAssertionsEnabled = false;
+
+		EASTDC_API void SetAssertionsEnabled(bool enabled)
+		{
+			gAssertionsEnabled = enabled;
+		}
+
+		EASTDC_API bool GetAssertionsEnabled()
+		{
+			return gAssertionsEnabled;
+		}
+	}
+}
+
+
+
+
+
+

+ 723 - 0
source/EAStopwatch.cpp

@@ -0,0 +1,723 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Implements a stopwatch-style stopwatch. This is useful for both benchmarking
+// and for implementing runtime timing.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAStopwatch.h>
+#include <EAAssert/eaassert.h>
+#if   defined(EA_PLATFORM_MICROSOFT)
+	#pragma warning(push, 0)
+	#include <Windows.h>
+	#pragma warning(pop)
+	#if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE)
+		EA_DISABLE_ALL_VC_WARNINGS()
+		#include <chrono>
+		#include <thread>
+		EA_RESTORE_ALL_VC_WARNINGS()
+	#endif
+#elif defined(EA_PLATFORM_SONY)
+	#include <kernel.h>
+#elif defined(EA_PLATFORM_POSIX)
+	#include <unistd.h>
+#endif
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Auto-setup code
+//
+// The code below is for setting up the stopwatch system, and it's needed
+// because it allows us to know at runtime what the stopwatch frequency is
+// without having to spend CPU cycles repeatedly calculating it at runtime.
+// The code below is set up to auto-execute on startup when possible, but 
+// it's OK if it doesn't because the Stopwatch constructor auto-checks for
+// initialization and calls if it not already. However, the auto code below
+// is still useful because it's better to get this taken care of on startup
+// rather than unexpectedly during runtime (as it takes a few milliseconds 
+// on some platforms).
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(_MSC_VER)
+	#ifndef EASTDC_INIT_SEG_DEFINED
+		#define EASTDC_INIT_SEG_DEFINED 
+		// Set initialization order between init_seg(compiler) (.CRT$XCC) and
+		// init_seg(lib) (.CRT$XCL). The MSVC linker sorts the .CRT sections
+		// alphabetically so we simply need to pick a name that is between
+		// XCC and XCL. This works on both Windows and XBox.
+		#pragma warning(disable: 4075) // "initializers put in unrecognized initialization area"
+		#pragma init_seg(".CRT$XCF")
+	#endif
+
+#elif defined(__GNUC__)
+	// By adding the 'constructor' attribute to this function, we tell GCC 
+	// to have this function be called on startup before main(). We have the 
+	// AutoStopwatchSetup mechanism below, but GCC ignores it because the 
+	// object isn't externally reference and GCC thinks it can thus be eliminated.
+	void EAStdCStopwatchSetupCoefficients();
+	void EAStdCStopwatchSetup() __attribute__ ((constructor));
+	void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency);
+
+#else
+	// Some compilers require these functions to be pre-declared.
+	void EAStdCStopwatchSetupCoefficients();
+	void EAStdCStopwatchSetup();
+	void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency);
+
+#endif
+
+
+
+
+
+
+// Anonymous namespace
+// Has the effect of making things declared within it be static, but with some improvements.
+namespace 
+{
+	// Stopwatch cycle metrics
+	uint64_t mnStopwatchFrequency(1); // Set to one to prevent possible div/0 errors.
+	float    mfStopwatchCyclesToNanosecondsCoefficient;
+	float    mfStopwatchCyclesToMicrosecondsCoefficient;
+	float    mfStopwatchCyclesToMillisecondsCoefficient;
+	float    mfStopwatchCyclesToSecondsCoefficient;
+	float    mfStopwatchCyclesToMinutesCoefficient;
+
+	// CPU cycle metrics
+	uint64_t mnCPUFrequency(1); // Set to one to prevent possible div/0 errors.
+	float    mfCPUCyclesToNanosecondsCoefficient;
+	float    mfCPUCyclesToMicrosecondsCoefficient;
+	float    mfCPUCyclesToMillisecondsCoefficient;
+	float    mfCPUCyclesToSecondsCoefficient;
+	float    mfCPUCyclesToMinutesCoefficient;
+
+	#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+		uint64_t mnCPUCycleReadingOverhead(0);
+		uint64_t mnStopwatchCycleReadingOverhead(0);
+	#endif
+
+	#if defined(EA_PLATFORM_MICROSOFT)
+		void EAStdCThreadSleep(int ms)
+		{
+			#if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE)
+				std::chrono::milliseconds duration(ms);
+				std::this_thread::sleep_for(duration);
+			#else
+				::SleepEx((DWORD)ms, TRUE);
+			#endif
+		}
+	#endif
+}
+
+
+
+void EAStdCStopwatchSetupCoefficients()
+{
+	// Calculate coefficients.
+	mfStopwatchCyclesToMinutesCoefficient      = 1.f / 60.f   / (int64_t)mnStopwatchFrequency;    // Some compilers require the 
+	mfStopwatchCyclesToSecondsCoefficient      = 1.f          / (int64_t)mnStopwatchFrequency;    // conversion to int64_t for the math.
+	mfStopwatchCyclesToMillisecondsCoefficient = 1000.f       / (int64_t)mnStopwatchFrequency;
+	mfStopwatchCyclesToMicrosecondsCoefficient = 1000000.f    / (int64_t)mnStopwatchFrequency;
+	mfStopwatchCyclesToNanosecondsCoefficient  = 1000000000.f / (int64_t)mnStopwatchFrequency;
+
+	#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+		// Here we make a rough measurement of the start and stop overhead of
+		// the stopwatch code. It is hard to say what a good way to determine this is,
+		// as the runtime use of the stopwatch will actually cause the overhead to 
+		// vary somewhat between uses. It is perhaps even debateable whether we
+		// should even be attempting to do such overhead calculations.
+		uint64_t nCurrentStopwatchCycleValue1;
+		uint64_t nCurrentStopwatchCycleValue2;
+		uint64_t nLowestStopwatchCycleReadingOverhead(UINT64_MAX);
+		uint64_t nCurrentStopwatchCycleReadingOverhead;
+
+		for(int t(0); t < 8; t++)
+		{
+			nCurrentStopwatchCycleValue1 = EA::StdC::Stopwatch::GetStopwatchCycle();
+			nCurrentStopwatchCycleValue2 = EA::StdC::Stopwatch::GetStopwatchCycle();
+			nCurrentStopwatchCycleReadingOverhead = nCurrentStopwatchCycleValue2 - nCurrentStopwatchCycleValue1;
+			if(nLowestStopwatchCycleReadingOverhead > nCurrentStopwatchCycleReadingOverhead)
+				nLowestStopwatchCycleReadingOverhead = nCurrentStopwatchCycleReadingOverhead;
+		}
+		mnStopwatchCycleReadingOverhead = nLowestStopwatchCycleReadingOverhead;
+	#endif
+
+	// CPU Frequencies
+	mfCPUCyclesToMinutesCoefficient      = 1.f / 60.f   / (int64_t)mnCPUFrequency;
+	mfCPUCyclesToSecondsCoefficient      = 1.f          / (int64_t)mnCPUFrequency;
+	mfCPUCyclesToMillisecondsCoefficient = 1000.f       / (int64_t)mnCPUFrequency;
+	mfCPUCyclesToMicrosecondsCoefficient = 1000000.f    / (int64_t)mnCPUFrequency;
+	mfCPUCyclesToNanosecondsCoefficient  = 1000000000.f / (int64_t)mnCPUFrequency;
+
+	#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+		uint64_t nCurrentCPUCycleValue1, nCurrentCPUCycleValue2;
+		uint64_t nLowestCPUCycleReadingOverhead(UINT64_MAX);
+		uint64_t nCurrentCPUCycleReadingOverhead;
+
+		for(int c = 0; c < 8; c++)
+		{
+			nCurrentCPUCycleValue1          = EA::StdC::Stopwatch::GetCPUCycle();
+			nCurrentCPUCycleValue2          = EA::StdC::Stopwatch::GetCPUCycle();
+			nCurrentCPUCycleReadingOverhead = nCurrentCPUCycleValue2 - nCurrentCPUCycleValue1;
+
+			if(nLowestCPUCycleReadingOverhead > nCurrentCPUCycleReadingOverhead)
+				nLowestCPUCycleReadingOverhead = nCurrentCPUCycleReadingOverhead;
+		}
+
+		mnCPUCycleReadingOverhead = nLowestCPUCycleReadingOverhead;
+	#endif
+}
+
+
+void EAStdCStopwatchSetup()
+{
+	if(mnStopwatchFrequency <= 1) // If we haven't already calculated this...
+	{
+		#if defined(EA_PLATFORM_SONY)
+			// According to Sony: A time stamp counter exists for each CPU core, but the frequency is the same 
+			// value for all CPU cores. This frequency will not change during the lifetime of a process.
+			mnCPUFrequency       = sceKernelGetProcessTimeCounterFrequency();
+			mnStopwatchFrequency = mnCPUFrequency;
+
+		#elif defined(__APPLE__)
+			mach_timebase_info_data_t timebaseInfo;
+		
+			mach_timebase_info(&timebaseInfo);
+			mnCPUFrequency       = (UINT64_C(1000000000) * (uint64_t)timebaseInfo.denom) / (uint64_t)timebaseInfo.numer;
+
+			////////////////////////////////////////////////////
+			// Stopwatch Frequency
+			mnStopwatchFrequency = mnCPUFrequency;
+
+		#elif defined(EA_PLATFORM_CAPILANO)
+			// Microsoft has stated (https://forums.xboxlive.com/AnswerPage.aspx?qid=c3fcdad5-f3e4-46d9-85f9-d337506f0d6b&tgt=1) that  
+			// QueryPerformanceCounter / QueryPerformanceFrequency map to rdtsc and they are stable throughout the life of the process.
+			// Thus we can use QueryPerformanceFrequency to portable tell the CPU frequency for our usage of rdtsc.
+			QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&mnStopwatchFrequency));
+			mnCPUFrequency = mnStopwatchFrequency;
+
+		#elif defined(EA_PLATFORM_MICROSOFT)
+			// On Windows, the only way to tell the CPU-based timer frequency is to manually
+			// time it. There is no function in Windows which tells you this information. 
+			// Indeed such a function might not be useful for CPU frequency-switching systems.
+
+			// SetPriorityClass / SetThreadPriority APIs not available for Windows 8 Metro apps
+			#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+			HANDLE process           = ::GetCurrentProcess();
+			DWORD  oldPriorityClass  = ::GetPriorityClass(process);
+			HANDLE thread            = ::GetCurrentThread();
+			int    oldThreadPriority = ::GetThreadPriority(thread);
+
+			// Set process/thread priorities.
+			::SetPriorityClass(process, REALTIME_PRIORITY_CLASS);
+			::SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);
+			#endif
+
+
+			////////////////////////////////////////////////////
+			// Stopwatch Frequency
+				////////////////////////////////////////////////////
+				// CPU Frequency
+				// Unfortunately, you can't call Win32 QueryPerformanceFrequency and
+				// expect that it will give you the processor frequency in megahertz.
+				// Doing such a thing would work for some CPUs and not others. In fact, 
+				// under Windows 2000, it works for my PIII-733 but not for my PII-300.
+				// So what we do instead is simply find the ratio of the QueryPerformanceCounter
+				// and our Stopwatch::GetCPUCycle functions. 
+				mnCPUFrequency = 0;
+				for(int i(0); i < 5; i++) // Give N chances at this, in case OS pre-emption occurs.
+				{
+					uint64_t nQPCCounter; ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter);
+					uint64_t nCPUCounter = EA::StdC::Stopwatch::GetCPUCycle();
+
+					double dRatio = (double)nCPUCounter / (double)nQPCCounter;
+
+					if((dRatio > 0.98 && dRatio < 1.02))
+					{
+						::QueryPerformanceFrequency((LARGE_INTEGER*)&mnCPUFrequency);
+						break;
+					}
+				}
+
+				if(!mnCPUFrequency)
+				{
+					// Do our own manual timing of clock ticks.
+					// We use Win32 QueryPerformanceCounter and QueryPerformanceFrequency to 
+					// calibrate our CPU cycle counter.
+					uint64_t nQPFrequency, nQPCCounter1, nQPCCounter2;
+					uint64_t nCPUCounter1, nCPUCounter2=0;
+					double  dQPCSeconds(0);
+
+					::QueryPerformanceFrequency((LARGE_INTEGER*)&nQPFrequency);
+					::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter1);
+					nCPUCounter1 = EA::StdC::Stopwatch::GetCPUCycle();
+
+					#if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+						static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.300;
+					#else
+						// Given that ticks are considered to be unreliable we can tolerate lower accuracy measurement
+						// of number of ticks per second.  https://en.wikipedia.org/wiki/Time_Stamp_Counter
+						// Here the largest limitation will be the MS Perf counter accuracy which is less than 1us.
+						static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.005;
+					#endif
+
+					while(dQPCSeconds < TIME_IN_SECONDS_TO_MEASURE_TICKS)
+					{
+						::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter2);
+						nCPUCounter2 = EA::StdC::Stopwatch::GetCPUCycle();
+						dQPCSeconds  = (nQPCCounter2 - nQPCCounter1) / (double)nQPFrequency;
+					}
+
+					mnCPUFrequency = (uint64_t)((nCPUCounter2 - nCPUCounter1) / dQPCSeconds);
+				}
+
+			#if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+				mnStopwatchFrequency = mnCPUFrequency;
+			#else
+				::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency);
+			#endif
+
+			// Reset process/thread priorities.
+			#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+			::SetPriorityClass(process, oldPriorityClass);
+			::SetThreadPriority(thread, oldThreadPriority);
+			#endif
+		#else
+
+			////////////////////////////////////////////////////
+			// CPU Frequency
+			#ifdef EASTDC_CPU_FREQ_CALCULATED
+				// On Unix, the only way to tell the CPU-based timer frequency is to manually
+				// time it. There is no function in Unix which tells you this information. 
+				// Indeed such a function might not be useful for CPU frequency-switching systems.
+
+				uint64_t nTimeCounter1 = EA::StdC::Stopwatch::GetStopwatchCycle();
+				uint64_t nCPUCounter1  = EA::StdC::Stopwatch::GetCPUCycle();
+
+				usleep(250000); // Sleep for a ~quarter second.
+
+				uint64_t nCPUCounter2   = EA::StdC::Stopwatch::GetCPUCycle();
+				uint64_t nTimeCounter2  = EA::StdC::Stopwatch::GetStopwatchCycle();
+				uint64_t nTimeDeltaUs   = nTimeCounter2 - nTimeCounter1;
+				uint64_t nCPUDeltaTicks = nCPUCounter2 - nCPUCounter1;
+
+				// GetStopwatchCycle will have varying resolution so we need to account for that accordingly
+				#if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+					mnCPUFrequency = (nCPUDeltaTicks * 1000000000 / nTimeDeltaUs); // We are using clock_gettime, which works in nanoseconds.
+				#elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
+					mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs); // We are using gettimeofday, which works in microseconds.
+				#else
+					mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs);
+				#endif
+
+				#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+					// To do. Slightly less accuracy without this implemented.
+				#endif
+
+			#elif EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+				mnCPUFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds.
+
+			#elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
+				mnCPUFrequency = 1000000;              // We are using gettimeofday, which works in microseconds.
+			#else
+				#error
+				mnCPUFrequency = 1;
+			#endif
+
+			////////////////////////////////////////////////////
+			// Stopwatch Frequency
+			#if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
+				mnStopwatchFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds.
+			#elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
+				mnStopwatchFrequency = 1000000;              // We are using gettimeofday, which works in microseconds.
+			#else
+				mnStopwatchFrequency = mnCPUFrequency;
+			#endif
+		#endif
+
+		EAStdCStopwatchSetupCoefficients();
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EAStdCStopwatchDisableCPUCalibration
+//
+// This function circumvents the EAStdCStopwatchSetup function for the purpose
+// of making it so that EAStdCStopwatchSetup doesn't take a long time to execute
+// on startup. If this function is executed before EAStdCStopwatchSetup then
+// if EAStdCStopwatchSetup is later executed then it will just immediately exit.
+// This function can be considered an alternative to the EAStdCStopwatchSetup 
+// function that executes much faster. The downside is that the CPU frequency
+// will not be calculated or known and thus the CPU-based timing functions won't
+// be available (though the system time-based timing functions will be). If you 
+// have a utility app that you need to run which needs to execute quickly and 
+// doesn't need CPU-based clock tick timing precision, you might want to use 
+// this function. See the EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION function for
+// how to do this. The most important thing is that this function needs to be 
+// called before the EAStdCStopwatchSetup function, which may require some 
+// compiler/platform-specific trickery as documented with EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION.
+// 
+EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency)
+{
+	if(cpuFrequency)
+		mnCPUFrequency = cpuFrequency;
+	else
+		mnCPUFrequency = UINT64_C(2000000000); // We use a moderate guess here of 2GHz. To consider: Make this a very inaccurate value so that it's obvious it's wrong at runtime.
+
+	////////////////////////////////////////////////////
+	// Stopwatch Frequency
+	#if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
+		mnStopwatchFrequency = mnCPUFrequency;
+	#else
+		#if defined(EA_PLATFORM_MICROSOFT)
+			::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency);
+		#else
+			// To do.
+		#endif
+	#endif
+
+	EAStdCStopwatchSetupCoefficients();
+}
+
+
+
+namespace EA
+{
+	namespace StdC
+	{
+		//We have this #if !defined() commented out because we aren't sure that all GCC versions act the same across all platforms.
+		//#if !defined(__GNUC__) // GCC uses __attribute__((constructor)) instead of this to call EAStdCStopwatchSetup.
+			// AutoStopwatchSetup
+			// We create this static class in order to trigger 
+			// automatic calling of the code below.
+			struct AutoStopwatchSetup
+			{
+				AutoStopwatchSetup()
+					{ EAStdCStopwatchSetup(); }
+			};
+
+			AutoStopwatchSetup gAutoStopwatchSetup EA_INIT_PRIORITY(1000);
+		//#endif
+	}
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Stopwatch
+///////////////////////////////////////////////////////////////////////////////
+
+EA::StdC::Stopwatch::Stopwatch(int nUnits, bool bStartImmediately)
+  : mnStartTime(0),
+	mnTotalElapsedTime(0),
+	mnUnits(0),
+	mfStopwatchCyclesToUnitsCoefficient(1.f)
+{
+	SetUnits(nUnits);
+	if(mnStopwatchFrequency <= 1) // If not already initialized...
+		EAStdCStopwatchSetup();
+	if(bStartImmediately)
+		Start();
+}
+
+
+void EA::StdC::Stopwatch::SetUnits(int nUnits)
+{
+	mnUnits = nUnits;
+
+	mfStopwatchCyclesToUnitsCoefficient = 1;
+
+	switch (mnUnits)
+	{
+		case kUnitsCPUCycles:
+			mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetCPUCycle().
+			break;
+
+		case kUnitsCycles:
+			mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetStopwatchCycle().
+			break;
+
+		case kUnitsNanoseconds:
+			mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToNanosecondsCoefficient;
+			break;
+
+		case kUnitsMicroseconds:
+			mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMicrosecondsCoefficient;
+			break;
+
+		case kUnitsMilliseconds:
+			mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMillisecondsCoefficient;
+			break;
+
+		case kUnitsSeconds:
+			mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToSecondsCoefficient;
+			break;
+
+		case kUnitsMinutes:
+			mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMinutesCoefficient;
+			break;
+	}
+}
+
+
+void EA::StdC::Stopwatch::Stop()
+{
+	if(mnStartTime) // Check to make sure the stopwatch is actually running
+	{
+		// Note that below we compare the elapsed time (from the most recent start
+		// to the this stop) to sStopwatchCycleReadingOverhead. For most timing situations,
+		// the elapsed time will be *much* greater than the overhead. For some 
+		// cases (e.g. timing 10-20 lines of C code) the elapsed time will be only 
+		// 3-10 times the value of sStopwatchCycleReadingOverhead. In these cases, the 
+		// value of subtracting this overhead may be useful. For some cases the 
+		// code being timed is so small or brief that sStopwatchCycleReadingOverhead may
+		// actually come out to be higher than the stretch of code. If this is the
+		// case, you really don't want to be trying to time this code with a 
+		// softare-based stopwatch. What we do is simply set the elapsed time to a 
+		// small value such as 1, in order for code that is using this stopwatch to at
+		// least believe that something is happening.
+
+		const uint64_t nCurrentTime(mnUnits == kUnitsCPUCycles ? GetCPUCycle() : GetStopwatchCycle());
+
+		// EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
+
+		#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+			const uint64_t nElapsedTime(nCurrentTime - mnStartTime);
+
+			if(nElapsedTime > mnStopwatchCycleReadingOverhead)
+				mnTotalElapsedTime += nElapsedTime - mnStopwatchCycleReadingOverhead;
+			else
+				mnTotalElapsedTime += 1; // We pretend that just one stopwatch cycle has elapsed.
+		#else
+			mnTotalElapsedTime += (nCurrentTime - mnStartTime);
+		#endif
+
+		mnStartTime = 0;
+	}
+}
+
+
+uint64_t EA::StdC::Stopwatch::GetElapsedTime() const
+{
+	uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime);
+
+	if(mnStartTime) // We we are currently running, then take into account time passed since last start.
+	{
+
+		uint64_t nCurrentTime;
+
+		// See the 'Stop' function for an explanation of the code below.
+		if(mnUnits == kUnitsCPUCycles)
+			nCurrentTime = GetCPUCycle();
+		else
+			nCurrentTime = GetStopwatchCycle();
+
+		uint64_t nElapsed = nCurrentTime - mnStartTime;
+
+		// EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
+
+		#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+			const uint64_t nElapsedTime = nElapsed;
+
+			if(nElapsedTime > mnStopwatchCycleReadingOverhead)
+				nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead;
+			else
+				nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed.
+		#else
+			nFinalTotalElapsedTime64 += nElapsed;
+		#endif
+
+	} // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles. 
+
+	if(mfStopwatchCyclesToUnitsCoefficient == 0) // If the stopwatch was started before the global EAStdCStopwatchSetup function was executed...
+	{
+		// We can recover from this by calling SetUnits here, which will re-read the global setup results.
+		const_cast<Stopwatch*>(this)->SetUnits(mnUnits);
+		// EA_ASSERT(mfStopwatchCyclesToUnitsCoefficient != 0);
+	}
+
+	// We are doing a float to int cast here, which is a relatively slow operation on some CPUs.
+	return (uint64_t)((nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient) + 0.49999f);
+}
+
+
+
+
+void EA::StdC::Stopwatch::SetElapsedTime(uint64_t nElapsedTime)
+{
+	if(IsRunning()) 
+		Restart();
+
+	// Concern: The division here is not lightning fast and also is subject to precision error.
+	mnTotalElapsedTime = (uint64_t)((nElapsedTime / mfStopwatchCyclesToUnitsCoefficient) + 0.49999f);
+}
+
+
+float EA::StdC::Stopwatch::GetElapsedTimeFloat() const
+{
+	uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime);
+
+	if(mnStartTime){ //We we are currently running, then take into account time passed since last start.
+
+		uint64_t nCurrentTime;
+
+		// See the 'Stop' function for an explanation of the code below.
+		if(mnUnits == kUnitsCPUCycles)
+			nCurrentTime = GetCPUCycle();
+		else
+			nCurrentTime = GetStopwatchCycle();
+
+		uint64_t nElapsed = nCurrentTime - mnStartTime;
+
+		// EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
+
+		#if EASTDC_STOPWATCH_OVERHEAD_ENABLED
+			const uint64_t nElapsedTime = nElapsed;
+
+			if(nElapsedTime > mnStopwatchCycleReadingOverhead)
+				nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead;
+			else
+				nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed.
+		#else
+			nFinalTotalElapsedTime64 += nElapsed;
+		#endif
+
+	} // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles. 
+
+	// We are doing a float to int cast here, which is a relatively slow operation on some CPUs.
+	return (float)(nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient);
+}
+
+
+void EA::StdC::Stopwatch::SetElapsedTimeFloat(float fElapsedTime)
+{
+	if(IsRunning()) 
+		Restart();
+
+	// Concern: The division here is not lightning fast and also is subject to precision error.
+	mnTotalElapsedTime = (uint64_t)(fElapsedTime / mfStopwatchCyclesToUnitsCoefficient);
+}
+
+
+float EA::StdC::Stopwatch::GetUnitsPerStopwatchCycle(Units units)
+{
+	switch (units)
+	{
+		case kUnitsNanoseconds:
+			return mfStopwatchCyclesToNanosecondsCoefficient;
+
+		case kUnitsMicroseconds:
+			return mfStopwatchCyclesToMicrosecondsCoefficient;
+
+		case kUnitsMilliseconds:
+			return mfStopwatchCyclesToMillisecondsCoefficient;
+
+		case kUnitsSeconds:
+			return mfStopwatchCyclesToSecondsCoefficient;
+
+		case kUnitsMinutes:
+			return mfStopwatchCyclesToMinutesCoefficient;
+
+		case kUnitsCycles:
+		case kUnitsCPUCycles:
+		case kUnitsUserDefined:
+		default:
+			break;
+	}
+
+	return 1;
+}
+
+
+float EA::StdC::Stopwatch::GetUnitsPerCPUCycle(Units units)
+{
+	switch (units)
+	{
+		case kUnitsNanoseconds:
+			return mfCPUCyclesToNanosecondsCoefficient;
+
+		case kUnitsMicroseconds:
+			return mfCPUCyclesToMicrosecondsCoefficient;
+
+		case kUnitsMilliseconds:
+			return mfCPUCyclesToMillisecondsCoefficient;
+
+		case kUnitsSeconds:
+			return mfCPUCyclesToSecondsCoefficient;
+
+		case kUnitsMinutes:
+			return mfCPUCyclesToMinutesCoefficient;
+
+		case kUnitsCycles:
+		case kUnitsCPUCycles:
+		case kUnitsUserDefined:
+		default:
+			break;
+	}
+
+	return 1;
+}
+
+
+// This function is here instead of inlined in the associated header file
+// because it uses mnStopwatchFrequency, which is a variable local to this 
+// source file.
+uint64_t EA::StdC::Stopwatch::GetStopwatchFrequency()
+{
+	return mnStopwatchFrequency;
+}
+
+
+// This function is here instead of inlined in the associated header file
+// because it uses mnStopwatchFrequency, which is a variable local to this 
+// source file.
+uint64_t EA::StdC::Stopwatch::GetCPUFrequency()
+{
+	return mnCPUFrequency;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// LimitStopwatch
+///////////////////////////////////////////////////////////////////////////////
+
+void EA::StdC::LimitStopwatch::SetTimeLimit(uint64_t nLimit, bool bStartImmediately)
+{
+	const uint64_t nCurrentTime = GetStopwatchCycle();
+
+	mnEndTime = nCurrentTime + (uint64_t)(nLimit / mfStopwatchCyclesToUnitsCoefficient);
+
+	if(bStartImmediately)
+		Start();
+}
+
+
+float EA::StdC::LimitStopwatch::GetTimeRemainingFloat() const
+{
+	const uint64_t nCurrentTime = GetStopwatchCycle();
+
+	const float fTimeRemaining = (float)((int64_t)(mnEndTime - nCurrentTime)) * mfStopwatchCyclesToUnitsCoefficient;
+
+	return fTimeRemaining;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 6103 - 0
source/EAString.cpp

@@ -0,0 +1,6103 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EACType.h>
+#include <EAStdC/EAMathHelp.h>
+#include <EAStdC/EAStdC.h>
+#include <EAStdC/EAMemory.h>
+#include <EAStdC/EAAlignment.h>
+#include <EAStdC/EABitTricks.h>
+#include <EAAssert/eaassert.h>
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+EA_DISABLE_VC_WARNING(4996 4127 6385 4146) // 'ecvt' was declared deprecated
+									       // conditional expression is constant.
+									       // Invalid data: accessing 'comp1', the readable size is '20' bytes, but '4194248' bytes might be read: Lines: 504, 505, 507, 508, 510, 512, 514
+										   //  warning C4146: unary minus operator applied to unsigned type, result still unsigned
+#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)
+	EA_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) // GCC 4.7+ mistakenly warns about this.
+#endif
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EASTDC_MIN / EASTDC_MAX
+//
+#define EASTDC_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define EASTDC_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+// Define word_type to match machine word type. This is not the same 
+// thing as size_t or uintptr_t, as there are machines with 64 bit
+// words but 32 bit pointers (e.g. XBox 360, PS3).
+
+#if (EA_PLATFORM_WORD_SIZE == 8) // From EABase
+	typedef uint64_t word_type;
+#else
+	typedef uint32_t word_type;
+#endif
+
+
+EASTDC_API char8_t* Strcpy(char8_t* pDestination, const char8_t* pSource)
+{
+	const char8_t* s = pSource;
+	char8_t*       d = pDestination;
+
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+	
+	return pDestination;
+}
+
+EASTDC_API char16_t* Strcpy(char16_t* pDestination, const char16_t* pSource)
+{
+	const char16_t* s = pSource;
+	char16_t*       d = pDestination;
+
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+	
+	return pDestination;
+}
+
+EASTDC_API char32_t* Strcpy(char32_t* pDestination, const char32_t* pSource)
+{
+	const char32_t* s = pSource;
+	char32_t*       d = pDestination;
+
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+	
+	return pDestination;
+}
+
+
+
+
+EASTDC_API char8_t* Strncpy(char8_t* pDestination, const char8_t* pSource, size_t n)
+{
+	const char8_t* s = pSource;
+	char8_t*       d = pDestination;
+
+	n++;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			while(--n)
+				*d++ = 0;
+			break;
+		}
+	}
+	
+	return pDestination;
+}
+
+EASTDC_API char16_t* Strncpy(char16_t* pDestination, const char16_t* pSource, size_t n)
+{
+	const char16_t* s = pSource;
+	char16_t*       d = pDestination;
+
+	n++;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			while(--n)
+				*d++ = 0;
+			break;
+		}
+	}
+	
+	return pDestination;
+}
+
+EASTDC_API char32_t* Strncpy(char32_t* pDestination, const char32_t* pSource, size_t n)
+{
+	const char32_t* s = pSource;
+	char32_t*       d = pDestination;
+
+	n++;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			while(--n)
+				*d++ = 0;
+			break;
+		}
+	}
+	
+	return pDestination;
+}
+
+
+
+
+EASTDC_API char8_t* StringnCopy(char8_t* pDestination, const char8_t* pSource, size_t n)
+{
+	char8_t* pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		if(n != static_cast<size_t>(-1)) // Is this portable?
+			*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+EASTDC_API char16_t* StringnCopy(char16_t* pDestination, const char16_t* pSource, size_t n)
+{
+	char16_t* pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		if(n != static_cast<size_t>(-1))
+			*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+EASTDC_API char32_t* StringnCopy(char32_t* pDestination, const char32_t* pSource, size_t n)
+{
+	char32_t* pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		if(n != static_cast<size_t>(-1))
+			*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+
+
+
+/* Reference implementation which ought to be a little slower than our more optimized implementation.
+EASTDC_API size_t Strlcpy(char8_t* pDestination, const char8_t* pSource, size_t nDestCapacity)
+{
+	const size_t n = Strlen(pSource);
+
+	if(n < nDestCapacity)
+		memcpy(pDestination, pSource, (n + 1) * sizeof(*pSource));
+	else if(nDestCapacity)
+	{
+		memcpy(pDestination, pSource, (nDestCapacity - 1) * sizeof(*pSource));
+		pDestination[nDestCapacity - 1] = 0;
+	}
+
+	return n;
+}
+*/
+
+EASTDC_API size_t Strlcpy(char8_t* pDestination, const char8_t* pSource, size_t nDestCapacity)
+{
+	const char8_t* s = pSource;
+	size_t         n = nDestCapacity;
+
+	if(n && --n)
+	{
+		do{
+			if((*pDestination++ = *s++) == 0)
+				break;
+		} while(--n);
+	}
+
+	if(!n)
+	{
+		if(nDestCapacity)
+			*pDestination = 0;
+		while(*s++)
+			{ }
+	}
+
+	return (size_t)(s - pSource - 1);
+}
+
+/* Reference implementation which ought to be a little slower than our more optimized implementation.
+EASTDC_API size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
+{
+	const size_t n = Strlen(pSource);
+
+	if(n < nDestCapacity)
+		memcpy(pDestination, pSource, (n + 1) * sizeof(*pSource));
+	else if(nDestCapacity)
+	{
+		memcpy(pDestination, pSource, (nDestCapacity - 1) * sizeof(*pSource));
+		pDestination[nDestCapacity - 1] = 0;
+	}
+
+	return n;
+}
+*/
+
+EASTDC_API size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
+{
+	const char16_t* s = pSource;
+	size_t          n = nDestCapacity;
+
+	if(n && --n)
+	{
+		do{
+			if((*pDestination++ = *s++) == 0)
+				break;
+		} while(--n);
+	}
+
+	if(!n)
+	{
+		if(nDestCapacity)
+			*pDestination = 0;
+		while(*s++)
+			{ }
+	}
+
+	return (size_t)(s - pSource - 1);
+}
+
+EASTDC_API size_t Strlcpy(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
+{
+	const char32_t* s = pSource;
+	size_t          n = nDestCapacity;
+
+	if(n && --n)
+	{
+		do{
+			if((*pDestination++ = *s++) == 0)
+				break;
+		} while(--n);
+	}
+
+	if(!n)
+	{
+		if(nDestCapacity)
+			*pDestination = 0;
+		while(*s++)
+			{ }
+	}
+
+	return (size_t)(s - pSource - 1);
+}
+
+
+
+/*
+// To consider: Enable this for completeness with the above:
+
+EASTDC_API int Strlcpy(char8_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	if(nSourceLength == kSizeTypeUnset)
+		nSourceLength = Strlen(pSource);
+
+	if(nSourceLength < nDestCapacity)
+	{
+		memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
+		pDest[nSourceLength] = 0;
+	}
+	else if(nDestCapacity)
+	{
+		memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
+		pDest[nDestCapacity - 1] = 0;
+	}
+
+	return (int)(unsigned)nSourceLength;
+}
+
+EASTDC_API int Strlcpy(char16_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	if(nSourceLength == kSizeTypeUnset)
+		nSourceLength = Strlen(pSource);
+
+	if(nSourceLength < nDestCapacity)
+	{
+		memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
+		pDest[nSourceLength] = 0;
+	}
+	else if(nDestCapacity)
+	{
+		memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
+		pDest[nDestCapacity - 1] = 0;
+	}
+
+	return (int)(unsigned)nSourceLength;
+}
+
+EASTDC_API int Strlcpy(char32_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	if(nSourceLength == kSizeTypeUnset)
+		nSourceLength = Strlen(pSource);
+
+	if(nSourceLength < nDestCapacity)
+	{
+		memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
+		pDest[nSourceLength] = 0;
+	}
+	else if(nDestCapacity)
+	{
+		memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
+		pDest[nDestCapacity - 1] = 0;
+	}
+
+	return (int)(unsigned)nSourceLength;
+}
+*/
+
+
+
+//static const int32_t kLeadingSurrogateStart  = 0x0000d800;
+//static const int32_t kTrailingSurrogateStart = 0x0000dc00;
+//static const int32_t kLeadingSurrogateEnd    = 0x0000dbff;
+//static const int32_t kTrailingSurrogateEnd   = 0x0000dfff;
+//static const int32_t kSurrogateOffset        = 0x00010000 - (kLeadingSurrogateStart << 10) - kTrailingSurrogateStart;
+
+// This is not static because it is used elsewhere.
+uint8_t utf8lengthTable[256] = 
+{
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  // For full UTF8 support, this last row should be: 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0
+};
+
+// Used to subtract out the control bits in multi-byte sequence.
+static const uint32_t utf8DecodingOffsetTable[5] = 
+{
+	0,                                                  // 0x00000000
+	0,                                                  // 0x00000000
+	(0xC0 << 6) + 0x80,                                 // 0x00003080
+	(0xE0 << 12) + (0x80 << 6) + 0x80,                  // 0x000e2080
+	(0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80    // 0x03c82080
+  // to do
+  // to do
+};
+
+// The minimum value that can be encoded in a particular number
+// of bytes. Used to disallow non-canonical encoded sequences.
+// It turns out that this is not a fully proper way to check for 
+// valid sequences. See the Unicode Standard http://unicode.org/versions/corrigendum1.html
+static const uint32_t utf8MinimumValueTable[] = 
+{
+	0x00000000,     // This slot is unused
+	0x00000000,     // 1 byte per char
+	0x00000080,     // 2 bytes per char
+	0x00000800,     // 3 bytes per char
+	0x00010000      // 4 bytes per char
+  //0x00200000,     // 5 bytes per char
+  //0x04000000,     // 6 bytes per char
+};
+
+// One past the maximum value that can be encoded in a particular number
+// of bytes. Used to disallow non-canonical encoded sequences.
+static const uint32_t utf8MaximumValueTable[] = 
+{
+	0x00000000,     // This slot is unused
+	0x00000080,     // 1 byte per char
+	0x00000800,     // 2 bytes per char
+	0x00010000,     // 3 bytes per char
+	0x00110000      // 4 bytes per char      *** Should be: 0x00200000
+  //0x04000000
+  //0x80000000 	
+};
+
+const uint32_t kUnicodeReplacementChar	= 0x0000fffd;
+const uint32_t kUnicodeInvalidDecode = 0xffffffffu;
+
+EA_FORCE_INLINE uint32_t DecodeCodePoint(const char8_t*& pSourceStart, const char8_t* pSourceEnd)
+{
+	const char8_t* pSource = pSourceStart;
+	uint32_t c = (uint8_t)*(pSource++);
+
+	if(c < 128)
+	{
+		// Valid character, do nothing
+	}
+	else
+	{
+		uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
+
+											   // Do we have an incomplete or invalid string?
+		if((pSourceStart + nLength > pSourceEnd) || (nLength == 0))
+		{
+			if(EA::StdC::GetAssertionsEnabled())
+			{
+				EA_FAIL_MSG("Incomplete Unicode character in buffer");
+			}
+			return kUnicodeInvalidDecode;
+		}
+
+		// Now decode the remaining ("following") bytes.
+		for(uint32_t i = 0; i < nLength - 1; ++i)
+		{
+			uint8_t nByte = (uint8_t)*(pSource++);
+
+			if((nByte < 0x80u) || (nByte > 0xbfu))   // Syntax check
+			{
+				if(EA::StdC::GetAssertionsEnabled())
+				{
+					EA_FAIL_MSG("Invalid following byte");
+				}
+				return kUnicodeInvalidDecode;
+			}
+
+			c = (c << 6) + nByte;   // Preserve control bits (don't OR)
+		}
+
+		c -= utf8DecodingOffsetTable[nLength];    // Subtract accumulated control bits just once
+
+												  // Check for canonical encoding.
+		if((c < utf8MinimumValueTable[nLength]) || (c >= utf8MaximumValueTable[nLength]))
+		{
+			return kUnicodeInvalidDecode;
+		}
+	}
+
+	pSourceStart = pSource;
+	return c;
+}
+
+EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char8_t*& pDestStart, char8_t* pDestEnd)
+{
+	// Encode as UTF-8
+	if(c < 0x00000080u)
+	{
+		*(pDestStart++) = static_cast<char8_t>(c);
+		return true;
+	}
+	else if(c < 0x00000800u)
+	{
+		if (pDestStart + 2 <= pDestEnd)
+		{
+			char8_t* pDest = pDestStart;
+			*(pDest++) = static_cast<char8_t>((c >> 6) | 0xc0);
+			*(pDest++) = static_cast<char8_t>((c | 0x80) & 0xbf);
+			pDestStart = pDest;
+			return true;
+		}
+		return false;
+	}
+	else if(c < 0x00010000u)
+	{
+		if (pDestStart + 3 <= pDestEnd)
+		{
+			char8_t* pDest = pDestStart;
+			*(pDest++) = static_cast<char8_t>((c >> 12) | 0xe0);
+			*(pDest++) = static_cast<char8_t>(((c >> 6) | 0x80) & 0xbf);
+			*(pDest++) = static_cast<char8_t>((c | 0x80) & 0xbf);
+			pDestStart = pDest;
+			return true;
+		}
+		return false;
+	}
+	else if(c < 0x00200000u)
+	{
+		if(pDestStart + 4 <= pDestEnd)
+		{
+			char8_t* pDest = pDestStart;
+			*(pDest++) = static_cast<char8_t>((c >> 18) | 0xf0);
+			*(pDest++) = static_cast<char8_t>(((c >> 12) | 0x80) & 0xbf);
+			*(pDest++) = static_cast<char8_t>(((c >> 6) | 0x80) & 0xbf);
+			*(pDest++) = static_cast<char8_t>((c | 0x80) & 0xbf);
+			pDestStart = pDest;
+			return true;
+		}
+		return false;
+	}
+	else
+	{
+		// We don't currently support Unicode beyond "Plane 0", as game software has never needed it.
+		// If you have a need for this support, feel free to submit a patch or make a specific request.
+		c = kUnicodeReplacementChar;
+		if(pDestStart + 3 <= pDestEnd)
+		{
+			char8_t* pDest = pDestStart;
+			*(pDest++) = static_cast<char8_t>((c >> 12) | 0xe0);
+			*(pDest++) = static_cast<char8_t>(((c >> 6) | 0x80) & 0xbf);
+			*(pDest++) = static_cast<char8_t>(((c >> 0) | 0x80) & 0xbf);
+			pDestStart = pDest;
+			return true;
+		}
+		return false;
+	}
+}
+
+EA_FORCE_INLINE uint32_t DecodeCodePoint(const char16_t*& pSourceStart, const char16_t* pSourceEnd)
+{
+	return (uint32_t)*(pSourceStart++);
+}
+
+EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char16_t*& pDestStart, char16_t* pDestEnd)
+{
+	*(pDestStart++) = static_cast<char16_t>(c);
+	return true;
+}
+
+EA_FORCE_INLINE uint32_t DecodeCodePoint(const char32_t*& pSourceStart, const char32_t* pSourceEnd)
+{
+	return (uint32_t)*(pSourceStart++);
+}
+
+EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char32_t*& pDestStart, char32_t* pDestEnd)
+{
+	*(pDestStart++) = static_cast<char32_t>(c);
+	return true;
+}
+
+template <typename InCharT, typename OutCharT>
+bool StrlcpyInternal(OutCharT* pDest, const InCharT* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	EA_ASSERT(pDest != nullptr && pSource != nullptr);
+
+	// If we have no capacity, then just return nothing
+	if(nDestCapacity == 0)
+	{
+		nDestUsed = 0;
+		nSourceUsed = 0;
+		return true;
+	}
+
+	const InCharT* pSourceStart = pSource;
+	const InCharT* pSourceEnd = pSource + nSourceLength;
+	if (pSourceEnd < pSourceStart)
+		pSourceEnd = (const InCharT*)(uintptr_t)-1;
+	OutCharT* pDestStart = pDest;
+	OutCharT* pDestEnd = pDest + nDestCapacity - 1;
+	bool bGood = true;
+
+	while(bGood && (pSource < pSourceEnd) && (pDest < pDestEnd))
+	{
+		uint32_t c = DecodeCodePoint(pSource, pSourceEnd);
+		if (c == 0)
+		{
+			pSource = pSourceEnd;
+			break;
+		}
+		bGood = (c != kUnicodeInvalidDecode) && EncodeCodePoint(c, pDest, pDestEnd);
+	}
+
+	*pDest = 0;
+
+	nDestUsed = pDest - pDestStart;
+	nSourceUsed = pSource - pSourceStart;
+	return bGood;
+}
+
+bool Strlcpy(char8_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+bool Strlcpy(char8_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+bool Strlcpy(char16_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+bool Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+bool Strlcpy(char32_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+bool Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
+{
+	return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
+}
+
+EASTDC_API int Strlcpy(char8_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint16_t)*pSource++;   // Deal with surrogate characters
+
+		// Encode as UTF-8
+		if (c < 0x00000080u)
+		{
+			if(c == 0) // Break on NULL char, even if explicit length was set
+				break;
+
+			if(pDest && ((destCount + 1) < nDestCapacity))
+				*pDest++ = static_cast<char8_t>(c);
+
+			destCount += 1;
+		}
+		else if(c < 0x00000800u)
+		{
+			if(pDest && ((destCount + 2) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 6) | 0xc0);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 2;
+		}
+		else if(c < 0x00010000u)
+		{
+			if(pDest && ((destCount + 3) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 12) | 0xe0);
+				*pDest++ = static_cast<char8_t>(((c >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 3;
+		}
+		else if(c < 0x00200000u)
+		{
+			if(pDest && ((destCount + 4) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 18) | 0xf0);
+				*pDest++ = static_cast<char8_t>(((c >> 12) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>(((c >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 4;
+		}
+		else
+		{
+			const uint32_t kIllegalUnicodeChar = 0x0000fffd;
+
+			if(pDest && ((destCount + 3) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>( (kIllegalUnicodeChar >> 12) | 0xe0);
+				*pDest++ = static_cast<char8_t>(((kIllegalUnicodeChar >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>(((kIllegalUnicodeChar >>  0) | 0x80) & 0xbf);
+			}
+
+			destCount += 3;
+		}
+	}
+
+	if(pDest && nDestCapacity != 0)
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+EASTDC_API int Strlcpy(char8_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint32_t)*pSource++;   // Deal with surrogate characters
+
+		// Encode as UTF-8
+		if (c < 0x00000080u)
+		{
+			if(c == 0) // Break on NULL char, even if explicit length was set
+				break;
+
+			if(pDest && ((destCount + 1) < nDestCapacity))
+				*pDest++ = static_cast<char8_t>(c);
+
+			destCount += 1;
+		}
+		else if(c < 0x00000800u)
+		{
+			if(pDest && ((destCount + 2) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 6) | 0xc0);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 2;
+		}
+		else if(c < 0x00010000u)
+		{
+			if(pDest && ((destCount + 3) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 12) | 0xe0);
+				*pDest++ = static_cast<char8_t>(((c >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 3;
+		}
+		else if(c < 0x00200000u)
+		{
+			if(pDest && ((destCount + 4) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>((c >> 18) | 0xf0);
+				*pDest++ = static_cast<char8_t>(((c >> 12) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>(((c >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>((c | 0x80) & 0xbf);
+			}
+
+			destCount += 4;
+		}
+		else
+		{
+			// We don't currently support Unicode beyond "Plane 0", as game software has never needed it.
+			// If you have a need for this support, feel free to submit a patch or make a specific request.
+			const uint32_t kIllegalUnicodeChar = 0x0000fffd;
+
+			if(pDest && ((destCount + 3) < nDestCapacity))
+			{
+				*pDest++ = static_cast<char8_t>( (kIllegalUnicodeChar >> 12) | 0xe0);
+				*pDest++ = static_cast<char8_t>(((kIllegalUnicodeChar >>  6) | 0x80) & 0xbf);
+				*pDest++ = static_cast<char8_t>(((kIllegalUnicodeChar >>  0) | 0x80) & 0xbf);
+			}
+
+			destCount += 3;
+		}
+	}
+
+	if(pDest && nDestCapacity != 0)
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+
+
+EASTDC_API int Strlcpy(char16_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint8_t)*pSource++;
+
+		if(c < 128)
+		{
+			if(c == 0) // Break on NULL char, even if explicit length was set
+				break;
+
+			if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
+				*pDest++ = static_cast<char16_t>(c);
+
+			destCount++;
+		}
+		else
+		{
+			uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
+
+			// Do we have an incomplete or invalid string?
+			if((nLength > (nSourceLength + 1)) || (nLength == 0))
+			{
+				if(EA::StdC::GetAssertionsEnabled())
+					{ EA_FAIL_MSG("Incomplete Unicode character in buffer"); }
+				if(pDest && (destCount < nDestCapacity))
+					*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
+				return -1;
+			}
+
+			// Now decode the remaining ("following") bytes.
+			for(uint32_t i = 0; i < nLength - 1; ++i) 
+			{
+				uint8_t nByte = (uint8_t)*pSource++;
+
+				if((nByte < 0x80u) || (nByte > 0xbfu))   // Syntax check
+				{
+					if(EA::StdC::GetAssertionsEnabled())
+						{ EA_FAIL_MSG("Invalid following byte"); }
+					if(pDest && (destCount < nDestCapacity))
+						*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
+					return -1;
+				}
+
+				c = (c << 6) + nByte;   // Preserve control bits (don't OR)
+			}
+
+			nSourceLength -= (nLength - 1);           // We've just processed all remaining bytes for this multi-byte character
+			c -= utf8DecodingOffsetTable[nLength];    // Subtract accumulated control bits just once
+
+			// Check for canonical encoding.
+			if((c >= utf8MinimumValueTable[nLength]) && (c < utf8MaximumValueTable[nLength]))
+			{
+				if(pDest && ((destCount + 1) < nDestCapacity))
+					*pDest++ = static_cast<char16_t>(c);
+
+				destCount++;
+			}
+			else
+				break;
+		}
+	}
+
+	if(pDest && (nDestCapacity != 0))
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+EASTDC_API int Strlcpy(char32_t* pDest, const char8_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint8_t)*pSource++;
+
+		if(c < 128)
+		{
+			if(c == 0) // Break on NULL char, even if explicit length was set
+				break;
+
+			if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
+				*pDest++ = static_cast<char32_t>(c);
+
+			destCount++;
+		}
+		else
+		{
+			uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
+
+			// Do we have an incomplete or invalid string?
+			if((nLength > (nSourceLength + 1)) || (nLength == 0))
+			{
+				if(EA::StdC::GetAssertionsEnabled())
+					{ EA_FAIL_MSG("Incomplete Unicode character in buffer"); }
+				if(pDest && (destCount < nDestCapacity))
+					*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
+				return -1;
+			}
+
+			// Now decode the remaining ("following") bytes.
+			for(uint32_t i = 0; i < nLength - 1; ++i) 
+			{
+				uint8_t nByte = (uint8_t)*pSource++;
+
+				if((nByte < 0x80u) || (nByte > 0xbfu))   // Syntax check
+				{
+					if(EA::StdC::GetAssertionsEnabled())
+						{ EA_FAIL_MSG("Invalid following byte"); }
+					if(pDest && (destCount < nDestCapacity))
+						*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
+					return -1;
+				}
+
+				c = (c << 6) + nByte;   // Preserve control bits (don't OR)
+			}
+
+			nSourceLength -= (nLength - 1);           // We've just processed all remaining bytes for this multi-byte character
+			c -= utf8DecodingOffsetTable[nLength];    // Subtract accumulated control bits just once
+
+			// Check for canonical encoding.
+			if((c >= utf8MinimumValueTable[nLength]) && (c < utf8MaximumValueTable[nLength]))
+			{
+				if(pDest && ((destCount + 1) < nDestCapacity))
+					*pDest++ = static_cast<char32_t>(c);
+
+				destCount++;
+			}
+			else
+				break;
+		}
+	}
+
+	if(pDest && (nDestCapacity != 0))
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+
+EASTDC_API int Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint32_t)*pSource++;
+
+		if(c == 0) // Break on NULL char, even if explicit length was set
+			break;
+
+		if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
+			*pDest++ = static_cast<char32_t>(c);
+
+		destCount += 1;
+	}
+
+	if(pDest && nDestCapacity != 0)
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+
+EASTDC_API int Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
+{
+	size_t destCount = 0;
+
+	while(nSourceLength-- > 0)
+	{
+		uint32_t c = (uint32_t)*pSource++;
+
+		if(c == 0) // Break on NULL char, even if explicit length was set
+			break;
+
+		if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
+			*pDest++ = static_cast<char16_t>(c);
+
+		destCount += 1;
+	}
+
+	if(pDest && nDestCapacity != 0)
+		*pDest = 0;
+
+	return (int)(unsigned)destCount;
+}
+
+
+
+EASTDC_API char8_t* Strcat(char8_t* pDestination, const char8_t* pSource)
+{
+	const char8_t* s = pSource;
+	char8_t*       d = pDestination;
+
+	while(*d++){}          // Do nothing.
+		--d;
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+	
+	return pDestination;
+}
+
+EASTDC_API char16_t* Strcat(char16_t* pDestination, const char16_t* pSource)
+{
+	const char16_t* s = pSource;
+	char16_t*       d = pDestination;
+
+	while(*d++){}          // Do nothing.
+		--d;
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+	
+	return pDestination;
+}
+
+EASTDC_API char32_t* Strcat(char32_t* pDestination, const char32_t* pSource)
+{
+	const char32_t* s = pSource;
+	char32_t*       d = pDestination;
+
+	while(*d++){}          // Do nothing.
+		--d;
+	while((*d++ = *s++) != 0)
+		{} // Do nothing.
+
+	return pDestination;
+}
+
+
+
+
+EASTDC_API char8_t* Strncat(char8_t* pDestination, const char8_t* pSource, size_t n)
+{
+	const char8_t* s = pSource;
+	char8_t*       d = pDestination;
+	
+	while(*d++){} // Do nothing.
+	--d;
+	++n;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			--d;
+			break;
+		}
+	}
+	*d = 0;
+	
+	return pDestination;
+}
+
+EASTDC_API char16_t* Strncat(char16_t* pDestination, const char16_t* pSource, size_t n)
+{
+	const char16_t* s = pSource;
+	char16_t*       d = pDestination;
+	
+	while(*d++){} // Do nothing.
+	--d;
+	++n;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			--d;
+			break;
+		}
+	}
+	*d = 0;
+	
+	return pDestination;
+}
+
+EASTDC_API char32_t* Strncat(char32_t* pDestination, const char32_t* pSource, size_t n)
+{
+	const char32_t* s = pSource;
+	char32_t*       d = pDestination;
+	
+	while(*d++){} // Do nothing.
+	--d;
+	++n;
+	while(--n)
+	{
+		if((*d++ = *s++) == 0)
+		{
+			--d;
+			break;
+		}
+	}
+	*d = 0;
+	
+	return pDestination;
+}
+
+
+
+EASTDC_API char8_t* StringnCat(char8_t* pDestination, const char8_t* pSource, size_t n)
+{
+	char8_t* const pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(*pDestination)
+			++pDestination;
+
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+EASTDC_API char16_t* StringnCat(char16_t* pDestination, const char16_t* pSource, size_t n)
+{
+	char16_t* const pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(*pDestination)
+			++pDestination;
+
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+EASTDC_API char32_t* StringnCat(char32_t* pDestination, const char32_t* pSource, size_t n)
+{
+	char32_t* const pOriginalDest = pDestination;
+
+	if(n)
+	{
+		while(*pDestination)
+			++pDestination;
+
+		while(n-- && *pSource)
+			*pDestination++ = *pSource++;
+
+		*pDestination = 0;
+	}
+
+	return pOriginalDest;
+}
+
+
+
+
+EASTDC_API size_t Strlcat(char8_t* pDestination, const char8_t* pSource, size_t nDestCapacity)
+{
+	const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
+	const size_t s = Strlen(pSource);
+	const size_t t = s + d;
+
+	EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
+		"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
+
+	if(t < nDestCapacity)
+		memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
+	else
+	{
+		if(nDestCapacity)
+		{
+			memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
+			pDestination[nDestCapacity - 1] = 0;
+		}
+	}
+
+	return t;
+}
+
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
+{
+	const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
+	const size_t s = Strlen(pSource);
+	const size_t t = s + d;
+
+	EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
+		"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
+
+	if(t < nDestCapacity)
+		memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
+	else
+	{
+		if(nDestCapacity)
+		{
+			memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
+			pDestination[nDestCapacity - 1] = 0;
+		}
+	}
+
+	return t;
+}
+
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
+{
+	const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
+	const size_t s = Strlen(pSource);
+	const size_t t = s + d;
+
+	EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
+		"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
+
+	if(t < nDestCapacity)
+		memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
+	else
+	{
+		if(nDestCapacity)
+		{
+			memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
+			pDestination[nDestCapacity - 1] = 0;
+		}
+	}
+
+	return t;
+}
+
+
+
+
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char8_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = StrlenUTF8Decoded(pSource);
+	size_t destLen   = Strlen(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char8_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = StrlenUTF8Decoded(pSource);
+	size_t destLen   = Strlen(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+
+
+
+EASTDC_API size_t Strlcat(char8_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = Strlen(pSource);
+	size_t destLen   = StrlenUTF8Decoded(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+EASTDC_API size_t Strlcat(char8_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = Strlen(pSource);
+	size_t destLen   = StrlenUTF8Decoded(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+EASTDC_API size_t Strlcat(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = Strlen(pSource);
+	size_t destLen   = Strlen(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+EASTDC_API size_t Strlcat(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
+{
+	size_t sourceLen = Strlen(pSource);
+	size_t destLen   = Strlen(pDestination);
+
+	if(nDestCapacity > destLen)
+		Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
+	
+	return sourceLen + destLen;
+}
+
+
+
+// Optimized Strlen
+//
+// This function assumes that we can read the last size_t-sized word at
+// the end of the string, even if as many as three of the word bytes are
+// beyond the end of the string.  This is typically a valid assumption
+// because valid memory is always aligned to big power-of-2 sizes.
+//
+// Tests of this Strlen show that it outperforms the basic strlen 
+// implementation by 2x-6x on lengths ranging from 128 bytes to 4096 bytes.
+// At lengths under 10 bytes this strlen performs similarly to strlen.
+// These observations apply to x86, x64 and PowerPC32 platforms.
+//
+// There could be faster strlen implementations with some additional
+// tricks, asm, SSE, etc. But this version works well while being simple.
+
+#if EASTDC_STATIC_ANALYSIS_ENABLED
+	#define EASTDC_ENABLE_OPTIMIZED_STRLEN 0 // Disabled because the optimized strlen reads words and the string may have some uninitialized chars at the end past the trailing 0 char. Valgrind reports this as an error, but it's not actually an error in practice.
+#else
+	#define EASTDC_ENABLE_OPTIMIZED_STRLEN 1
+#endif
+
+#if EASTDC_ENABLE_OPTIMIZED_STRLEN
+
+	EASTDC_API size_t Strlen(const char8_t* pString)
+	{
+		#if EA_COMPILER_HAS_BUILTIN(__builtin_strlen)
+			return __builtin_strlen(pString);
+		#else
+			// Instead of casting between types, we just create a union.
+			union PointerUnion
+			{
+				const char8_t*   mp8;
+				const word_type* mpW;
+				uintptr_t        mU;
+			} pu;
+
+			// Leading unaligned bytes
+			for(pu.mp8 = pString; pu.mU & (sizeof(word_type) - 1); pu.mp8++)
+			{
+				if(*pu.mp8 == 0)
+					return (size_t)(pu.mp8 - pString);
+			}
+
+			for(; ; pu.mpW++)
+			{
+				#if defined(__GNUC__) && (__GNUC__ >= 3) && !defined(__EDG_VERSION__)
+					__builtin_prefetch(pu.mpW + 64, 0, 0);
+				#endif
+
+				// Quit if there are any zero char8_ts.
+				const word_type kOneBytes  = ((word_type)-1 / 0xff); // 0x01010101
+				const word_type kHighBytes = (kOneBytes * 0x80);     // 0x80808080
+
+				const word_type u = *pu.mpW;
+
+				if((u - kOneBytes) & ~u & kHighBytes)
+					break;
+			}
+
+			// Trailing unaligned bytes
+			while(*pu.mp8)
+				++pu.mp8;
+
+			return (size_t)(pu.mp8 - pString);
+		#endif
+	}
+
+#else
+
+	EASTDC_API size_t Strlen(const char8_t* pString)
+	{
+		ssize_t nLength = (size_t)-1; // EABase 1.0.14 and later recognize ssize_t for all platforms.
+
+		do
+		{
+			++nLength;
+		} while (*pString++);
+
+		return (size_t)nLength;
+	}
+
+#endif
+
+
+#if EASTDC_ENABLE_OPTIMIZED_STRLEN
+
+	EASTDC_API size_t Strlen(const char16_t* pString)
+	{
+		// Instead of casting between types, we just create a union.
+		union PointerUnion
+		{
+			const char16_t*  mp16;
+			const word_type* mpW;
+			uintptr_t        mU;
+		} pu;
+
+		// Leading unaligned bytes
+		for(pu.mp16 = pString; pu.mU & (sizeof(word_type) - 1); pu.mp16++)
+		{
+			if(*pu.mp16 == 0)
+				return (size_t)(pu.mp16 - pString);
+		}
+
+		for(; ; pu.mpW++)
+		{
+			#if defined(__GNUC__) && (__GNUC__ >= 3) && !defined(__EDG_VERSION__)
+				__builtin_prefetch(pu.mpW + 64, 0, 0);
+			#endif
+
+			// Quit if there are any zero char16_ts.
+			const word_type kOneBytes  = ((word_type)-1 / 0xffff); // 0x00010001
+			const word_type kHighBytes = (kOneBytes * 0x8000);     // 0x80008000
+
+			const word_type u = *pu.mpW;
+
+			if((u - kOneBytes) & ~u & kHighBytes)
+				break;
+		}
+
+		// Trailing unaligned bytes
+		while(*pu.mp16)
+			++pu.mp16;
+
+		return (size_t)(pu.mp16 - pString);
+	}
+
+#else
+
+	EASTDC_API size_t Strlen(const char16_t* pString)
+	{
+		size_t nLength = (size_t)-1;
+
+		do
+		{
+			++nLength;
+		} while (*pString++);
+
+		return nLength;
+	}
+
+#endif
+
+// To consider: This might benefit from an optimized implementation on machines withi 64 bit registers.
+EASTDC_API size_t Strlen(const char32_t* pString)
+{
+	size_t nLength = (size_t)-1;
+	
+	do{
+		++nLength;
+	}while(*pString++);
+	
+	return nLength;
+}
+
+
+
+
+
+// Returns number of Unicode characters are in the UTF8-encoded string.
+// Return value will be <= Strlen(pString).
+EASTDC_API size_t StrlenUTF8Decoded(const char8_t* pString)
+{
+	size_t nLength = 0;
+
+	while(*pString)
+	{
+		if((*pString & 0xc0) != 0x80)
+			++nLength;
+		++pString;
+	}
+
+	return nLength;
+}
+
+
+// Returns number of characters that would be in a UTF8-encoded string.
+// Return value will be >= Strlen(pString).
+EASTDC_API size_t StrlenUTF8Encoded(const char16_t* pString)
+{
+	size_t   nLength = 0;
+	uint32_t c;
+
+	while((c = *pString++) != 0)
+	{
+		if(c < 0x00000080)
+			nLength += 1;
+		else if(c < 0x00000800)
+			nLength += 2;
+		else // if(c < 0x00010000) 
+			nLength += 3;
+
+		// The following would be used if the input string was 32 bit instead of 16 bit.
+		//else if(c < 0x00200000)
+		//    destCount += 4;
+		//else                    // Else we use the error char 0xfffd
+		//    destCount += 3;
+	}
+
+	return nLength;
+}
+
+// Returns number of characters that would be in a UTF8-encoded string.
+// Return value will be >= Strlen(pString).
+EASTDC_API size_t StrlenUTF8Encoded(const char32_t* pString)
+{
+	size_t   nLength = 0;
+	uint32_t c;
+
+	while((c = *pString++) != 0)
+	{
+		if(c < 0x00000080)
+			nLength += 1;
+		else if(c < 0x00000800)
+			nLength += 2;
+		else // if(c < 0x00010000) 
+			nLength += 3;
+
+		// The following would be used if the input string was 32 bit instead of 32 bit.
+		//else if(c < 0x00200000)
+		//    destCount += 4;
+		//else                    // Else we use the error char 0xfffd
+		//    destCount += 3;
+	}
+
+	return nLength;
+}
+
+
+
+EASTDC_API char8_t* Strend(const char8_t* pString)
+{
+	while (*pString)
+		++pString;
+
+	return (char8_t*)pString;
+}
+
+EASTDC_API char16_t* Strend(const char16_t* pString)
+{
+	while (*pString)
+		++pString;
+
+	return (char16_t*)pString;
+}
+
+EASTDC_API char32_t* Strend(const char32_t* pString)
+{
+	while(*pString)
+		++pString;
+
+	return (char32_t*)pString;
+}
+
+
+
+EASTDC_API size_t Strxfrm(char8_t* pDest, const char8_t* pSource, size_t n)
+{
+	const size_t nLength = Strlen(pSource);
+
+	if(n > 0)
+	{
+		Strncpy(pDest, pSource, n - 1);
+		if(n < nLength)
+			pDest[n - 1] = 0;
+	}
+
+	return nLength;
+}
+
+EASTDC_API size_t Strxfrm(char16_t* pDest, const char16_t* pSource, size_t n)
+{
+	const size_t nLength = Strlen(pSource);
+
+	if(n > 0)
+	{
+		Strncpy(pDest, pSource, n - 1);
+		if(n < nLength)
+			pDest[n - 1] = 0;
+	}
+
+	return nLength;
+}
+
+EASTDC_API size_t Strxfrm(char32_t* pDest, const char32_t* pSource, size_t n)
+{
+	const size_t nLength = Strlen(pSource);
+
+	if(n > 0)
+	{
+		Strncpy(pDest, pSource, n - 1);
+		if(n < nLength)
+			pDest[n - 1] = 0;
+	}
+
+	return nLength;
+}
+
+
+
+EASTDC_API char8_t* Strdup(const char8_t* pString)
+{
+	if(pString)
+	{
+		const size_t nLength = Strlen(pString);
+		char8_t* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char8_t[nLength + 1]; // '+ 1' to include terminating zero.
+
+		Strcpy(p, pString);
+		return p;
+	}
+
+	return NULL;
+}
+
+EASTDC_API char16_t* Strdup(const char16_t* pString)
+{
+	if(pString)
+	{
+		const size_t nLength = Strlen(pString);
+		char16_t* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char16_t[nLength + 1]; // '+ 1' to include terminating zero.
+
+		Strcpy(p, pString);
+		return p;
+	}
+
+	return NULL;
+}
+
+EASTDC_API char32_t* Strdup(const char32_t* pString)
+{
+	if(pString)
+	{
+		const size_t nLength = Strlen(pString);
+		char32_t* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char32_t[nLength + 1]; // '+ 1' to include terminating zero.
+
+		Strcpy(p, pString);
+		return p;
+	}
+
+	return NULL;
+}
+
+
+
+EASTDC_API void Strdel(char8_t* pString)
+{
+	EASTDC_DELETE[] pString;
+}
+
+EASTDC_API void Strdel(char16_t* pString)
+{
+	EASTDC_DELETE[] pString;
+}
+
+EASTDC_API void Strdel(char32_t* pString)
+{
+	EASTDC_DELETE[] pString;
+}
+
+
+
+
+EASTDC_API char8_t* Strupr(char8_t* pString)
+{
+	// This implementation converts only 7 bit ASCII characters.
+	// As such it is safe to use with 7-bit-safe multibyte encodings
+	// such as UTF8 but may yield incorrect results with such text.
+	char8_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		if((uint8_t)*pStringTemp <= 127)
+			*pStringTemp = (char8_t)Toupper(*pStringTemp);
+		++pStringTemp;
+	}
+
+	return pString;
+}
+
+EASTDC_API char16_t* Strupr(char16_t* pString)
+{
+	char16_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		const char16_t c = *pStringTemp;
+		*pStringTemp++ = Toupper(c);
+	}
+
+	return pString;
+}
+
+EASTDC_API char32_t* Strupr(char32_t* pString)
+{
+	char32_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		const char32_t c = *pStringTemp;
+		*pStringTemp++ = Toupper(c);
+	}
+
+	return pString;
+}
+
+
+
+
+EASTDC_API char8_t* Strlwr(char8_t* pString)
+{
+	// This implementation converts only 7 bit ASCII characters.
+	// As such it is safe to use with 7-bit-safe multibyte encodings
+	// such as UTF8 but may yield incorrect results with such text.
+	char8_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		if((uint8_t)*pStringTemp <= 127)
+			*pStringTemp = (char8_t)Tolower(*pStringTemp);
+		++pStringTemp;
+	}
+
+	return pString;
+}
+
+EASTDC_API char16_t* Strlwr(char16_t* pString)
+{
+	char16_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		const char16_t c = *pStringTemp;
+		*pStringTemp++ = Tolower(c);
+	}
+
+	return pString;
+}
+
+EASTDC_API char32_t* Strlwr(char32_t* pString)
+{
+	char32_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+	{
+		const char32_t c = *pStringTemp;
+		*pStringTemp++ = Tolower(c);
+	}
+
+	return pString;
+}
+
+
+
+
+EASTDC_API char8_t* Strmix(char8_t* pDestination, const char8_t* pSource, const char8_t* pDelimiters)
+{
+	bool bCapitalize = true;
+	char8_t* const pOriginalDest = pDestination;
+
+	while(*pSource)
+	{
+		char8_t c = *pSource++;
+
+		// This character is upper-cased if bCapitalize flag is true, else lower-cased
+		if(bCapitalize)
+		{
+			if(Islower(c))
+			{
+				c = Toupper(c);
+				bCapitalize = false;
+			}
+			else if(Isupper(c))
+				bCapitalize = false;
+		}
+		else
+		{
+			if(Isupper(c))
+				c = Tolower(c);
+		}
+
+		// Check whether this character is a separator character. If so, set the bCapitalize flag.
+		for(const char8_t* pCheck = pDelimiters; *pCheck; ++pCheck)
+		{
+			if(c == *pCheck)
+				bCapitalize = true;
+		}
+
+		*pDestination++ = c;
+	}
+
+	*pDestination = 0;
+	return pOriginalDest;
+}
+
+EASTDC_API char16_t* Strmix(char16_t* pDestination, const char16_t* pSource, const char16_t* pDelimiters)
+{
+	bool bCapitalize = true;
+	char16_t* const pOriginalDest = pDestination;
+
+	while(*pSource)
+	{
+		char16_t c = *pSource++;
+
+		// This character is upper-cased if bCapitalize flag is true, else lower-cased
+		if(bCapitalize)
+		{
+			if(Islower(c))
+			{
+				c = Toupper(c);
+				bCapitalize = false;
+			}
+			else if(Isupper(c))
+				bCapitalize = false;
+		}
+		else
+		{
+			if(Isupper(c))
+				c = Tolower(c);
+		}
+
+		// Check whether this character is a separator character. If so, set the bCapitalize flag.
+		for(const char16_t* pCheck = pDelimiters; *pCheck; ++pCheck)
+		{
+			if(c == *pCheck)
+				bCapitalize = true;
+		}
+
+		*pDestination++ = c;
+	}
+
+	*pDestination = 0;
+	return pOriginalDest;
+}
+
+EASTDC_API char32_t* Strmix(char32_t* pDestination, const char32_t* pSource, const char32_t* pDelimiters)
+{
+	bool bCapitalize = true;
+	char32_t* const pOriginalDest = pDestination;
+
+	while(*pSource)
+	{
+		char32_t c = *pSource++;
+
+		// This character is upper-cased if bCapitalize flag is true, else lower-cased
+		if(bCapitalize)
+		{
+			if(Islower(c))
+			{
+				c = Toupper(c);
+				bCapitalize = false;
+			}
+			else if(Isupper(c))
+				bCapitalize = false;
+		}
+		else
+		{
+			if(Isupper(c))
+				c = Tolower(c);
+		}
+
+		// Check whether this character is a separator character. If so, set the bCapitalize flag.
+		for(const char32_t* pCheck = pDelimiters; *pCheck; ++pCheck)
+		{
+			if(c == *pCheck)
+				bCapitalize = true;
+		}
+
+		*pDestination++ = c;
+	}
+
+	*pDestination = 0;
+	return pOriginalDest;
+}
+
+
+
+
+EASTDC_API char8_t* Strchr(const char8_t* pString, int c)
+{
+	do
+	{
+		if (*pString == c)
+			return (char8_t*)pString;
+	} while (*pString++);
+
+	return NULL;
+}
+
+EASTDC_API char16_t* Strchr(const char16_t* pString, char16_t c)
+{
+	do
+	{
+		if (*pString == c)
+			return (char16_t*)pString;
+	} while (*pString++);
+
+	return NULL;
+}
+
+EASTDC_API char32_t* Strchr(const char32_t* pString, char32_t c)
+{
+	do {
+		if(*pString == c)
+			return (char32_t*)pString;
+	} while (*pString++);
+
+	return NULL;
+}
+
+
+
+
+EASTDC_API char8_t* Strnchr(const char8_t* pString, int c, size_t n)
+{
+	while(n-- > 0)
+	{
+		if(*pString == c)
+		{
+			return (char8_t*)pString;
+		}
+		if(*pString == '\0')
+		{
+			return NULL;
+		}
+		pString++;
+	}
+	return NULL;
+}
+
+EASTDC_API char16_t* Strnchr(const char16_t* pString, char16_t c, size_t n)
+{
+	while(n-- > 0)
+	{
+		if(*pString == c)
+		{
+			return (char16_t*)pString;
+		}
+		if(*pString == '\0')
+		{
+			return NULL;
+		}
+		pString++;
+	}
+	return NULL;
+}
+
+EASTDC_API char32_t* Strnchr(const char32_t* pString, char32_t c, size_t n)
+{
+	while(n-- > 0)
+	{
+		if(*pString == c)
+		{
+			return (char32_t*)pString;
+		}
+		if(*pString == '\0')
+		{
+			return NULL;
+		}
+		pString++;
+	}
+	return NULL;
+}
+
+
+
+
+EASTDC_API size_t Strcspn(const char8_t* pString1, const char8_t* pString2)
+{
+	const char8_t* pStringCurrent = pString1;
+
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2.
+	while(*pStringCurrent)
+	{
+		for(const char8_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pStringCurrent)
+				return (size_t)(pStringCurrent - pString1);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString1);
+}
+
+EASTDC_API size_t Strcspn(const char16_t* pString1, const char16_t* pString2)
+{
+	const char16_t* pStringCurrent = pString1;
+
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char16_t
+	// means that the map would have to be 65536 bits (8192 bytes) in size.
+	while(*pStringCurrent)
+	{
+		for(const char16_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pStringCurrent)
+				return (size_t)(pStringCurrent - pString1);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString1);
+}
+
+EASTDC_API size_t Strcspn(const char32_t* pString1, const char32_t* pString2)
+{
+	const char32_t* pStringCurrent = pString1;
+
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char32_t
+	// means that the map would have to be huge in size.
+	while(*pStringCurrent)
+	{
+		for(const char32_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pStringCurrent)
+				return (size_t)(pStringCurrent - pString1);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString1);
+}
+
+
+
+
+EASTDC_API char8_t* Strpbrk(const char8_t* pString1, const char8_t* pString2)
+{
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2.
+	while(*pString1)
+	{
+		for(const char8_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pString1)
+				return (char8_t*)pString1;
+		}
+
+		++pString1;
+	}
+
+	return NULL;
+}
+
+EASTDC_API char16_t* Strpbrk(const char16_t* pString1, const char16_t* pString2)
+{
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char16_t
+	// means that the map would have to be 65536 bits (8192 bytes) in size.
+	while(*pString1)
+	{
+		for(const char16_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pString1)
+				return (char16_t*)pString1;
+		}
+
+		++pString1;
+	}
+
+	return NULL;
+}
+
+EASTDC_API char32_t* Strpbrk(const char32_t* pString1, const char32_t* pString2)
+{
+	// This implementation does a double loop. As such, it can get slow for
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char32_t
+	// means that the map would have to be huge in size.
+	while(*pString1)
+	{
+		for(const char32_t* pCharSet = pString2; *pCharSet; ++pCharSet)
+		{
+			if(*pCharSet == *pString1)
+				return (char32_t*)pString1;
+		}
+
+		++pString1;
+	}
+
+	return NULL;
+}
+
+
+EASTDC_API char8_t* Strrchr(const char8_t* pString, int c)
+{
+	const char8_t* pFound = NULL;
+	char8_t cCurrent;
+
+	while ((cCurrent = *pString++) != 0)
+	{
+		if (cCurrent == c)
+			pFound = (pString - 1);
+	}
+
+	if (pFound)
+		return (char8_t*)pFound;
+
+	return c ? NULL : (char8_t*)(pString - 1);
+}
+
+EASTDC_API char16_t* Strrchr(const char16_t* pString, char16_t c)
+{
+	const char16_t* pFound = NULL;
+	char16_t cCurrent;
+
+	while ((cCurrent = *pString++) != 0)
+	{
+		if (cCurrent == c)
+			pFound = (pString - 1);
+	}
+
+	if (pFound)
+		return (char16_t*)pFound;
+
+	return c ? NULL : (char16_t*)(pString - 1);
+}
+
+EASTDC_API char32_t* Strrchr(const char32_t* pString, char32_t c)
+{
+	const char32_t* pFound = NULL;
+	char32_t        cCurrent;
+
+	while ((cCurrent = *pString++) != 0)
+	{
+		if (cCurrent == c)
+			pFound = (pString - 1);
+	}
+
+	if (pFound)
+		return (char32_t*)pFound;
+
+	return c ? NULL : (char32_t*)(pString - 1);
+}
+
+
+EASTDC_API size_t Strspn(const char8_t* pString, const char8_t* pSubString)
+{
+	// This implementation does a double loop. As such, it can get slow for 
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. 
+	const char8_t* pStringCurrent = pString;
+
+	while(*pStringCurrent)
+	{
+		for(const char8_t* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
+		{
+			if(*pSubStringCurrent == 0)
+				return (size_t)(pStringCurrent - pString);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString);
+}
+
+EASTDC_API size_t Strspn(const char16_t* pString, const char16_t* pSubString)
+{
+	// This implementation does a double loop. As such, it can get slow for 
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char16_t 
+	// means that the map would have to be 65536 bits (8192 bytes) in size.
+	const char16_t* pStringCurrent = pString;
+
+	while(*pStringCurrent)
+	{
+		for(const char16_t* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
+		{
+			if(*pSubStringCurrent == 0)
+				return (size_t)(pStringCurrent - pString);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString);
+}
+
+EASTDC_API size_t Strspn(const char32_t* pString, const char32_t* pSubString)
+{
+	// This implementation does a double loop. As such, it can get slow for 
+	// very long strings. An alternative implementation is to use a hash
+	// table or to create a bytemap of the chars in pString2. But char32_t 
+	// means that the map would have to be huge in size.
+	const char32_t* pStringCurrent = pString;
+
+	while(*pStringCurrent)
+	{
+		for(const char32_t* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
+		{
+			if(*pSubStringCurrent == 0)
+				return (size_t)(pStringCurrent - pString);
+		}
+
+		++pStringCurrent;
+	}
+
+	return (size_t)(pStringCurrent - pString);
+}
+
+
+
+EASTDC_API char8_t* Strstr(const char8_t* pString, const char8_t* pSubString)
+{
+	char8_t* s1 = (char8_t*)pString - 1;
+	char8_t* p1 = (char8_t*)pSubString - 1;
+	char8_t  c0, c1, c2;
+
+	if((c0 = *++p1) == 0)   // An empty pSubString results in success, return pString.
+		return (char8_t*)pString;
+
+	while((c1 = *++s1) != 0)
+	{
+		if(c1 == c0)
+		{
+			const char8_t* s2 = (s1 - 1);
+			const char8_t* p2 = (p1 - 1);
+			
+			while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
+
+			if(!c2)
+				return (char8_t*)s1;
+		}
+	}
+	return NULL;
+}
+
+EASTDC_API char16_t* Strstr(const char16_t* pString, const char16_t* pSubString)
+{
+	char16_t* s1 = (char16_t*)pString - 1;
+	char16_t* p1 = (char16_t*)pSubString - 1;
+	char16_t  c0, c1, c2;
+
+	if((c0 = *++p1) == 0)   // An empty pSubString results in success, return pString.
+		return (char16_t*)pString;
+
+	while((c1 = *++s1) != 0)
+	{
+		if(c1 == c0)
+		{
+			const char16_t* s2 = (s1 - 1);
+			const char16_t* p2 = (p1 - 1);
+			
+			while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
+
+			if(!c2)
+				return (char16_t*)s1;
+		}
+	}
+	return NULL;
+}
+
+EASTDC_API char32_t* Strstr(const char32_t* pString, const char32_t* pSubString)
+{
+	char32_t* s1 = (char32_t*)pString - 1;
+	char32_t* p1 = (char32_t*)pSubString - 1;
+	char32_t  c0, c1, c2;
+
+	if((c0 = *++p1) == 0)   // An empty pSubString results in success, return pString.
+		return (char32_t*)pString;
+
+	while((c1 = *++s1) != 0)
+	{
+		if(c1 == c0)
+		{
+			const char32_t* s2 = (s1 - 1);
+			const char32_t* p2 = (p1 - 1);
+			
+			while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
+
+			if(!c2)
+				return (char32_t*)s1;
+		}
+	}
+	return NULL;
+}
+
+
+
+EASTDC_API char8_t* Stristr(const char8_t* s1, const char8_t* s2)
+{
+	const char8_t* cp = s1;
+
+	if(!*s2)
+		return (char8_t*)s1;
+
+	while(*cp)
+	{
+		const char8_t* s = cp;
+		const char8_t* t = s2;
+
+		while(*s && *t && (Tolower(*s) == Tolower(*t)))
+			++s, ++t;
+
+		if(*t == 0)
+			return (char8_t*)cp;
+		++cp;
+	}
+
+	return 0;
+}
+
+EASTDC_API char16_t* Stristr(const char16_t* s1, const char16_t* s2)
+{
+	const char16_t* cp = s1;
+
+	if(!*s2)
+		return (char16_t*)s1;
+
+	while(*cp)
+	{
+		const char16_t* s = cp;
+		const char16_t* t = s2;
+
+		while(*s && *t && (Tolower(*s) == Tolower(*t)))
+			++s, ++t;
+
+		if(*t == 0)
+			return (char16_t*)cp;
+		++cp;
+	}
+
+	return 0;
+}
+
+EASTDC_API char32_t* Stristr(const char32_t* s1, const char32_t* s2)
+{
+	const char32_t* cp = s1;
+
+	if(!*s2)
+		return (char32_t*)s1;
+
+	while(*cp)
+	{
+		const char32_t* s = cp;
+		const char32_t* t = s2;
+
+		while(*s && *t && (Tolower(*s) == Tolower(*t)))
+			++s, ++t;
+
+		if(*t == 0)
+			return (char32_t*)cp;
+		++cp;
+	}
+
+	return 0;
+}
+
+
+
+
+EASTDC_API char8_t* Strrstr(const char8_t* s1, const char8_t* s2) 
+{
+	if(!*s2)
+		return (char8_t*)s1;
+
+	const char8_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char8_t* psc1 = --ps1;
+		const char8_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(*psc1++ != *sc2++)
+				break;
+			else if(!*sc2)
+				return (char8_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+EASTDC_API char16_t* Strrstr(const char16_t* s1, const char16_t* s2) 
+{
+	if(!*s2)
+		return (char16_t*)s1;
+
+	const char16_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char16_t* psc1 = --ps1;
+		const char16_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(*psc1++ != *sc2++)
+				break;
+			else if(!*sc2)
+				return (char16_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+EASTDC_API char32_t* Strrstr(const char32_t* s1, const char32_t* s2) 
+{
+	if(!*s2)
+		return (char32_t*)s1;
+
+	const char32_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char32_t* psc1 = --ps1;
+		const char32_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(*psc1++ != *sc2++)
+				break;
+			else if(!*sc2)
+				return (char32_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+
+
+
+EASTDC_API char8_t* Strirstr(const char8_t* s1, const char8_t* s2)
+{
+	if(!*s2)
+		return (char8_t*)s1;
+
+	const char8_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char8_t* psc1 = --ps1;
+		const char8_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(Tolower(*psc1++) != Tolower(*sc2++))
+				break;
+			else if(!*sc2)
+				return (char8_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+EASTDC_API char16_t* Strirstr(const char16_t* s1, const char16_t* s2)
+{
+	if(!*s2)
+		return (char16_t*)s1;
+
+	const char16_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char16_t* psc1 = --ps1;
+		const char16_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(Tolower(*psc1++) != Tolower(*sc2++))
+				break;
+			else if(!*sc2)
+				return (char16_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+EASTDC_API char32_t* Strirstr(const char32_t* s1, const char32_t* s2)
+{
+	if(!*s2)
+		return (char32_t*)s1;
+
+	const char32_t* ps1 = s1 + Strlen(s1);
+
+	while(ps1 != s1) 
+	{
+		const char32_t* psc1 = --ps1;
+		const char32_t* sc2  = s2;
+
+		for(;;)
+		{
+			if(Tolower(*psc1++) != Tolower(*sc2++))
+				break;
+			else if(!*sc2)
+				return (char32_t*)ps1;
+		}
+	}
+
+	return 0;
+}
+
+
+
+EASTDC_API bool Strstart(const char8_t* pString, const char8_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(*pString++ != *pPrefix++)
+			return false;
+	}
+
+	return true;
+}
+
+
+EASTDC_API bool Strstart(const char16_t* pString, const char16_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(*pString++ != *pPrefix++)
+			return false;
+	}
+
+	return true;
+}
+
+
+EASTDC_API bool Strstart(const char32_t* pString, const char32_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(*pString++ != *pPrefix++)
+			return false;
+	}
+
+	return true;
+}
+
+
+
+EASTDC_API bool Stristart(const char8_t* pString, const char8_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(Tolower(*pString++) != Tolower(*pPrefix++))
+			return false;
+	}
+
+	return true;
+}
+
+
+EASTDC_API bool Stristart(const char16_t* pString, const char16_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(Tolower(*pString++) != Tolower(*pPrefix++))
+			return false;
+	}
+
+	return true;
+}
+
+
+EASTDC_API bool Stristart(const char32_t* pString, const char32_t* pPrefix)
+{
+	while(*pPrefix)
+	{
+		if(Tolower(*pString++) != Tolower(*pPrefix++))
+			return false;
+	}
+
+	return true;
+}
+
+
+
+
+EASTDC_API bool Strend(const char8_t* pString, const char8_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char8_t)) == 0;
+
+	return false;
+}
+
+
+EASTDC_API bool Strend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char16_t)) == 0;
+
+	return false;
+}
+
+
+EASTDC_API bool Strend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char32_t)) == 0;
+
+	return false;
+}
+
+
+EASTDC_API bool Striend(const char8_t* pString, const char8_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
+
+	return false;
+}
+
+
+EASTDC_API bool Striend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
+
+	return false;
+}
+
+
+EASTDC_API bool Striend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength, size_t suffixLength)
+{
+	if(stringLength == kSizeTypeUnset)
+		stringLength = Strlen(pString);
+
+	if(suffixLength == kSizeTypeUnset)
+		suffixLength = Strlen(pSuffix);
+
+	if(stringLength >= suffixLength)
+		return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
+
+	return false;
+}
+
+
+///////////////////////////////////////////////////////////////////
+// This function was implemented by Avery Lee.
+//
+EASTDC_API char8_t* Strtok(char8_t* pString, const char8_t* pDelimiters, char8_t** pContext)
+{
+	// find point on string to resume
+	char8_t* s = pString;
+
+	if(!s)
+	{
+		s = *pContext;
+		if(!s)
+			return NULL;
+	}
+
+	// Compute bit hash based on lower 5 bits of delimiter characters
+	const char8_t* d = pDelimiters;
+	int32_t        hash = 0;
+	uint32_t       delimiterCount = 0;
+
+	while(const char8_t c = *d++)
+	{
+		hash |= (int32_t)(0x80000000 >> (c & 31));
+		++delimiterCount;
+	}
+
+	// Skip delimiters
+	for(;;)
+	{
+		const char8_t c = *s;
+
+		// If we hit the end of the string, it ends solely with delimiters
+		// and there are no more tokens to get.
+		if(!c)
+		{
+			*pContext = NULL;
+			return NULL;
+		}
+
+		// Fast rejection against hash set
+		if(int32_t(uint64_t(hash) << (c & 31)) >= 0)
+			break;
+
+		// brute-force search against delimiter list
+		for(uint32_t i=0; i<delimiterCount; ++i)
+		{
+			if (pDelimiters[i] == c)    // Is it a delimiter? ...
+				goto still_delimiters;  // yes, continue the loop
+		}
+
+		// Not a token, so exit
+		break;
+
+	still_delimiters:
+		++s;
+	}
+
+	// Mark beginning of token
+	char8_t* const pToken = s;
+
+	// Search for end of token
+	while(const char8_t c = *s)
+	{
+		// Fast rejection against hash set
+		if(int32_t(int64_t(hash) << (c & 31)) < 0)
+		{
+			// Brute-force search against delimiter list
+			for(uint32_t i=0; i<delimiterCount; ++i)
+			{
+				if(pDelimiters[i] == c)
+				{
+					// This token ends with a delimiter.
+					*s = 0;                 // null-term substring
+					*pContext = (s + 1);    // restart on next byte
+					return pToken;          // return found token
+				}
+			}
+		}
+
+		++s;
+	}
+
+	// We found a token but it was at the end of the string, 
+	// so we null out the context and return the last token.
+	*pContext = NULL;           // no more tokens
+	return pToken;              // return found token
+}
+
+EASTDC_API char16_t* Strtok(char16_t* pString, const char16_t* pDelimiters, char16_t** pContext)
+{
+	// Find point on string to resume
+	char16_t* s = pString;
+
+	if(!s)
+	{
+		s = *pContext;
+		if(!s)
+			return NULL;
+	}
+
+	// compute bit hash based on lower 5 bits of delimiter characters
+	const char16_t* d = pDelimiters;
+	int32_t         hash = 0;
+	uint32_t        delimiterCount = 0;
+
+	while(const char16_t c = *d++)
+	{
+		hash |= (int32_t)(0x80000000 >> (c & 31));
+		++delimiterCount;
+	}
+
+	// Skip delimiters
+	for(;;)
+	{
+		const char16_t c = *s;
+
+		// If we hit the end of the string, it ends solely with delimiters
+		// and there are no more tokens to get.
+		if(!c)
+		{
+			*pContext = NULL;
+			return NULL;
+		}
+
+		// Fast rejection against hash set
+		if(int32_t(int64_t(hash) << (c & 31)) >= 0)
+			break;
+
+		// Brute-force search against delimiter list
+		for(uint32_t i=0; i<delimiterCount; ++i)
+		{
+			if(pDelimiters[i] == (char16_t)c)    // Is it a delimiter? ...
+				goto still_delimiters;           // yes, continue the loop
+		}
+
+		// Not a token, so exit
+		break;
+
+	still_delimiters:
+		++s;
+	}
+
+	// Mark beginning of token
+	char16_t* const pToken = s;
+
+	// Search for end of token
+	while(const char16_t c = *s)
+	{
+		// Fast rejection against hash set
+		if(int32_t(int64_t(hash) << (c & 31)) < 0)
+		{
+			// Brute-force search against delimiter list
+			for(uint32_t i=0; i<delimiterCount; ++i)
+			{
+				if(pDelimiters[i] == c)
+				{
+					// This token ends with a delimiter.
+					*s = 0;                 // null-term substring
+					*pContext = (s + 1);    // restart on next byte
+					return pToken;          // return found token
+				}
+			}
+		}
+
+		++s;
+	}
+
+	// We found a token but it was at the end of the string, 
+	// so we null out the context and return the last token.
+	*pContext = NULL;           // no more tokens
+	return pToken;              // return found token
+}
+
+EASTDC_API char32_t* Strtok(char32_t* pString, const char32_t* pDelimiters, char32_t** pContext)
+{
+	// Find point on string to resume
+	char32_t* s = pString;
+
+	if(!s)
+	{
+		s = *pContext;
+		if(!s)
+			return NULL;
+	}
+
+	// compute bit hash based on lower 5 bits of delimiter characters
+	const char32_t* d = pDelimiters;
+	int32_t         hash = 0;
+	uint32_t        delimiterCount = 0;
+
+	while(const uint32_t c = (uint32_t)*d++)
+	{
+		hash |= (int32_t)(0x80000000 >> (c & 31));
+		++delimiterCount;
+	}
+
+	// Skip delimiters
+	for(;;)
+	{
+		const char32_t c = *s;
+
+		// If we hit the end of the string, it ends solely with delimiters
+		// and there are no more tokens to get.
+		if(!c)
+		{
+			*pContext = NULL;
+			return NULL;
+		}
+
+		// Fast rejection against hash set
+		if(int32_t(int64_t(hash) << (c & 31)) >= 0)
+			break;
+
+		// Brute-force search against delimiter list
+		for(uint32_t i=0; i<delimiterCount; ++i)
+		{
+			if(pDelimiters[i] == c)    // Is it a delimiter? ...
+				goto still_delimiters;  // yes, continue the loop
+		}
+
+		// Not a token, so exit
+		break;
+
+	still_delimiters:
+		++s;
+	}
+
+	// Mark beginning of token
+	char32_t* const pToken = s;
+
+	// Search for end of token
+	while(const uint32_t c = (uint32_t)*s)
+	{
+		// Fast rejection against hash set
+		if(int32_t(int64_t(hash) << (c & 31)) < 0)
+		{
+			// Brute-force search against delimiter list
+			for(uint32_t i=0; i<delimiterCount; ++i)
+			{
+				if(pDelimiters[i] == (char32_t)c)
+				{
+					// This token ends with a delimiter.
+					*s = 0;                 // null-term substring
+					*pContext = (s + 1);    // restart on next byte
+					return pToken;          // return found token
+				}
+			}
+		}
+
+		++s;
+	}
+
+	// We found a token but it was at the end of the string, 
+	// so we null out the context and return the last token.
+	*pContext = NULL;           // no more tokens
+	return pToken;              // return found token
+}
+
+
+
+EASTDC_API const char8_t* Strtok2(const char8_t* pString, const char8_t* pDelimiters, 
+								  size_t* pResultLength, bool bFirst)
+{
+	// Skip any non-delimiters
+	if(!bFirst)
+	{
+		while(*pString && !Strchr(pDelimiters, *pString))
+			++pString;
+	}
+
+	// Skip any delimiters
+	while(*pString && Strchr(pDelimiters, *pString))
+		++pString;
+
+	const char8_t* const pBegin = pString;
+
+	// Calculate the length of the string
+	while(*pString && !Strchr(pDelimiters, *pString))
+		++pString;
+
+	if(pBegin != pString)
+	{
+		*pResultLength = static_cast<size_t>(pString - pBegin);
+		return pBegin;
+	}
+
+	*pResultLength = 0;
+	return NULL;
+}
+
+EASTDC_API const char16_t* Strtok2(const char16_t* pString, const char16_t* pDelimiters, size_t* pResultLength, bool bFirst)
+{
+	// Skip any non-delimiters
+	if(!bFirst)
+	{
+		while(*pString && !Strchr(pDelimiters, *pString))
+			++pString;
+	}
+
+	// Skip any delimiters
+	while(*pString && Strchr(pDelimiters, *pString))
+		++pString;
+
+	const char16_t* const pBegin = pString;
+
+	// Calculate the length of the string
+	while(*pString && !Strchr(pDelimiters, *pString))
+		++pString;
+
+	if(pBegin != pString)
+	{
+		*pResultLength = static_cast<size_t>(pString - pBegin);
+		return pBegin;
+	}
+
+	*pResultLength = 0;
+	return NULL;
+}
+
+EASTDC_API const char32_t* Strtok2(const char32_t* pString, const char32_t* pDelimiters, size_t* pResultLength, bool bFirst)
+{
+	// Skip any non-delimiters
+	if(!bFirst)
+	{
+		while(*pString && !Strchr(pDelimiters, *pString))
+			++pString;
+	}
+
+	// Skip any delimiters
+	while(*pString && Strchr(pDelimiters, *pString))
+		++pString;
+
+	const char32_t* const pBegin = pString;
+
+	// Calculate the length of the string
+	while(*pString && !Strchr(pDelimiters, *pString))
+		++pString;
+
+	if(pBegin != pString)
+	{
+		*pResultLength = static_cast<size_t>(pString - pBegin);
+		return pBegin;
+	}
+
+	*pResultLength = 0;
+	return NULL;
+}
+
+
+
+EASTDC_API char8_t* Strset(char8_t* pString, int c)
+{
+	char8_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+		*pStringTemp++ = (char8_t)c;
+
+	return pString;
+}
+
+EASTDC_API char16_t* Strset(char16_t* pString, char16_t c)
+{
+	char16_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+		*pStringTemp++ = c;
+
+	return pString;
+}
+
+EASTDC_API char32_t* Strset(char32_t* pString, char32_t c)
+{
+	char32_t* pStringTemp = pString;
+
+	while(*pStringTemp)
+		*pStringTemp++ = c;
+
+	return pString;
+}
+
+
+
+EASTDC_API char8_t* Strnset(char8_t* pString, int c, size_t n)
+{
+	char8_t* pSaved = pString;
+
+	for(size_t i = 0; *pString && (i < n); ++i)
+		*pString++ = (char8_t)c;
+
+	return pSaved;
+}
+
+EASTDC_API char16_t* Strnset(char16_t* pString, char16_t c, size_t n)
+{
+	char16_t* pSaved = pString;
+
+	for(size_t i = 0; *pString && (i < n); ++i)
+		*pString++ = c;
+
+	return pSaved;
+}
+
+EASTDC_API char32_t* Strnset(char32_t* pString, char32_t c, size_t n)
+{
+	char32_t* pSaved = pString;
+
+	for(size_t i = 0; *pString && (i < n); ++i)
+		*pString++ = c;
+
+	return pSaved;
+}
+
+
+
+EASTDC_API char8_t* Strrev(char8_t* pString)
+{
+	for(char8_t* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
+	{
+		char8_t c = *p2;
+		*p2 = *p1;
+		*p1 = c;
+	}
+
+	return pString;
+}
+
+EASTDC_API char16_t* Strrev(char16_t* pString)
+{
+	for(char16_t* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
+	{
+		char16_t c = *p2;
+		*p2 = *p1;
+		*p1 = c;
+	}
+
+	return pString;
+}
+
+EASTDC_API char32_t* Strrev(char32_t* pString)
+{
+	for(char32_t* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
+	{
+		char32_t c = *p2;
+		*p2 = *p1;
+		*p1 = c;
+	}
+
+	return pString;
+}
+
+
+EASTDC_API char8_t* Strstrip(char8_t* pString)
+{
+	// Walk forward from the beginning and find the first non-whitespace.
+	while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
+		++pString;
+
+	if(*pString)
+	{
+		// Walk backward from the end and find the last whitespace.
+		size_t   length = EA::StdC::Strlen(pString);
+		char8_t* pEnd   = (pString + length) - 1;
+
+		while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
+			pEnd--;
+
+		pEnd[1] = '\0';
+	}
+
+	return pString;
+}
+
+
+EASTDC_API char16_t* Strstrip(char16_t* pString)
+{
+	// Walk forward from the beginning and find the first non-whitespace.
+	while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
+		++pString;
+
+	if(*pString)
+	{
+		// Walk backward from the end and find the last whitespace.
+		size_t    length = EA::StdC::Strlen(pString);
+		char16_t* pEnd   = (pString + length) - 1;
+
+		while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
+			pEnd--;
+
+		pEnd[1] = '\0';
+	}
+
+	return pString;
+}
+
+
+EASTDC_API char32_t* Strstrip(char32_t* pString)
+{
+	// Walk forward from the beginning and find the first non-whitespace.
+	while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
+		++pString;
+
+	if(*pString)
+	{
+		// Walk backward from the end and find the last whitespace.
+		size_t    length = EA::StdC::Strlen(pString);
+		char32_t* pEnd   = (pString + length) - 1;
+
+		while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
+			pEnd--;
+
+		pEnd[1] = '\0';
+	}
+
+	return pString;
+}
+
+
+
+// Optimized Strcmp
+//
+// This function assumes that we can read the last size_t-sized word at
+// the end of the string, even if as many as three of the word bytes are
+// beyond the end of the string.  This is typically a valid assumption
+// because valid memory is always aligned to big power-of-2 sizes.
+//
+// There could be faster strcmp implementations with some additional
+// tricks, asm, SSE, etc. But this version works well while being simple.
+// To do: Implement a vector-specific version for at least x64-based platforms.
+
+#if EASTDC_STATIC_ANALYSIS_ENABLED
+	#define EASTDC_ENABLE_OPTIMIZED_STRCMP 0 // Disabled because the optimized strcmp reads words and the string may have some uninitialized chars at the 
+#else                                        // end past the trailing 0 char. Valgrind reports this as an error, but it's not actually an error in practice.
+	#define EASTDC_ENABLE_OPTIMIZED_STRCMP 1
+#endif
+
+#if EASTDC_ENABLE_OPTIMIZED_STRCMP
+	#if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_OSX)
+		// Some platforms have an optimized vector implementation of strcmp which is fast and which provides
+		// identical return value behavior to our Strcmp (which is to return the byte difference and not just
+		// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
+		EASTDC_API int Strcmp(const char8_t* pString1, const char8_t* pString2)
+		{
+			return strcmp(pString1, pString2);
+		}
+	#else
+		// To do: Implement an x86/x64 vectorized version of Strcmp, which can work on 16 byte chunks and thus be faster than below.
+
+		EASTDC_API int Strcmp(const char8_t* pString1, const char8_t* pString2)
+		{
+			if(IsAligned<const char8_t, sizeof(word_type)>(pString1) && // If pString1 and pString2 are word-aligned... compare in word-sized chunks.
+			   IsAligned<const char8_t, sizeof(word_type)>(pString2))
+			{
+				const word_type* pWord1 = (word_type*)pString1;
+				const word_type* pWord2 = (word_type*)pString2;
+
+				while(*pWord1 == *pWord2)
+				{
+					if(ZeroPresent8(*pWord1++))
+						return 0;
+					++pWord2;
+				}
+
+				// At this point, the strings differ somewhere in the bytes pointed to by pWord1/pWord2.
+				pString1 = reinterpret_cast<const char8_t*>(pWord1); // Fall through and do byte comparisons for the rest of the string.
+				pString2 = reinterpret_cast<const char8_t*>(pWord2);
+			}
+
+			while(*pString1 && (*pString1 == *pString2))
+			{
+				++pString1;
+				++pString2;
+			}
+
+			return ((uint8_t)*pString1 - (uint8_t)*pString2);
+		}
+	#endif
+#else
+	EASTDC_API int Strcmp(const char8_t* pString1, const char8_t* pString2)
+	{
+		char8_t c1, c2;
+
+		while((c1 = *pString1++) == (c2 = *pString2++))
+		{
+			if(c1 == 0)
+				return 0;
+		}
+
+		return ((uint8_t)c1 - (uint8_t)c2);
+	}
+#endif
+
+
+#if EASTDC_ENABLE_OPTIMIZED_STRCMP
+	// To do: Implement an x86/x64 vectorized version of Strcmp, which can work on 16 byte chunks and thus be faster than below.
+
+	EASTDC_API int Strcmp(const char16_t* pString1, const char16_t* pString2)
+	{
+		if(IsAligned<const char16_t, sizeof(word_type)>(pString1) && // If pString1 and pString2 are word-aligned... compare in word-sized chunks.
+		   IsAligned<const char16_t, sizeof(word_type)>(pString2))
+		{
+			const word_type* pWord1 = (word_type*)pString1;
+			const word_type* pWord2 = (word_type*)pString2;
+
+			while(*pWord1 == *pWord2)
+			{
+				if(ZeroPresent16(*pWord1++))
+					return 0;
+				++pWord2;
+			}
+
+			// At this point, the strings differ somewhere in the bytes pointed to by pWord1/pWord2.
+			pString1 = reinterpret_cast<const char16_t*>(pWord1); // Fall through and do byte comparisons for the rest of the string.
+			pString2 = reinterpret_cast<const char16_t*>(pWord2);
+		}
+
+		while(*pString1 && (*pString1 == *pString2))
+		{
+			++pString1;
+			++pString2;
+		}
+
+		return ((uint16_t)*pString1 - (uint16_t)*pString2);
+	}
+#else
+	EASTDC_API int Strcmp(const char16_t* pString1, const char16_t* pString2)
+	{
+		char16_t c1, c2;
+
+		while((c1 = *pString1++) == (c2 = *pString2++))
+		{
+			if(c1 == 0) // If we've reached the end of the string with no difference...
+				return 0;
+		}
+
+		EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
+		return ((uint16_t)c1 - (uint16_t)c2);
+	}
+#endif
+
+
+EASTDC_API int Strcmp(const char32_t* pString1, const char32_t* pString2)
+{
+	char32_t c1, c2;
+
+	while((c1 = *pString1++) == (c2 = *pString2++))
+	{
+		if(c1 == 0) // If we've reached the end of the string with no difference...
+			return 0;
+	}
+
+	// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
+	return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
+}
+
+
+
+#if EASTDC_ENABLE_OPTIMIZED_STRCMP
+	// Some platforms have an optimized vector implementation of strncmp which is fast and which provides
+	// identical return value behavior to our Strncmp (which is to return the byte difference and not just
+	// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
+	EASTDC_API int Strncmp(const char8_t* pString1, const char8_t* pString2, size_t n)
+	{
+		return strncmp(pString1, pString2, n);
+	}
+
+	// To do: Implement a general portable version of a more optimized Strncmp.
+
+#else
+	EASTDC_API int Strncmp(const char8_t* pString1, const char8_t* pString2, size_t n)
+	{
+		char8_t c1, c2;
+
+		++n;
+		while(--n)
+		{
+			if((c1 = *pString1++) != (c2 = *pString2++))
+				return ((uint8_t)c1 - (uint8_t)c2);
+			else if(c1 == 0)
+				break;
+		}
+		
+		return 0;
+	}
+#endif
+
+EASTDC_API int Strncmp(const char16_t* pString1, const char16_t* pString2, size_t n)
+{
+	char16_t c1, c2;
+
+	// Code below which uses (c1 - c2) assumes this.
+	EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
+
+	++n;
+	while(--n)
+	{
+		if((c1 = *pString1++) != (c2 = *pString2++))
+			return ((uint16_t)c1 - (uint16_t)c2);
+		else if(c1 == 0)
+			break;
+	}
+		
+	return 0;
+}
+
+EASTDC_API int Strncmp(const char32_t* pString1, const char32_t* pString2, size_t n)
+{
+	char32_t c1, c2;
+
+	++n;
+	while(--n)
+	{
+		if((c1 = *pString1++) != (c2 = *pString2++))
+		{
+			// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
+			return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
+		}
+		else if(c1 == 0)
+			break;
+	}
+
+	return 0;
+}
+
+
+
+#if EASTDC_ENABLE_OPTIMIZED_STRCMP && (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_OSX))
+	// Some platforms have an optimized vector implementation of stricmp/strcasecmp which is fast and which provides
+	// identical return value behavior to our Stricmp (which is to return the byte difference and not just
+	// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
+	EASTDC_API int Stricmp(const char8_t* pString1, const char8_t* pString2)
+	{
+		return strcasecmp(pString1, pString2);
+	}
+
+	// To do: Implement a general portable version of a more optimized Stricmp.
+
+#else
+	EASTDC_API int Stricmp(const char8_t* pString1, const char8_t* pString2)
+	{
+		char8_t c1, c2;
+
+		while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
+		{
+			if(c1 == 0)
+				return 0;
+		}
+
+		return ((uint8_t)c1 - (uint8_t)c2);
+	}
+#endif
+
+
+EASTDC_API int Stricmp(const char16_t* pString1, const char16_t* pString2)
+{
+	char16_t c1, c2;
+
+	while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
+	{
+		if(c1 == 0)
+			return 0;
+	}
+
+	// Code below which uses (c1 - c2) assumes this.
+	EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
+	return ((uint16_t)c1 - (uint16_t)c2);
+}
+
+EASTDC_API int Stricmp(const char32_t* pString1, const char32_t* pString2)
+{
+	char32_t c1, c2;
+
+	while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
+	{
+		if(c1 == 0)
+			return 0;
+	}
+
+	// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
+	return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
+}
+
+
+
+EASTDC_API int Strnicmp(const char8_t* pString1, const char8_t* pString2, size_t n)
+{
+	char8_t c1, c2;
+
+	++n;
+	while(--n)
+	{
+		if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
+			return ((uint8_t)c1 - (uint8_t)c2);
+		else if(c1 == 0)
+			break;
+	}
+		
+	return 0;
+}
+
+EASTDC_API int Strnicmp(const char16_t* pString1, const char16_t* pString2, size_t n)
+{
+	char16_t c1, c2;
+
+	// Code below which uses (c1 - c2) assumes this.
+	EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
+
+	++n;
+	while(--n)
+	{
+		if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
+			return ((uint16_t)c1 - (uint16_t)c2);
+		else if(c1 == 0)
+			break;
+	}
+		
+	return 0;
+}
+
+EASTDC_API int Strnicmp(const char32_t* pString1, const char32_t* pString2, size_t n)
+{
+	char32_t c1, c2;
+
+	++n;
+	while(--n)
+	{
+		if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
+		{
+			// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
+			return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
+		}
+		else if(c1 == 0)
+			break;
+	}
+		
+	return 0;
+}
+
+
+
+
+// *** this function is deprecated. ***
+EASTDC_API int StrcmpAlnum(const char8_t* pString1, const char8_t* pString2)
+{
+	char8_t c1, c2;
+	const char8_t* pStart1      = pString1;
+	const char8_t* pStart2      = pString2;
+	const char8_t* pDigitStart1 = pString1;
+
+	while(((c1 = *pString1++) == (c2 = *pString2++)) && c1)
+	{
+		if(!Isdigit(c1))
+			pDigitStart1 = pString1;
+	}
+
+	const int c1d = Isdigit(c1);
+	const int c2d = Isdigit(c2);
+
+	if(c1d && c2d)
+		return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
+
+	if(c1d != c2d)  // If one char is decimal and the other is not..
+		return c1d ? 1 : -1;
+
+	return ((uint8_t)c1 - (uint8_t)c2);
+}
+
+
+// *** this function is deprecated. ***
+EASTDC_API int StrcmpAlnum(const char16_t* pString1, const char16_t* pString2)
+{
+	char16_t c1, c2;
+	const char16_t* pStart1      = pString1;
+	const char16_t* pStart2      = pString2;
+	const char16_t* pDigitStart1 = pString1;
+
+	while(((c1 = *pString1++) == (c2 = *pString2++)) && c1)
+	{
+		if(!Isdigit(c1))
+			pDigitStart1 = pString1;
+	}
+
+	const int c1d = Isdigit(c1);
+	const int c2d = Isdigit(c2);
+
+	if(c1d && c2d)
+		return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
+
+	if(c1d != c2d)  // If one char is decimal and the other is not..
+		return c1d ? 1 : -1;
+
+	return ((uint16_t)c1 - (uint16_t)c2);
+}
+
+
+// *** this function is deprecated. ***
+EASTDC_API int StricmpAlnum(const char8_t* pString1, const char8_t* pString2)
+{
+	char8_t c1, c2;
+	const char8_t* pStart1      = pString1;
+	const char8_t* pStart2      = pString2;
+	const char8_t* pDigitStart1 = pString1;
+
+	while(((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++))) && c1)
+	{
+		if(!Isdigit(c1))
+			pDigitStart1 = pString1;
+	}
+
+	const int c1d = Isdigit(c1);
+	const int c2d = Isdigit(c2);
+
+	if(c1d && c2d)
+		return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
+
+	if(c1d != c2d)  // If one char is decimal and the other is not..
+		return c1d ? 1 : -1;
+
+	return ((uint8_t)c1 - (uint8_t)c2);
+}
+
+
+// *** this function is deprecated. ***
+EASTDC_API int StricmpAlnum(const char16_t* pString1, const char16_t* pString2)
+{
+	char16_t c1, c2;
+	const char16_t* pStart1      = pString1;
+	const char16_t* pStart2      = pString2;
+	const char16_t* pDigitStart1 = pString1;
+
+	while(((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++))) && c1)
+	{
+		if(!Isdigit(c1))
+			pDigitStart1 = pString1;
+	}
+
+	const int c1d = Isdigit(c1);
+	const int c2d = Isdigit(c2);
+
+	if(c1d && c2d)
+		return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
+
+	if(c1d != c2d)  // If one char is decimal and the other is not..
+		return c1d ? 1 : -1;
+
+	return ((uint16_t)c1 - (uint16_t)c2);
+}
+
+
+
+EASTDC_API int StrcmpNumeric(const char8_t* pString1, const char8_t* pString2, 
+							 size_t length1, size_t length2, 
+							 char8_t decimal, char8_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+EASTDC_API int StrcmpNumeric(const char16_t* pString1, const char16_t* pString2, 
+							 size_t length1, size_t length2, 
+							 char16_t decimal, char16_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+EASTDC_API int StrcmpNumeric(const char32_t* pString1, const char32_t* pString2, 
+							 size_t length1, size_t length2, 
+							 char32_t decimal, char32_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+
+
+
+EASTDC_API int StricmpNumeric(const char8_t* pString1, const char8_t* pString2, 
+							  size_t length1, size_t length2, 
+							  char8_t decimal, char8_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+EASTDC_API int StricmpNumeric(const char16_t* pString1, const char16_t* pString2, 
+							  size_t length1, size_t length2, 
+							  char16_t decimal, char16_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+EASTDC_API int StricmpNumeric(const char32_t* pString1, const char32_t* pString2, 
+							  size_t length1, size_t length2, 
+							  char32_t decimal, char32_t thousandsSeparator)
+{
+	// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
+	EA_UNUSED(pString1);
+	EA_UNUSED(pString2);
+	EA_UNUSED(length1);
+	EA_UNUSED(length2);
+	EA_UNUSED(decimal);
+	EA_UNUSED(thousandsSeparator);
+
+	return 0;
+}
+
+
+
+
+
+EASTDC_API int Strcoll(const char8_t* pString1, const char8_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strcmp(pString1, pString2);
+}
+
+EASTDC_API int Strcoll(const char16_t* pString1, const char16_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strcmp(pString1, pString2);
+}
+
+EASTDC_API int Strcoll(const char32_t* pString1, const char32_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strcmp(pString1, pString2);
+}
+
+
+
+
+EASTDC_API int Strncoll(const char8_t* pString1, const char8_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strncmp(pString1, pString2, n);
+}
+
+EASTDC_API int Strncoll(const char16_t* pString1, const char16_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strncmp(pString1, pString2, n);
+}
+
+EASTDC_API int Strncoll(const char32_t* pString1, const char32_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strncmp(pString1, pString2, n);
+}
+
+
+
+
+EASTDC_API int Stricoll(const char8_t* pString1, const char8_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Stricmp(pString1, pString2);
+}
+
+EASTDC_API int Stricoll(const char16_t* pString1, const char16_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Stricmp(pString1, pString2);
+}
+
+EASTDC_API int Stricoll(const char32_t* pString1, const char32_t* pString2)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Stricmp(pString1, pString2);
+}
+
+
+
+
+EASTDC_API int Strnicoll(const char8_t* pString1, const char8_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strnicmp(pString1, pString2, n);
+}
+
+EASTDC_API int Strnicoll(const char16_t* pString1, const char16_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strnicmp(pString1, pString2, n);
+}
+
+EASTDC_API int Strnicoll(const char32_t* pString1, const char32_t* pString2, size_t n)
+{
+	// The user needs to use a localization package to get proper localized collation.
+	return Strnicmp(pString1, pString2, n);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EcvtBuf / FcvtBuf
+//
+#if EASTDC_NATIVE_FCVT    
+	EASTDC_API char8_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char8_t* buffer)
+	{
+		#ifdef __GNUC__
+			const char8_t* const pResult =  ecvt(dValue, nDigitCount, decimalPos, sign);
+		#else
+			const char8_t* const pResult = _ecvt(dValue, nDigitCount, decimalPos, sign);
+		#endif
+
+		strcpy(buffer, pResult);
+
+		#if EASTDC_NATIVE_FCVT_SHORT
+			// For ecvt, nDigitCount is the resulting length of the buffer of digits, regardless of the decimal point location.
+			if(nDigitCount > 15) // The '> 15' part is a quick check to avoid the rest of the code for most cases.
+			{
+				int len = (int)strlen(buffer);
+
+				while(len < nDigitCount)
+					buffer[len++] = '0';
+				buffer[len] = 0;
+			}
+		#endif
+
+		return buffer;
+	}
+
+	EASTDC_API char8_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char8_t* buffer)
+	{
+		#ifdef __GNUC__
+			const char8_t* const pResult =  fcvt(dValue, nDigitCountAfterDecimal, decimalPos, sign);
+		#else
+			char8_t pResult[_CVTBUFSIZE+1];
+			_fcvt_s(pResult, sizeof(pResult), dValue, nDigitCountAfterDecimal, decimalPos, sign);
+		#endif
+
+		strcpy(buffer, pResult);
+
+		#if EASTDC_NATIVE_FCVT_SHORT
+			// For fcvt, nDigitCount is the resulting length of the buffer of digits after the decimal point location.
+			nDigitCountAfterDecimal += *decimalPos;
+
+			if(nDigitCountAfterDecimal > 15) // The '> 15' part is a quick check to avoid the rest of the code for most cases.
+			{
+				int len = (int)strlen(buffer);
+
+				while(len < nDigitCountAfterDecimal)
+					buffer[len++] = '0';
+				buffer[len] = 0;
+			}
+		#endif
+
+		return buffer;
+	}
+
+#else
+
+	#if defined(EA_COMPILER_MSVC)
+		#include <float.h>
+		#define isnan(x)  _isnan(x)
+	  //#define isinf(x) !_finite(x)
+	#endif
+
+	#if !defined(isnan)
+
+		inline bool isnan(double fValue)
+		{
+			const union {
+				double  f;
+				int64_t i;
+			} converter = { fValue };
+
+			// An IEEE real value is a NaN if all exponent bits are one and
+			// the mantissa is not zero.
+			return (converter.i & ~kFloat64SignMask) > kFloat64ExponentMask;
+		}
+	#endif
+
+	union DoubleShape
+	{
+		double   mValue;
+		uint32_t mUint64;
+
+		#if defined(EA_SYSTEM_LITTLE_ENDIAN)
+			struct numberStruct
+			{
+				unsigned int fraction1 : 32;
+				unsigned int fraction0 : 20;
+				unsigned int exponent  : 11;
+				unsigned int sign      :  1;
+			} mNumber;
+		#else
+			struct numberStruct
+			{
+				unsigned int sign      :  1;
+				unsigned int exponent  : 11;
+				unsigned int fraction0 : 20;
+				unsigned int fraction1 : 32;
+			} mNumber;
+		#endif
+	};
+
+	union FloatShape
+	{
+		float    mValue;
+		uint32_t mUint32;
+
+		#if defined(EA_SYSTEM_LITTLE_ENDIAN)
+			struct numberStruct
+			{
+				unsigned int fraction : 23;
+				unsigned int exponent :  8;
+				unsigned int sign     :  1;
+			} mNumber;
+		#else
+			struct numberStruct
+			{
+				unsigned int sign     :  1;
+				unsigned int exponent :  8;
+				unsigned int fraction : 23;
+			} mNumber;
+		#endif
+	};
+
+
+	EASTDC_API char8_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char8_t* buffer)
+	{
+		int      nDigitCountAfterDecimal;
+		double   fract;
+		double   integer;
+		double   tmp;
+		int      neg = 0;
+		int      expcnt = 0;
+		char8_t* buf = buffer;
+		char8_t* t = buf;
+		char8_t* p = buf + kEcvtBufMaxSize - 1;
+		char8_t* pbuf = p;
+
+		// We follow the same preconditions as Microsoft does with its _ecvt function.
+		EA_ASSERT((nDigitCount >= 0) && (decimalPos != NULL) && (sign != NULL) && (buffer != NULL));
+
+		// assume decimal to left of digits in string
+		*decimalPos = 0;
+
+		// To consider: Enable the following.
+		//if(nDigitCount > 16) // It turns out that we can't get any more precision than this.
+		//   nDigitCount = 16; // Any digits beyond 16 would be nearly meaningless.
+
+		if(sizeof(double) == sizeof(float)) // If the user has the compiler set to use doubles that are smaller...
+		{
+			FloatShape floatShape;
+			floatShape.mValue = (float)dValue; // This should be a lossless conversion.
+
+			if(floatShape.mNumber.exponent == 0xff) // If not finite...
+			{
+				if(floatShape.mUint32 & 0x007fffff) // If is a NAN...
+				{
+					*t++ = 'N';
+					*t++ = 'A';
+					*t++ = 'N';
+				}
+				else
+				{
+					*t++ = 'I';
+					*t++ = 'N';
+					*t++ = 'F';
+				}
+				*t = 0;
+				return buffer;
+			}
+		}
+		else
+		{
+			DoubleShape doubleShape;
+			doubleShape.mValue = dValue;
+
+			if(doubleShape.mNumber.exponent == 0x7ff) // If not finite...
+			{
+				if(isnan(dValue)) // If is a NAN...
+				{
+					*t++ = 'N';
+					*t++ = 'A';
+					*t++ = 'N';
+				}
+				else
+				{
+					*t++ = 'I';
+					*t++ = 'N';
+					*t++ = 'F';
+				}
+				*t = 0;
+				return buffer;
+			}
+		}
+
+		if(dValue < 0)
+		{
+			neg  = 1;
+			dValue = -dValue;
+		}
+
+		fract = modf(dValue, &integer);
+
+		if(dValue >= 1.0f)
+		{
+			for(; integer; ++expcnt)
+			{
+				tmp = modf(integer / 10.0f, &integer);
+				*p-- = (char8_t)((int)((tmp + 0.01f) * 10.0f) + '0');
+				EA_ASSERT(p >= buffer);
+			}
+		}
+
+		*t++ = 0;   // Extra slot for rounding
+		buf += 1;   // Point return value to beginning of string.
+
+		int tempExp = expcnt;
+		nDigitCountAfterDecimal = nDigitCount - expcnt;
+		
+		if(expcnt)
+		{
+			//if expcnt > nDigitCount, need to round the integer part, and reset expcnt
+			if(expcnt > nDigitCount)
+			{
+				pbuf = p + nDigitCount + 1;
+
+				if(*pbuf >= '5')
+				{
+					do
+					{
+						pbuf--;
+						if(++*pbuf <= '9')
+							break;
+						*pbuf = '0';
+					}
+					while(pbuf >= p+1);
+				}
+
+				expcnt = nDigitCount;
+				fract = 0.0;//no more rounding will be needed down below!
+			}
+
+			for(++p; expcnt--;)
+				*t++ = *p++;
+		}
+
+		if(nDigitCountAfterDecimal >= 0)
+		{
+			// Per spec, don't actually put decimal in string, just let caller know where it should be...
+			*decimalPos = (int)(ptrdiff_t)(t - buf); // Count of chars into string when to place decimal point
+		}
+		else
+			*decimalPos = (int)tempExp;
+
+		bool leading = dValue < 1.0f ? true : false;//for Ecvt, leading zeros need to be omitted and decimalPos needs to be readjusted
+		while((nDigitCountAfterDecimal > 0) && fract)
+		{
+			fract = modf(fract * 10.0f, &tmp);
+			
+			if(leading && (int)tmp == 0)
+			{
+				(*decimalPos)--;
+				continue;
+			}
+			else
+			{
+				leading = false;
+				*t++  = (char8_t)((int)tmp + '0');
+				nDigitCountAfterDecimal -= 1;
+			}
+		} 
+		
+		if(fract)
+		{
+			char8_t* scan = (t - 1);
+
+			// round off the number
+			modf(fract * 10.0f, &tmp);
+
+			if(tmp > 4)
+			{
+				for(; ; --scan)
+				{
+					if(*scan == '.')
+						scan -= 1;
+					if(++*scan <= '9')
+						break;
+					*scan = '0';
+					if(scan == buf)
+					{
+						*--scan = '1';
+						buf -= 1;      // Rounded into holding spot
+						++*decimalPos;  // This line added by Paul Pedriana, May 8 2008, in order to fix a bug where ("%.1f", 0.952) gave "0.1" instead of "1.0". I need to investigate this more to verify the fix.
+						break;
+					}
+				}
+			} 
+			else if(neg)
+			{
+				// fix ("%.3f", -0.0004) giving -0.000
+				for( ; ; scan -= 1)
+				{
+					if(scan <= buf)
+						break;
+					if(*scan == '.')
+						scan -= 1;
+					if(*scan != '0')
+						break;
+					if(scan == buf)
+						neg = 0;
+				}
+			}
+		}
+		
+		if(nDigitCountAfterDecimal<0)//this means the digitcount is smaller than integre part and need to round the integer part
+			nDigitCountAfterDecimal = 0;
+		
+		while(nDigitCountAfterDecimal--)
+			*t++ = '0';
+		*t++ = 0; // Always terminate the string of digits
+
+		if(*buffer == 0) // If the above rounding place wasn't necessary...
+			memmove(buffer, buffer + 1, (size_t)(t - (buffer + 1)));
+
+		*sign = neg ? 1 : 0;
+
+		return buffer;
+
+	}
+
+	EASTDC_API char8_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char8_t* buffer)
+	{
+		double   fract;
+		double   integer;
+		double   tmp;
+		int      neg = 0;
+		int      expcnt = 0;
+		char8_t* buf = buffer;
+		char8_t* t = buf;
+		char8_t* p = buf + kFcvtBufMaxSize - 1;
+
+		// We follow the same preconditions as Microsoft does with its _fcvt function.
+		EA_ASSERT((nDigitCountAfterDecimal >= 0) && (decimalPos != NULL) && (sign != NULL) && (buffer != NULL));
+
+		// assume decimal to left of digits in string
+		*decimalPos = 0;
+
+		if(sizeof(double) == sizeof(float)) // If the user has the compiler set to use doubles that are smaller...
+		{
+			FloatShape floatShape;
+			floatShape.mValue = (float)dValue; // This should be a lossless conversion.
+
+			if(floatShape.mNumber.exponent == 0xff) // If not finite...
+			{
+				if(floatShape.mUint32 & 0x007fffff) // If is a NAN...
+				{
+					*t++ = 'N';
+					*t++ = 'A';
+					*t++ = 'N';
+				}
+				else
+				{
+					*t++ = 'I';
+					*t++ = 'N';
+					*t++ = 'F';
+				}
+				*t = 0;
+				return buffer;
+			}
+		}
+		else
+		{
+			DoubleShape doubleShape;
+			doubleShape.mValue = dValue;
+
+			if(doubleShape.mNumber.exponent == 0x7ff) // If not finite...
+			{
+				if(isnan(dValue)) // If is a NAN...
+				{
+					*t++ = 'N';
+					*t++ = 'A';
+					*t++ = 'N';
+				}
+				else
+				{
+					*t++ = 'I';
+					*t++ = 'N';
+					*t++ = 'F';
+				}
+				*t = 0;
+				return buffer;
+			}
+		}
+
+		if(dValue < 0)
+		{
+			neg  = 1;
+			dValue = -dValue;
+		}
+
+		fract = modf(dValue, &integer);
+
+		if(dValue >= 1.0f)
+		{
+			for(; integer; ++expcnt)
+			{
+				tmp = modf(integer / 10.0f, &integer);
+				*p-- = (char8_t)((int)((tmp + 0.01f) * 10.0f) + '0');
+				EA_ASSERT(p >= buffer);
+			}
+		}
+
+		*t++ = 0;   // Extra slot for rounding
+		buf += 1;   // Point return value to beginning of string.
+		
+		if(expcnt)
+		{
+			for(++p; expcnt--;)
+				*t++ = *p++;
+		}
+
+		// Per spec, don't actually put decimal in string, just let caller know where it should be...
+		*decimalPos = (int)(ptrdiff_t)(t - buf); // Count of chars into string when to place decimal point.
+
+		// We give up trying to calculate fractions beyond 16 digits, which is the maximum possible precision with a double.
+		int count = (nDigitCountAfterDecimal <= 16) ? nDigitCountAfterDecimal : 16;
+		while(count && fract)
+		{
+			fract = modf(fract * 10.0f, &tmp);
+			*t++  = (char8_t)((int)tmp + '0');
+			nDigitCountAfterDecimal--;
+			count--;
+		}
+
+		if(fract)
+		{
+			char8_t* scan = (t - 1);
+
+			// round off the number
+			modf(fract * 10.0f, &tmp);
+
+			if(tmp > 4)
+			{
+				for(; ; --scan)
+				{
+					if(*scan == '.')
+						scan -= 1;
+					if(++*scan <= '9')
+						break;
+					*scan = '0';
+					if(scan == buf)
+					{
+						*--scan = '1';
+						buf -= 1;       // Rounded into holding spot
+						++*decimalPos;  // This line added by Paul Pedriana, May 8 2008, in order to fix a bug where ("%.1f", 0.952) gave "0.1" instead of "1.0". I need to investigate this more to verify the fix.
+						break;
+					}
+				}
+			} 
+			else if(neg)
+			{
+				// fix ("%.3f", -0.0004) giving -0.000
+				for( ; ; --scan)
+				{
+					if(scan <= buf)
+						break;
+					if(*scan == '.')
+						scan -= 1;
+					if(*scan != '0')
+						break;
+					if(scan == buf)
+						neg = 0;
+				}
+			}
+		}
+
+		while(nDigitCountAfterDecimal--)
+			*t++ = '0';
+		*t++ = 0; // Always terminate the string of digits
+
+		if(*buffer == 0) // If the above rounding place wasn't necessary...
+			memmove(buffer, buffer + 1, (size_t)(t - (buffer + 1)));
+
+		*sign = neg ? 1 : 0;
+
+		return buffer;
+	}
+
+	// Matching #undef for each #define above for unity build friendliness.
+	#if defined(EA_COMPILER_MSVC)
+		#undef isnan
+	  //#undef isinf
+	#endif
+
+#endif // Compiler support
+
+
+
+EASTDC_API char16_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char16_t* buffer)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBufferCvt8[kEcvtBufMaxSize];
+	char16_t* pCurrent16 = buffer;
+
+	EcvtBuf(dValue, nDigitCount, decimalPos, sign, pBufferCvt8);
+
+	for(char8_t* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
+		*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent16 = 0;
+
+	return buffer;
+}
+
+EASTDC_API char32_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char32_t* buffer)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBufferCvt8[kEcvtBufMaxSize];
+	char32_t* pCurrent32 = buffer;
+
+	EcvtBuf(dValue, nDigitCount, decimalPos, sign, pBufferCvt8);
+
+	for(char8_t* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
+		*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent32 = 0;
+
+	return buffer;
+}
+
+
+
+
+EASTDC_API char16_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char16_t* buffer)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBufferCvt8[kEcvtBufMaxSize];
+	char16_t* pCurrent16 = buffer;
+
+	FcvtBuf(dValue, nDigitCountAfterDecimal, decimalPos, sign, pBufferCvt8);
+
+	for(char8_t* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
+		*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent16 = 0;
+
+	return buffer;
+}
+
+EASTDC_API char32_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char32_t* buffer)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBufferCvt8[kEcvtBufMaxSize];
+	char32_t* pCurrent32 = buffer;
+
+	FcvtBuf(dValue, nDigitCountAfterDecimal, decimalPos, sign, pBufferCvt8);
+
+	for(char8_t* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
+		*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent32 = 0;
+
+	return buffer;
+}
+
+// end of EcvtBuf / FcvtBuf
+////////////////////////////////////////////////////////////////////////////////////
+
+
+
+// Optimization technique:
+//     https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
+// This results in performance improvements of 2x to 5x depending on the input value. Our general test in 
+// TestString.cpp showed a 3.1x performance gain on VC++/x64.
+//
+static uint32_t digits10(uint64_t v)
+{
+	if(v < 10)
+		return 1;
+	if(v < 100)
+		return 2;
+	if(v < 1000)
+		return 3;
+	if(v < UINT64_C(1000000000000))
+	{
+		if(v < UINT64_C(100000000))
+		{
+			if(v < 1000000)
+			{
+				if (v < 10000)
+					return 4;
+				return (uint32_t)(5 + (v >= 100000));
+			}
+
+			return (uint32_t)(7 + (v >= 10000000));
+		}
+
+		if(v < UINT64_C(10000000000))
+			return (uint32_t)(9 + (v >= UINT64_C(1000000000)));
+
+		return (uint32_t)(11 + (v >= UINT64_C(100000000000)));
+	}
+
+	return 12 + digits10(v / UINT64_C(1000000000000));
+}
+
+char8_t* X64toaCommon10(uint64_t nValue, char8_t* pBuffer)
+{
+	static const char8_t digits[201] =
+		"0001020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849"
+		"5051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
+
+	uint32_t length = digits10(nValue);
+	uint32_t next   = length - 1;
+
+	pBuffer[length] = '\0';
+
+	while(nValue >= 100)
+	{
+		const uint64_t i = (nValue % 100) * 2;
+		nValue /= 100;
+		pBuffer[next] = digits[i + 1];
+		pBuffer[next - 1] = digits[i];
+		next -= 2;
+	}
+
+	if (nValue < 10)
+		pBuffer[next] = (char8_t)('0' + (uint32_t)nValue);
+	else
+	{
+		const uint32_t i  = (uint32_t)nValue * 2;
+		pBuffer[next]     = digits[i + 1];
+		pBuffer[next - 1] = digits[i];
+	}
+
+	return pBuffer;
+}
+
+
+static char8_t* X64toaCommon(uint64_t nValue, char8_t* pBuffer, int nBase, bool bNegative)
+{
+	char8_t* pCurrent = pBuffer;
+
+	if(bNegative)
+		*pCurrent++ = '-';
+
+	if(nBase == 10)
+		X64toaCommon10(nValue, pCurrent);
+	else
+	{
+		char8_t* pFirstDigit = pCurrent;
+
+		do{
+			const unsigned nDigit = (unsigned)(nValue % nBase);
+			nValue /= nBase;
+
+			if(nDigit > 9)
+				*pCurrent++ = (char8_t)(nDigit - 10 + 'a');
+			else
+				*pCurrent++ = (char8_t)(nDigit + '0');
+		} while(nValue > 0);
+
+		// Need to reverse the string.
+		*pCurrent-- = 0;
+
+		do{
+			const char8_t cTemp = *pCurrent;
+			*pCurrent--         = *pFirstDigit;
+			*pFirstDigit++      =  cTemp;
+		}while(pFirstDigit < pCurrent);
+	}
+
+	return pBuffer;
+}
+
+
+static char16_t* X64toaCommon(uint64_t nValue, char16_t* pBuffer, int nBase, bool bNegative)
+{
+	char16_t* pCurrent = pBuffer;
+
+	if(bNegative)
+		*pCurrent++ = '-';
+
+	char16_t* pFirstDigit = pCurrent;
+
+	do{
+		const unsigned nDigit = (unsigned)(nValue % nBase);
+		nValue /= nBase;
+
+		if(nDigit > 9)
+			*pCurrent++ = (char16_t)(nDigit - 10 + 'a');
+		else
+			*pCurrent++ = (char16_t)(nDigit + '0');
+	} while(nValue > 0);
+
+	// Need to reverse the string.
+	*pCurrent-- = 0;
+
+	do{
+		const char16_t cTemp = *pCurrent;
+		*pCurrent--          = *pFirstDigit;
+		*pFirstDigit++       =  cTemp;
+	}while(pFirstDigit < pCurrent);
+
+	return pBuffer;
+}
+
+static char32_t* X64toaCommon(uint64_t nValue, char32_t* pBuffer, int nBase, bool bNegative)
+{
+	char32_t* pCurrent = pBuffer;
+
+	if(bNegative)
+		*pCurrent++ = '-';
+
+	char32_t* pFirstDigit = pCurrent;
+
+	do{
+		const unsigned nDigit = (unsigned)(nValue % nBase);
+		nValue /= nBase;
+
+		if(nDigit > 9)
+			*pCurrent++ = (char32_t)(nDigit - 10 + 'a');
+		else
+			*pCurrent++ = (char32_t)(nDigit + '0');
+	} while(nValue > 0);
+
+	// Need to reverse the string.
+	*pCurrent-- = 0;
+
+	do{
+		const char32_t cTemp = *pCurrent;
+		*pCurrent--          = *pFirstDigit;
+		*pFirstDigit++       =  cTemp;
+	}while(pFirstDigit < pCurrent);
+
+	return pBuffer;
+}
+
+
+
+EASTDC_API char8_t* I32toa(int32_t nValue, char8_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+	{
+		#if defined(__GNUC__)   // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
+		if(nValue != INT32_MIN)
+		#endif
+		nValue = -nValue;
+	}
+
+	return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
+}
+
+EASTDC_API char16_t* I32toa(int32_t nValue, char16_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+	{
+		#if defined(__GNUC__)   // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
+		if(nValue != INT32_MIN)
+		#endif
+		nValue = -nValue;
+	}
+
+	return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
+}
+
+EASTDC_API char32_t* I32toa(int32_t nValue, char32_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+	{
+		#if defined(__GNUC__)   // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
+		if(nValue != INT32_MIN)
+		#endif
+		nValue = -nValue;
+	}
+
+	return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
+}
+
+
+
+EASTDC_API char8_t* U32toa(uint32_t nValue, char8_t* pBuffer, int nBase)
+{
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
+}
+
+EASTDC_API char16_t* U32toa(uint32_t nValue, char16_t* pBuffer, int nBase)
+{
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
+}
+
+EASTDC_API char32_t* U32toa(uint32_t nValue, char32_t* pBuffer, int nBase)
+{
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
+}
+
+
+
+
+EASTDC_API char8_t* I64toa(int64_t nValue, char8_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+		nValue = -(uint64_t)nValue;
+
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
+}
+
+EASTDC_API char16_t* I64toa(int64_t nValue, char16_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+		nValue = -(uint64_t)nValue;
+
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
+}
+
+EASTDC_API char32_t* I64toa(int64_t nValue, char32_t* pBuffer, int nBase)
+{
+	const bool bNegative = (nValue < 0) && (nBase == 10);
+
+	if(bNegative)
+		nValue = -(uint64_t)nValue;
+
+	return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
+}
+
+
+
+
+EASTDC_API char8_t* U64toa(uint64_t nValue, char8_t* pBuffer, int nBase)
+{
+	return X64toaCommon(nValue, pBuffer, nBase, 0);
+}
+
+EASTDC_API char16_t* U64toa(uint64_t nValue, char16_t* pBuffer, int nBase)
+{
+	return X64toaCommon(nValue, pBuffer, nBase, 0);
+}
+
+EASTDC_API char32_t* U64toa(uint64_t nValue, char32_t* pBuffer, int nBase)
+{
+	return X64toaCommon(nValue, pBuffer, nBase, 0);
+}
+
+
+
+
+
+EASTDC_API double StrtodEnglish(const char8_t* pValue, char8_t** ppEnd)
+{
+	// This implementation is an exact copy of StrtodEnglish but 
+	// with char8_t in place of char16_t. For the time being, if 
+	// you do maintenance on either of these functions, you need to 
+	// copy the result to the other version.
+	int            c;
+	double         dTotal(0.0);
+	char8_t        chSign('+');
+	const char8_t* pEnd = pValue;
+
+	while(Isspace(*pValue))
+		++pValue;    //Remove leading spaces.
+
+	pEnd =  pValue;
+	c    = *pValue++;
+	if(c == '-' || c == '+'){
+		chSign = (char8_t)c;
+		pEnd   = pValue;
+		c      = *pValue++;
+	}
+
+	while((c >= '0') && (c <= '9')){
+		dTotal = (10 * dTotal) + (c - '0');
+		pEnd = pValue;
+		c = *pValue++;
+	}
+
+	if(c == '.'){
+		double dMultiplier(1); //Possibly some BCD variable would be more accurate.
+
+		pEnd = pValue;
+		c    = *pValue++;
+		while((c >= '0') && (c <= '9')){
+			dMultiplier *= 0.1;
+			dTotal += (c - '0') * dMultiplier;
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+	}
+
+	if(c == 'e' || c == 'E'){
+		int     nExponentValue(0);
+		double  dExponentTotal;
+		char8_t chExponentSign('+');
+
+		pEnd = pValue;
+		c    = *pValue++; //Move past the exponent.
+
+		if(c == '-' || c == '+'){
+			chExponentSign = (char8_t)c;
+			pEnd = pValue;
+			c    = *pValue++; //Move past the '+' or '-' sign.
+		}
+
+		while((c >= '0') && (c <= '9')){
+			nExponentValue = (10 * nExponentValue) + (c - '0');
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+
+		dExponentTotal = ::pow(10.0, (double)nExponentValue); // The CRT pow function is actually somewhat slow and weak.
+															  // It would be very nice to change this to at least implement
+		if(chExponentSign == '-')                             // the low exponents with a lookup table.
+			dExponentTotal = 1/dExponentTotal;
+		dTotal *= dExponentTotal;
+	}
+
+	if(ppEnd)
+		*ppEnd = (char8_t*)pEnd;
+
+	if(chSign == '-')
+		return -dTotal;
+	return dTotal;
+}
+
+EASTDC_API double StrtodEnglish(const char16_t* pValue, char16_t** ppEnd)
+{
+	// This implementation is an exact copy of StrtodEnglish8 but 
+	// with char16_t in place of char. For the time being, if you
+	// do maintenance on either of these functions, you need to 
+	// copy the result to the other version.
+	char16_t        c;
+	double          dTotal(0.0);
+	char16_t        chSign('+');
+	const char16_t* pEnd = pValue;
+
+	while(Isspace(*pValue))
+		++pValue;    // Remove leading spaces.
+
+	pEnd =  pValue;
+	c    = *pValue++;
+	if(c == '-' || c == '+'){
+		chSign = (char16_t)c;
+		pEnd   = pValue;
+		c      = *pValue++;
+	}
+
+	while((c >= '0') && (c <= '9')){
+		dTotal = (10 * dTotal) + (c - '0');
+		pEnd = pValue;
+		c = *pValue++;
+	}
+
+	if(c == '.'){
+		double dMultiplier(1); // Possibly some BCD variable would be more accurate.
+
+		pEnd = pValue;
+		c    = *pValue++;
+		while((c >= '0') && (c <= '9')){
+			dMultiplier *= 0.1;
+			dTotal += (c - '0') * dMultiplier;
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+	}
+
+	if(c == 'e' || c == 'E'){
+		int      nExponentValue(0);
+		double   dExponentTotal;
+		char16_t chExponentSign('+');
+
+		pEnd = pValue;
+		c    = *pValue++; //Move past the exponent.
+
+		if(c == '-' || c == '+'){
+			chExponentSign = (char16_t)c;
+			pEnd = pValue;
+			c    = *pValue++; // Move past the '+' or '-' sign.
+		}
+
+		while((c >= '0') && (c <= '9')){
+			nExponentValue = (int)((10 * nExponentValue) + (c - '0'));
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+
+		dExponentTotal = ::pow(10.0, (double)nExponentValue);  // The CRT pow function is actually somewhat slow and weak.
+															   // It would be very nice to change this to at least implement
+		if(chExponentSign == '-')                              // the low exponents with a lookup table.
+			dExponentTotal = 1/dExponentTotal;
+		dTotal *= dExponentTotal;
+	}
+
+	if(ppEnd)
+		*ppEnd = (char16_t*)pEnd;
+
+	if(chSign == '-')
+		return -dTotal;
+	return dTotal;
+}
+
+EASTDC_API double StrtodEnglish(const char32_t* pValue, char32_t** ppEnd)
+{
+	// This implementation is an exact copy of StrtodEnglish8 but 
+	// with char32_t in place of char. For the time being, if you
+	// do maintenance on either of these functions, you need to 
+	// copy the result to the other version.
+	char32_t        c;
+	double          dTotal(0.0);
+	char32_t        chSign('+');
+	const char32_t* pEnd = pValue;
+
+	while(Isspace(*pValue))
+		++pValue;    // Remove leading spaces.
+
+	pEnd =  pValue;
+	c    = *pValue++;
+	if(c == '-' || c == '+'){
+		chSign = (char32_t)c;
+		pEnd   = pValue;
+		c      = *pValue++;
+	}
+
+	while((c >= '0') && (c <= '9')){
+		dTotal = (10 * dTotal) + (c - '0');
+		pEnd = pValue;
+		c = *pValue++;
+	}
+
+	if(c == '.'){
+		double dMultiplier(1); // Possibly some BCD variable would be more accurate.
+
+		pEnd = pValue;
+		c    = *pValue++;
+		while((c >= '0') && (c <= '9')){
+			dMultiplier *= 0.1;
+			dTotal += (c - '0') * dMultiplier;
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+	}
+
+	if(c == 'e' || c == 'E'){
+		int      nExponentValue(0);
+		double   dExponentTotal;
+		char32_t chExponentSign('+');
+
+		pEnd = pValue;
+		c     = *pValue++; //Move past the exponent.
+
+		if(c == '-' || c == '+'){
+			chExponentSign = (char32_t)c;
+			pEnd = pValue;
+			c    = *pValue++; // Move past the '+' or '-' sign.
+		}
+
+		while((c >= '0') && (c <= '9')){
+			nExponentValue = (int)((10 * nExponentValue) + (c - '0'));
+			pEnd = pValue;
+			c    = *pValue++;
+		}
+
+		dExponentTotal = ::pow(10.0, (double)nExponentValue);  // The CRT pow function is actually somewhat slow and weak.
+															   // It would be very nice to change this to at least implement
+		if(chExponentSign == '-')                              // the low exponents with a lookup table.
+			dExponentTotal = 1/dExponentTotal;
+		dTotal *= dExponentTotal;
+	}
+
+	if(ppEnd)
+		*ppEnd = (char32_t*)pEnd;
+
+	if(chSign == '-')
+		return -dTotal;
+	return dTotal;
+}
+
+
+
+
+static uint64_t StrtoU64Common(const char8_t* pValue, char8_t** ppEnd, int nBase, bool bUnsigned)
+{
+	uint64_t       nValue(0);                 // Current value
+	const char8_t* p = pValue;                // Current position
+	char8_t        c;                         // Temp value
+	char8_t        chSign('+');               // One of either '+' or '-'
+	bool           bDigitWasRead(false);      // True if any digits were read.
+	bool           bOverflowOccurred(false);  // True if integer overflow occurred.
+
+	// Skip leading whitespace
+	c = *p++;
+	while(Isspace(c))
+		c = *p++;
+
+	// Check for sign.
+	if((c == '-') || (c == '+')){
+		chSign = c;
+		c = *p++;
+	}
+
+	// Do checks on nBase.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (char8_t*)pValue;
+		return 0;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 8, 10, or 16. 
+		if(c != '0')
+			nBase = 10;
+		else if(*p == 'x' || *p == 'X')
+			nBase = 16;
+		else
+			nBase = 8;
+	}
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
+			++p;
+			c = *p++;
+		}
+	}
+
+	// If nValue exceeds this, an integer overflow is reported.
+	#if (EA_PLATFORM_WORD_SIZE >= 8)
+		const uint64_t nMaxValue(UINT64_MAX / nBase);
+		const uint64_t nModValue(UINT64_MAX % nBase);
+	#else
+		// 32 bit platforms are very slow at doing 64 bit div and mod operations.
+		uint64_t nMaxValue;
+		uint64_t nModValue;
+
+		switch(nBase)
+		{
+			case 2:
+				nMaxValue = UINT64_C(9223372036854775807);
+				nModValue = 1;
+				break;
+			case 8:
+				nMaxValue = UINT64_C(2305843009213693951);
+				nModValue = 7;
+				break;
+			case 10:
+				nMaxValue = UINT64_C(1844674407370955161);
+				nModValue = 5;
+				break;
+			case 16:
+				nMaxValue = UINT64_C(1152921504606846975);
+				nModValue = 15;
+				break;
+			default:
+				nMaxValue = (UINT64_MAX / nBase);
+				nModValue = (UINT64_MAX % nBase);
+				break;
+		}
+	#endif
+
+	for(unsigned nCurrentDigit; ; ){
+		if(Isdigit(c))
+			nCurrentDigit = (unsigned)(c - '0');
+		else if(Isalpha(c))
+			nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
+		else
+			break; // The digit is invalid.
+
+		if(nCurrentDigit >= (unsigned)nBase)
+			break; // The digit is invalid.
+
+		bDigitWasRead = true;
+
+		// Check for overflow.
+		if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
+			nValue = (nValue * nBase) + nCurrentDigit;
+		else
+			bOverflowOccurred = true; // Set the flag, but continue processing.
+
+		c = *p++;
+	}
+
+	--p; // Go back to the last character
+
+	if(!bDigitWasRead){
+		if(ppEnd)
+			p = pValue; // We'll assign 'ppEnd' below.
+	}
+	else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
+		// Integer overflow occurred.
+		if(bUnsigned)
+			nValue = UINT64_MAX;
+		else if(chSign == '-')
+			nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
+		else
+			nValue = INT64_MAX;
+
+		errno = ERANGE; // The standard specifies that we set this value.
+	}
+
+	if(ppEnd)
+		*ppEnd = (char8_t*)p;
+
+	if(chSign == '-')
+		nValue = -nValue;
+
+	return nValue;
+}
+
+static uint64_t StrtoU64Common(const char16_t* pValue, char16_t** ppEnd, int nBase, bool bUnsigned)
+{
+	uint64_t        nValue(0);                 // Current value
+	const char16_t* p = pValue;                // Current position
+	char16_t        c;                         // Temp value
+	char16_t        chSign('+');               // One of either '+' or '-'
+	bool            bDigitWasRead(false);      // True if any digits were read.
+	bool            bOverflowOccurred(false);  // True if integer overflow occurred.
+
+	// Skip leading whitespace
+	c = *p++;
+	while(Isspace(c))
+		c = *p++;
+
+	// Check for sign.
+	if((c == '-') || (c == '+')){
+		chSign = c;
+		c = *p++;
+	}
+
+	// Do checks on nBase.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (char16_t*)pValue;
+		return 0;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 8, 10, or 16. 
+		if(c != '0')
+			nBase = 10;
+		else if(*p == 'x' || *p == 'X')
+			nBase = 16;
+		else
+			nBase = 8;
+	}
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
+			++p;
+			c = *p++;
+		}
+	}
+
+	// If nValue exceeds this, an integer overflow is reported.
+	#if (EA_PLATFORM_WORD_SIZE >= 8)
+		const uint64_t nMaxValue(UINT64_MAX / nBase);
+		const uint64_t nModValue(UINT64_MAX % nBase);
+	#else
+		// 32 bit platforms are very slow at doing 64 bit div and mod operations.
+		uint64_t nMaxValue;
+		uint64_t nModValue;
+
+		switch(nBase)
+		{
+			case 2:
+				nMaxValue = UINT64_C(9223372036854775807);
+				nModValue = 1;
+				break;
+			case 8:
+				nMaxValue = UINT64_C(2305843009213693951);
+				nModValue = 7;
+				break;
+			case 10:
+				nMaxValue = UINT64_C(1844674407370955161);
+				nModValue = 5;
+				break;
+			case 16:
+				nMaxValue = UINT64_C(1152921504606846975);
+				nModValue = 15;
+				break;
+			default:
+				nMaxValue = (UINT64_MAX / nBase);
+				nModValue = (UINT64_MAX % nBase);
+				break;
+		}
+	#endif
+
+	for(unsigned nCurrentDigit; ;){
+		if(Isdigit(c))
+			nCurrentDigit = (unsigned)(c - '0');
+		else if(Isalpha(c))
+			nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
+		else
+			break; // The digit is invalid.
+
+		if(nCurrentDigit >= (unsigned)nBase)
+			break; // The digit is invalid.
+
+		bDigitWasRead = true;
+
+		// Check for overflow.
+		if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
+			nValue = (nValue * nBase) + nCurrentDigit;
+		else
+			bOverflowOccurred = true; // Set the flag, but continue processing.
+
+		c = *p++;
+	}
+
+	--p; // Go back to the last character
+
+	if(!bDigitWasRead){
+		if(ppEnd)
+			p = pValue; // We'll assign 'ppEnd' below.
+	}                                                                                  // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
+	else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
+		// Integer overflow occurred.
+		if(bUnsigned)
+			nValue = UINT64_MAX;
+		else if(chSign == '-')
+			nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
+		else
+			nValue = INT64_MAX;
+
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoU64Common: Range underflow or overflow.");}
+		errno = ERANGE; // The standard specifies that we set this value.
+	}
+
+	if(ppEnd)
+		*ppEnd = (char16_t*)p;
+
+	if(chSign == '-')
+		nValue = -nValue;
+
+	return nValue;
+}
+
+static uint64_t StrtoU64Common(const char32_t* pValue, char32_t** ppEnd, int nBase, bool bUnsigned)
+{
+	uint64_t        nValue(0);                 // Current value
+	const char32_t* p = pValue;                // Current position
+	char32_t        c;                         // Temp value
+	char32_t        chSign('+');               // One of either '+' or '-'
+	bool            bDigitWasRead(false);      // True if any digits were read.
+	bool            bOverflowOccurred(false);  // True if integer overflow occurred.
+
+	// Skip leading whitespace
+	c = *p++;
+	while(Isspace(c))
+		c = *p++;
+
+	// Check for sign.
+	if((c == '-') || (c == '+')){
+		chSign = c;
+		c = *p++;
+	}
+
+	// Do checks on nBase.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (char32_t*)pValue;
+		return 0;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 8, 10, or 32. 
+		if(c != '0')
+			nBase = 10;
+		else if(*p == 'x' || *p == 'X')
+			nBase = 32;
+		else
+			nBase = 8;
+	}
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
+			++p;
+			c = *p++;
+		}
+	}
+
+	// If nValue exceeds this, an integer overflow is reported.
+	#if (EA_PLATFORM_WORD_SIZE >= 8)
+		const uint64_t nMaxValue(UINT64_MAX / nBase);
+		const uint64_t nModValue(UINT64_MAX % nBase);
+	#else
+		// 32 bit platforms are very slow at doing 64 bit div and mod operations.
+		uint64_t nMaxValue;
+		uint64_t nModValue;
+
+		switch(nBase)
+		{
+			case 2:
+				nMaxValue = UINT64_C(9223372036854775807);
+				nModValue = 1;
+				break;
+			case 8:
+				nMaxValue = UINT64_C(2305843009213693951);
+				nModValue = 7;
+				break;
+			case 10:
+				nMaxValue = UINT64_C(1844674407370955161);
+				nModValue = 5;
+				break;
+			case 16:
+				nMaxValue = UINT64_C(1152921504606846975);
+				nModValue = 15;
+				break;
+			default:
+				nMaxValue = (UINT64_MAX / nBase);
+				nModValue = (UINT64_MAX % nBase);
+				break;
+		}
+	#endif
+
+	for(unsigned nCurrentDigit; ;){
+		if(Isdigit(c))
+			nCurrentDigit = (unsigned)(c - '0');
+		else if(Isalpha(c))
+			nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
+		else
+			break; // The digit is invalid.
+
+		if(nCurrentDigit >= (unsigned)nBase)
+			break; // The digit is invalid.
+
+		bDigitWasRead = true;
+
+		// Check for overflow.
+		if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
+			nValue = (nValue * nBase) + nCurrentDigit;
+		else
+			bOverflowOccurred = true; // Set the flag, but continue processing.
+
+		c = *p++;
+	}
+
+	--p; // Go back to the last character
+
+	if(!bDigitWasRead){
+		if(ppEnd)
+			p = pValue; // We'll assign 'ppEnd' below.
+	}                                                                                  // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
+	else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
+		// Integer overflow occurred.
+		if(bUnsigned)
+			nValue = UINT64_MAX;
+		else if(chSign == '-')
+			nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
+		else
+			nValue = INT64_MAX;
+
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoU64Common: Range underflow or overflow.");}
+		errno = ERANGE; // The standard specifies that we set this value.
+	}
+
+	if(ppEnd)
+		*ppEnd = (char32_t*)p;
+
+	if(chSign == '-')
+		nValue = -nValue;
+
+	return nValue;
+}
+
+
+
+EASTDC_API int32_t StrtoI32(const char8_t* pValue, char8_t** ppEnd, int nBase)
+{
+	int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
+
+	if(val < INT32_MIN)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
+		errno = ERANGE;
+		return (int32_t)INT32_MIN;
+	}
+
+	if(val > INT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
+		errno = ERANGE;
+		return INT32_MAX;
+	}
+	
+	return (int32_t) val;
+}
+
+EASTDC_API int32_t StrtoI32(const char16_t* pValue, char16_t** ppEnd, int nBase)
+{
+	int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
+
+	if(val < INT32_MIN)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
+		errno = ERANGE;
+		return (int32_t)INT32_MIN;
+	}
+
+	if(val > INT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
+		errno = ERANGE;
+		return INT32_MAX;
+	}
+	
+	return (int32_t) val;
+}
+
+EASTDC_API int32_t StrtoI32(const char32_t* pValue, char32_t** ppEnd, int nBase)
+{
+	int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
+
+	if(val < INT32_MIN)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
+		errno = ERANGE;
+		return (int32_t)INT32_MIN;
+	}
+
+	if(val > INT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
+		errno = ERANGE;
+		return INT32_MAX;
+	}
+	
+	return (int32_t) val;
+}
+
+
+
+
+EASTDC_API uint32_t StrtoU32(const char8_t* pValue, char8_t** ppEnd, int nBase)
+{
+	uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
+
+	if(val > UINT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
+		errno = ERANGE;
+		return UINT32_MAX;
+	}
+
+	return (uint32_t)val;
+}
+
+EASTDC_API uint32_t StrtoU32(const char16_t* pValue, char16_t** ppEnd, int nBase)
+{
+	uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
+
+	if(val > UINT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
+		errno = ERANGE;
+		return UINT32_MAX;
+	}
+
+	return (uint32_t)val;
+}
+
+EASTDC_API uint32_t StrtoU32(const char32_t* pValue, char32_t** ppEnd, int nBase)
+{
+	uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
+
+	if(val > UINT32_MAX)
+	{
+		if(EA::StdC::GetAssertionsEnabled())
+			{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
+		errno = ERANGE;
+		return UINT32_MAX;
+	}
+
+	return (uint32_t)val;
+}
+
+
+
+
+EASTDC_API int64_t StrtoI64(const char8_t* pString, char8_t** ppStringEnd, int nBase)
+{
+	return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
+}
+
+EASTDC_API int64_t StrtoI64(const char16_t* pString, char16_t** ppStringEnd, int nBase)
+{
+	return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
+}
+
+EASTDC_API int64_t StrtoI64(const char32_t* pString, char32_t** ppStringEnd, int nBase)
+{
+	return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
+}
+
+
+
+EASTDC_API uint64_t StrtoU64(const char8_t* pString, char8_t** ppStringEnd, int nBase)
+{
+	return StrtoU64Common(pString, ppStringEnd, nBase, true);
+}
+
+EASTDC_API uint64_t StrtoU64(const char16_t* pString, char16_t** ppStringEnd, int nBase)
+{
+	return StrtoU64Common(pString, ppStringEnd, nBase, true);
+}
+
+EASTDC_API uint64_t StrtoU64(const char32_t* pString, char32_t** ppStringEnd, int nBase)
+{
+	return StrtoU64Common(pString, ppStringEnd, nBase, true);
+}
+
+
+
+EASTDC_API char8_t* FtoaEnglish(double dValue, char8_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+{
+	// Note that this function is a duplicate of FtoaEnglish16 but 
+	// with char instead of char16_t. Modifications to either of 
+	// these functions should be replicated to the other.
+
+	int nDecimalPosition, nSign;
+	int nPositionResult(0);
+	int nPositionTemp(0);
+	int i;
+	int nExponent;
+
+	if(nResultCapacity <= 0)
+		return NULL;
+
+	if(bExponentEnabled){
+		if(dValue == 0.0)
+			nExponent = 0;
+		else
+		{
+			const double dValueAbs = fabs(dValue);
+			const double dValueLog = ::log10(dValueAbs);
+			nExponent = (int)::floor(dValueLog);
+		}
+
+		if((nExponent >= nPrecision) || (nExponent < -4)){    // printf's %g switches to exponential whenever exp >= precision || exp < -4.
+			// Compute how many digits we need for the exponent.
+			int nDigits = 1;
+			int nLimit  = 10;
+
+			while(nLimit <= nExponent){
+				nLimit *= 10;
+				++nDigits;
+			}
+
+			const double dExpPow = ::pow(10.0, (double)-nExponent);
+
+			if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
+				char8_t* p = pResult + Strlen(pResult);
+
+				*p++ = (char8_t)'e';
+				*p++ = ((nExponent < 0) ? (char8_t)'-' : (char8_t)'+');
+				I32toa(abs(nExponent), p, 10);
+				return pResult;
+			}
+			return NULL;
+		}
+	}
+
+	// fcvt is a function that converts a floating point value to its component
+	// string, sign, and decimal position. It doesn't convert it to a fully
+	// finished string because sign and decimal usage usually varies between
+	// locales and this function is trying to be locale-independent. It is up
+	// to the user of this function to present the final data in a form that 
+	// is locale-savvy. Actually, not all compilers implement fcvt.
+	#if EASTDC_NATIVE_FCVT
+		#ifdef __GNUC__   // nPrecision refers to the number of digits after the decimal point.
+		const char8_t* const pResultTemp =  fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
+		#else
+		char8_t pResultTemp[_CVTBUFSIZE+1];
+		_fcvt_s(pResultTemp, sizeof(pResultTemp), dValue, nPrecision, &nDecimalPosition, &nSign);
+		#endif
+	#else
+	   char8_t bufferTemp[kFcvtBufMaxSize];
+	   const char8_t* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
+	#endif
+
+	// If the value is negative, then add a leading '-' sign.
+	if(nSign){
+		if(nPositionResult >= nResultCapacity){
+			pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+			return NULL;
+		}
+		pResult[nPositionResult] = '-';
+		nPositionResult++;
+	}
+
+	// If the value is < 1, then add a leading '0' digit.
+	if(fabs(dValue) < 1.0){ 
+	  #if EASTDC_NATIVE_FCVT && defined(__GNUC__)
+		// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
+		// it yields an output string with a leading "0." So we need to make a special case to 
+		// detect this here.
+		if(dValue != 0.0)
+	  #endif
+		{
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '0';
+		}
+	}
+
+	// Read digits up to the decimal position and write them to the output string.
+	if(nDecimalPosition > 0){      // If the input was something like 1000.0
+		for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = pResultTemp[nPositionTemp++];
+		}
+	}
+
+	if(pResultTemp[nPositionTemp]){
+		// Find the last of the zeroes in the pResultTemp string. We don't want
+		// to add unnecessary trailing zeroes to the returned string and don't
+		// want to return a decimal point in the string if it isn't necessary.
+		int nFirstTrailingZeroPosition(nPositionTemp);
+		int nLastPositionTemp(nPositionTemp);
+
+		while(pResultTemp[nLastPositionTemp]){
+			if(pResultTemp[nLastPositionTemp] != '0')
+				nFirstTrailingZeroPosition = nLastPositionTemp + 1;
+			nLastPositionTemp++;
+		}
+
+		// If there is any reason to write a decimal point, then we write 
+		// it and write the data that comes after it.
+		if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
+			// Add a decimal point.
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '.';
+
+			if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
+				for(i = nDecimalPosition; i < 0; i++){
+					if(nPositionResult >= nResultCapacity){
+						pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+						return NULL;
+					}
+					pResult[nPositionResult++] = '0';
+					--nPrecision; 
+				}
+			}
+
+			// Read digits after the decimal position and write them to the output string.
+			for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
+				if(nPositionResult >= nResultCapacity){
+					//What we do here is possibly erase trailing zeroes that we've written after the decimal.
+					int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
+					pResult[nEndPosition] = 0;
+					while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
+						pResult[nEndPosition] = 0;
+					return NULL;
+				}
+				pResult[nPositionResult++] = pResultTemp[nPositionTemp++];
+			}
+		}
+	}
+
+	// Write the final terminating zero.
+	if(nPositionResult >= nResultCapacity){
+		pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+		return NULL;
+	}
+	pResult[nPositionResult] = 0;
+	return pResult;
+}
+
+EASTDC_API char16_t* FtoaEnglish(double dValue, char16_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+{
+	// Note that this function is a duplicate of FtoaEnglish8 but 
+	// with char16_t instead of char. Modifications to either of 
+	// these functions should be replicated to the other.
+
+	int nDecimalPosition, nSign;
+	int nPositionResult(0);
+	int nPositionTemp(0);
+	int i;
+	int nExponent;
+
+	if(nResultCapacity <= 0)
+		return NULL;
+
+	if(bExponentEnabled){
+		if(dValue == 0.0)
+			nExponent = 0;
+		else
+		{
+			const double dValueAbs = fabs(dValue);
+			const double dValueLog = ::log10(dValueAbs);
+			nExponent = (int)::floor(dValueLog);
+		}
+
+		if((nExponent >= nPrecision) || (nExponent < -4)){    // printf's %g switches to exponential whenever exp >= mnPrecisionUsed || exp < -4.
+			// Compute how many digits we need for the exponent.
+			int nDigits = 1;
+			int nLimit  = 10;
+
+			while(nLimit <= nExponent){
+				nLimit *= 10;
+				++nDigits;
+			}
+
+			const double dExpPow = ::pow(10.0, (double)-nExponent);
+
+			if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
+				char16_t* p = pResult + Strlen(pResult);
+
+				*p++ = (char16_t)'e';
+				*p++ = ((nExponent < 0) ? (char16_t)'-' : (char16_t)'+');
+				I32toa(abs(nExponent), p, 10);
+				return pResult;
+			}
+			return NULL;
+		}
+	}
+
+	// fcvt is a function that converts a floating point value to its component
+	// string, sign, and decimal position. It doesn't convert it to a fully
+	// finished string because sign and decimal usage usually varies between
+	// locales and this function is trying to be locale-independent. It is up
+	// to the user of this function to present the final data in a form that 
+	// is locale-savvy. Actually, not all compilers implement fcvt.
+	#if EASTDC_NATIVE_FCVT
+		#ifdef __GNUC__
+			const char8_t* const pResultTemp =  fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
+		#else
+			const char8_t* const pResultTemp = _fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
+		#endif
+	#else
+	   char8_t bufferTemp[kFcvtBufMaxSize];
+	   const char8_t* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
+	#endif
+
+	// If the value is negative, then add a leading '-' sign.
+	if(nSign){
+		if(nPositionResult >= nResultCapacity){
+			pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+			return NULL;
+		}
+		pResult[nPositionResult] = '-';
+		nPositionResult++;
+	}
+
+	// If the value is < 1, then add a leading '0' digit.
+	if(fabs(dValue) < 1.0){ 
+	  #if EASTDC_NATIVE_FCVT && defined(__GNUC__)
+		// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
+		// it yields an output string with a leading "0." So we need to make a special case to 
+		// detect this here.
+		if(dValue != 0.0)
+	  #endif
+		{
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '0';
+		}
+	}
+
+	// Read digits up to the decimal position and write them to the output string.
+	if(nDecimalPosition > 0){      // If the input was something like 1000.0
+		for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = (char16_t)pResultTemp[nPositionTemp++];
+		}
+	}
+
+	if(pResultTemp[nPositionTemp]){
+		// Find the last of the zeroes in the pResultTemp string. We don't want
+		// to add unnecessary trailing zeroes to the returned string and don't
+		// want to return a decimal point in the string if it isn't necessary.
+		int nFirstTrailingZeroPosition(nPositionTemp);
+		int nLastPositionTemp(nPositionTemp);
+
+		while(pResultTemp[nLastPositionTemp]){
+			if(pResultTemp[nLastPositionTemp] != '0')
+				nFirstTrailingZeroPosition = nLastPositionTemp + 1;
+			nLastPositionTemp++;
+		}
+
+		// If there is any reason to write a decimal point, then we write 
+		// it and write the data that comes after it.
+		if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
+			// Add a decimal point.
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '.';
+
+			if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
+				for(i = nDecimalPosition; i < 0; i++){
+					if(nPositionResult >= nResultCapacity){
+						pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+						return NULL;
+					}
+					pResult[nPositionResult++] = '0';
+					--nPrecision; 
+				}
+			}
+
+			// Read digits after the decimal position and write them to the output string.
+			for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
+				if(nPositionResult >= nResultCapacity){
+					//What we do here is possibly erase trailing zeroes that we've written after the decimal.
+					int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
+					pResult[nEndPosition] = 0;
+					while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
+						pResult[nEndPosition] = 0;
+					return NULL;
+				}
+				pResult[nPositionResult++] = (char16_t)pResultTemp[nPositionTemp++];
+			}
+		}
+	}
+
+	// Write the final terminating zero.
+	if(nPositionResult >= nResultCapacity){
+		pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+		return NULL;
+	}
+	pResult[nPositionResult] = 0;
+	return pResult;
+}
+
+EASTDC_API char32_t* FtoaEnglish(double dValue, char32_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
+{
+	// Note that this function is a duplicate of FtoaEnglish8 but 
+	// with char32_t instead of char. Modifications to either of 
+	// these functions should be replicated to the other.
+
+	int nDecimalPosition, nSign;
+	int nPositionResult(0);
+	int nPositionTemp(0);
+	int i;
+	int nExponent;
+
+	if(nResultCapacity <= 0)
+		return NULL;
+
+	if(bExponentEnabled){
+		if(dValue == 0.0)
+			nExponent = 0;
+		else
+		{
+			const double dValueAbs = fabs(dValue);
+			const double dValueLog = ::log10(dValueAbs);
+			nExponent = (int)::floor(dValueLog);
+		}
+
+		if((nExponent >= nPrecision) || (nExponent < -4)){    // printf's %g switches to exponential whenever exp >= mnPrecisionUsed || exp < -4.
+			// Compute how many digits we need for the exponent.
+			int nDigits = 1;
+			int nLimit  = 10;
+
+			while(nLimit <= nExponent){
+				nLimit *= 10;
+				++nDigits;
+			}
+
+			const double dExpPow = ::pow(10.0, (double)-nExponent);
+
+			if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
+				char32_t* p = pResult + Strlen(pResult);
+
+				*p++ = (char32_t)'e';
+				*p++ = ((nExponent < 0) ? (char32_t)'-' : (char32_t)'+');
+				I32toa(abs(nExponent), p, 10);
+				return pResult;
+			}
+			return NULL;
+		}
+	}
+
+	// fcvt is a function that converts a floating point value to its component
+	// string, sign, and decimal position. It doesn't convert it to a fully
+	// finished string because sign and decimal usage usually varies between
+	// locales and this function is trying to be locale-independent. It is up
+	// to the user of this function to present the final data in a form that 
+	// is locale-savvy. Actually, not all compilers implement fcvt.
+	#if EASTDC_NATIVE_FCVT
+		#ifdef __GNUC__
+			const char8_t* const pResultTemp =  fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
+		#else
+			const char8_t* const pResultTemp = _fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
+		#endif
+	#else
+	   char8_t bufferTemp[kFcvtBufMaxSize];
+	   const char8_t* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
+	#endif
+
+	// If the value is negative, then add a leading '-' sign.
+	if(nSign){
+		if(nPositionResult >= nResultCapacity){
+			pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+			return NULL;
+		}
+		pResult[nPositionResult] = '-';
+		nPositionResult++;
+	}
+
+	// If the value is < 1, then add a leading '0' digit.
+	if(fabs(dValue) < 1.0){ 
+	  #if EASTDC_NATIVE_FCVT && defined(__GNUC__)
+		// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
+		// it yields an output string with a leading "0." So we need to make a special case to 
+		// detect this here.
+		if(dValue != 0.0)
+	  #endif
+		{
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '0';
+		}
+	}
+
+	// Read digits up to the decimal position and write them to the output string.
+	if(nDecimalPosition > 0){      // If the input was something like 1000.0
+		for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = (char32_t)pResultTemp[nPositionTemp++];
+		}
+	}
+
+	if(pResultTemp[nPositionTemp]){
+		// Find the last of the zeroes in the pResultTemp string. We don't want
+		// to add unnecessary trailing zeroes to the returned string and don't
+		// want to return a decimal point in the string if it isn't necessary.
+		int nFirstTrailingZeroPosition(nPositionTemp);
+		int nLastPositionTemp(nPositionTemp);
+
+		while(pResultTemp[nLastPositionTemp]){
+			if(pResultTemp[nLastPositionTemp] != '0')
+				nFirstTrailingZeroPosition = nLastPositionTemp + 1;
+			nLastPositionTemp++;
+		}
+
+		// If there is any reason to write a decimal point, then we write 
+		// it and write the data that comes after it.
+		if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
+			// Add a decimal point.
+			if(nPositionResult >= nResultCapacity){
+				pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+				return NULL;
+			}
+			pResult[nPositionResult++] = '.';
+
+			if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
+				for(i = nDecimalPosition; i < 0; i++){
+					if(nPositionResult >= nResultCapacity){
+						pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+						return NULL;
+					}
+					pResult[nPositionResult++] = '0';
+					--nPrecision; 
+				}
+			}
+
+			// Read digits after the decimal position and write them to the output string.
+			for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
+				if(nPositionResult >= nResultCapacity){
+					//What we do here is possibly erase trailing zeroes that we've written after the decimal.
+					int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
+					pResult[nEndPosition] = 0;
+					while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
+						pResult[nEndPosition] = 0;
+					return NULL;
+				}
+				pResult[nPositionResult++] = (char32_t)pResultTemp[nPositionTemp++];
+			}
+		}
+	}
+
+	// Write the final terminating zero.
+	if(nPositionResult >= nResultCapacity){
+		pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
+		return NULL;
+	}
+	pResult[nPositionResult] = 0;
+	return pResult;
+}
+
+
+
+
+EASTDC_API size_t ReduceFloatString(char8_t* pString, size_t nLength)
+{
+	if(nLength == (size_t)-1)
+		nLength = strlen(pString);
+
+	size_t nNewLength(nLength);
+
+	if(nLength > 0)
+	{
+		// Get the decimal index and exponent index. We won't chop off any zeros 
+		// unless they are after the decimal position and before an exponent position.
+		int nDecimalIndex  = -1;
+		int nExponentIndex = -1;
+		int nCurrentIndex  =  0;
+
+		while(nCurrentIndex < (int)nLength)
+		{
+			if(pString[nCurrentIndex] == '.')
+				nDecimalIndex = nCurrentIndex;
+			if((pString[nCurrentIndex] == 'e') || (pString[nCurrentIndex] == 'E'))
+				nExponentIndex = nCurrentIndex;
+			nCurrentIndex++;
+		}
+
+		// Now we need to go to the end of the string and walk backwards to 
+		// find any contiguous zero digits after a decimal point.
+		if(nDecimalIndex >= 0) // If there is any decimal point...
+		{
+			const int nFirstDigitToCheck(nDecimalIndex + 1);
+			const int nLastDigitToCheck ((nExponentIndex >= 0) ? (nExponentIndex - 1) : (int)(nLength - 1));
+
+			nCurrentIndex = nLastDigitToCheck;
+
+			while(nCurrentIndex >= nFirstDigitToCheck)
+			{
+				// assert((pString[nCurrentIndex] >= '0') && (pString[nCurrentIndex] <= '9'));
+
+				if(pString[nCurrentIndex] == '0')
+				{
+					// Copy the string downward. Note that we copy the trailing 
+					// terminator of the string as well.
+					for(int i = nCurrentIndex; i < (int)nNewLength; i++)
+						pString[i] = pString[i + 1]; // Copy the string downward.
+					nNewLength--;
+				}
+				else
+					break;
+				nCurrentIndex--;
+			}
+		}
+		else
+		{
+			// If the string is all zeroes, convert it to just one zero.
+			size_t i;
+
+			for(i = 0; (i < nLength) && (pString[i] == '0'); i++)
+				{ } // Do nothing.
+
+			if(i == nLength)
+				nLength = 0; // And fall through to the code below.
+		}
+
+		// It is possible that the input string was "000", in which case the above code would 
+		// erase the entire string. Here we simply make a string of "0" and return it.
+		if(nLength == 0)
+		{
+			pString[0] = '0';
+			pString[1] =  0;
+			nNewLength =  1;
+		}
+		else
+		{
+			// We may have a number such as "234.", in which case we remove the trailing decimal.
+			if((nDecimalIndex >= 0) && (nDecimalIndex == ((int)(unsigned)nNewLength - 1)))
+			{
+				pString[nDecimalIndex] = 0;
+				nNewLength--;
+			}
+
+			size_t i;
+
+			// It is also posible that we now have a string like "0." or "000." or just ".".
+			// In this case, we simply set the string to "0".
+			for(i = 0; i < nNewLength; i++)
+			{
+				if((pString[i] != '0') && (pString[i] != '.'))
+					break;
+			}
+
+			if(i == nNewLength) // If the string was all zeros...
+			{
+				pString[0] = '0';
+				pString[1] = 0;
+				nNewLength = 1;
+			}
+
+			if((nNewLength >= 3) && (pString[0] == '0') && (pString[1] == '.'))   // If we have "0.x"
+			{
+				memmove(pString, pString + 1, nNewLength * sizeof(char8_t));
+				nNewLength--;
+			}
+		}
+	}
+
+	return nNewLength;
+}
+
+EASTDC_API size_t ReduceFloatString(char16_t* pString, size_t nLength)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBuffer8[64];
+	char8_t*  pCurrent8;
+	char16_t* pCurrent16;
+	size_t    n = 0;
+
+	if(nLength < 63)
+		nLength = 63;
+
+	for(pCurrent8 = pBuffer8, pCurrent16 = pString; *pCurrent16 && (n < nLength); ++n) // Do a 16 bit to 8 bit strcpy.
+		*pCurrent8++ = (char8_t)(unsigned char)*pCurrent16++;
+	
+	*pCurrent8 = 0;
+
+	n = ReduceFloatString(pBuffer8, n);
+
+	for(pCurrent8 = pBuffer8, pCurrent16 = pString; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
+		*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent16 = 0;
+
+	return n;
+}
+
+EASTDC_API size_t ReduceFloatString(char32_t* pString, size_t nLength)
+{
+	// We implement this by calling the 8 bit version and copying its data.
+	char8_t   pBuffer8[64];
+	char8_t*  pCurrent8;
+	char32_t* pCurrent32;
+	size_t    n = 0;
+
+	if(nLength < 63)
+		nLength = 63;
+
+	for(pCurrent8 = pBuffer8, pCurrent32 = pString; *pCurrent32 && (n < nLength); ++n) // Do a 32 bit to 8 bit strcpy.
+		*pCurrent8++ = (char8_t)(unsigned char)*pCurrent32++;
+	
+	*pCurrent8 = 0;
+
+	n = ReduceFloatString(pBuffer8, n);
+
+	for(pCurrent8 = pBuffer8, pCurrent32 = pString; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
+		*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
+
+	*pCurrent32 = 0;
+
+	return n;
+}
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#undef EASTDC_MIN
+#undef EASTDC_MAX
+
+
+#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)
+	EA_RESTORE_GCC_WARNING()
+#endif
+EA_RESTORE_VC_WARNING()
+
+
+

+ 1840 - 0
source/EATextUtil.cpp

@@ -0,0 +1,1840 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EATextUtil.h>
+#include <EAStdC/EAString.h>
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// EATEXTUTIL_MIN / EATEXTUTIL_MAX
+//
+#define EATEXTUTIL_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define EATEXTUTIL_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+extern uint8_t utf8lengthTable[256];
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8Validate
+//
+// There are multiple definitions of what a valid UTF8 string is. UTF8 allows
+// the ability to encode the same UTF16 character in multiple ways. This in
+// one sense is a legal UTF8 array. However, for some security reasons it is
+// sometimes considered that a UTF8 array is illegal (or at least 'unsafe')
+// if it encodes some character with more bytes than needed. Actually the
+// Unicode standard v3.0 says that these 'insecure' UTF8 sequences are
+// formally illegal to generate but not illegal to interpret.
+// See "http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html"
+//
+// We take the high-security approach here, though it is slower. We could write
+// a simpler function that does a non-security check with the simple table
+// of info here:
+//    0x00-0x7f are single standalone bytes.
+//    0xc2-0xFD are first byte of a multi-byte sequence.
+//    0xc2-0xdf are first byte of a pair.
+//    0xe0-0xef are first byte of a triplet.
+//    0x00-0xf7 are first byte of a quadruplet.
+//    0xf8-0xfb are first byte of a 5-tuplet.
+//    0xfc-0xfd are first byte of a 6-tuplet.
+//    0xfe-0xff are invalid bytes anywhere in a UTF8 string.
+//    0x80-0xbf are the second-sixth byte of a multi-byte sequence, though not all values are valid for all such bytes.
+//
+// See 'http://www.cl.cam.ac.uk/~mgk25/unicode.html' or search for "UTF8 FAQ"
+// on the Internet for more details on UTF8 and Unicode.
+//
+EASTDC_API bool UTF8Validate(const char8_t* pText, size_t nLength)
+{
+	const uint8_t*       pSource8    = (const uint8_t*)pText;
+	const uint8_t* const pSource8End = pSource8 + nLength;
+
+	while(pSource8 < pSource8End)
+	{
+		if(pSource8[0] < 0x80)
+			++pSource8;
+		else if(pSource8[0] < 0xC2)
+			break; // The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+		else if(pSource8[0] < 0xE0) // If 2 input chars result in 1 output char...
+		{
+			if(pSource8End - pSource8 >= 2)
+			{
+				if(!((pSource8[1] ^ 0x80) < 0x40))
+					break; //The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+				pSource8 += 2;
+			}
+			else
+				break; //The input string is not long enough to finish reading the current character.
+		}
+		else if(pSource8[0] < 0xF0) // If 3 input chars result in 1 output char...
+		{
+			if((pSource8End - pSource8) >= 3)
+			{
+				if(!(((pSource8[1] ^ 0x80) < 0x40) &&
+					 ((pSource8[2] ^ 0x80) < 0x40) &&
+					  (pSource8[0] >= 0xE1 || pSource8[1] >= 0xA0)))
+					break; //The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+				pSource8 += 3;
+			}
+			else
+				break; //The input string is not long enough to finish reading the current character.
+		}
+		else if(pSource8[0] < 0xF8) // If 4 input chars result in 1 output char...
+		{
+			if((pSource8End - pSource8) >= 4)
+			{
+				if(!(((pSource8[1] ^ 0x80) < 0x40) &&
+					 ((pSource8[2] ^ 0x80) < 0x40) &&
+					 ((pSource8[3] ^ 0x80) < 0x40) &&
+					  (pSource8[0] >= 0xF1 || pSource8[1] >= 0x90)))
+					break; // The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+				pSource8 += 4;
+			}
+			else
+				break; //The input string is not long enough to finish reading the current character.
+		}
+		else if(pSource8[0] < 0xFC) // If 5 input chars result in 1 output char...
+		{
+			if((pSource8End - pSource8) >= 5)
+			{
+				if(!(((pSource8[1] ^ 0x80) < 0x40) &&
+					 ((pSource8[2] ^ 0x80) < 0x40) &&
+					 ((pSource8[3] ^ 0x80) < 0x40) &&
+					 ((pSource8[4] ^ 0x80) < 0x40) &&
+					  (pSource8[0] >= 0xf9 || pSource8[1] >= 0x88)))
+					break; //The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+				pSource8 += 5;
+			}
+			else
+				break; //The input string is not long enough to finish reading the current character.
+		}
+		else if(pSource8[0] < 0xFE) // If 6 input chars result in 1 output char...
+		{
+			if((pSource8End - pSource8) >= 6)
+			{
+				if(!(((pSource8[1] ^ 0x80) < 0x40) &&
+					 ((pSource8[2] ^ 0x80) < 0x40) &&
+					 ((pSource8[3] ^ 0x80) < 0x40) &&
+					 ((pSource8[4] ^ 0x80) < 0x40) &&
+					 ((pSource8[5] ^ 0x80) < 0x40) &&
+					  (pSource8[0] >= 0xfd || pSource8[1] >= 0x84)))
+					break; //The character is invalid. It is important that we check for this because various security issues potentially arise if we don't.
+				pSource8 += 6;
+			}
+			else
+				break; //The input string is not long enough to finish reading the current character.
+		}
+		else //Else the current input char is invalid.
+			break;
+	}
+
+	return (pSource8 == pSource8End); // The return value is OK if we successfully processed all characters.
+}
+
+
+// Returns the pointer p incremented by n multibyte characters.
+// The string must be a valid UTF8 string or else the behavior is undefined.
+// If the string is not known to be valid, then it should be first validated independently
+// or a validating version of this function should be used instead.
+EASTDC_API char8_t* UTF8Increment(const char8_t* p, size_t n)
+{
+	while(n--)
+	{
+		// To do: Change this code to instead use the utf8lengthTable fropm EAString.cpp
+
+		const int c = (uint8_t)*p;
+
+		if     (c <= 0xc1)    // Actually, any value greater than 0x80 and less than 0xc2 is an invalid leading UTF8 char.
+			p += 1;
+		else if(c <= 0xdf) 
+			p += 2;
+		else if(c <= 0xef)
+			p += 3;
+		else if(c <= 0xf7)
+			p += 4;
+		else if(c <= 0xfb)
+			p += 5;
+		else if(c <= 0xfd)
+			p += 6;
+		else
+			p += 1;           // Error. We return 1 instead of 0 or -1 because the user is probably iterating a string and so this is safer.
+	}
+
+	return (char8_t*)p;
+}
+
+
+// Returns the pointer p decremented by n multibyte characters.
+// The string must be decrementable by the given number of characters or else 
+// the behavior becomes undefined.
+// The string must be a valid UTF8 string or else the behavior is undefined.
+// If the string is not known to be valid, then it should be first validated independently
+// or a validating version of this function should be used instead.
+EASTDC_API char8_t* UTF8Decrement(const char8_t* p, size_t n)
+{
+	while(n)
+	{
+		if(!UTF8IsFollowByte(*--p))
+			--n;
+	}
+
+	return (char8_t*)p;
+}
+
+
+// Returns number of Unicode characters are in the UTF8-encoded string.
+// Return value will be <= Strlen(pString).
+// The string p must be 0-terminated or the behavior of this function is undefined.
+// The string must be a valid UTF8 string or else the behavior is undefined.
+// If the string is not known to be valid, then it should be first validated independently
+// or a validating version of this function should be used instead.
+EASTDC_API size_t UTF8Length(const char8_t* p)
+{
+	size_t n = 0;
+
+	while(*p)
+	{
+		if((*p & 0xc0) != 0x80) // If this is a leading char...
+			++n;
+		++p;
+	}
+
+	return n;
+}
+
+
+// Returns number of characters that would be in a UTF8-encoded string.
+// Return value will be >= Strlen(pString).
+// The string p must be 0-terminated or the behavior of this function is undefined.
+EASTDC_API size_t UTF8Length(const char16_t* p)
+{
+	size_t   n = 0;
+	uint32_t c;
+
+	while((c = *p++) != 0)
+	{
+		if(c < 0x00000080)
+			n += 1;
+		else if(c < 0x00000800)
+			n += 2;
+		else // if(c < 0x00010000) 
+			n += 3;
+	}
+
+	return n;
+}
+
+
+// Returns number of characters that would be in a UTF8-encoded string.
+// Return value will be >= Strlen(pString).
+// The string p must be 0-terminated or the behavior of this function is undefined.
+// Assumes the input values are valid, else the return value will be wrong.
+EASTDC_API size_t UTF8Length(const char32_t* p)
+{
+	size_t   n = 0;
+	uint32_t c;
+
+	while((c = (uint32_t)*p++) != 0)
+	{
+		if(c < 0x00000080)
+			n += 1;
+		else if(c < 0x00000800)
+			n += 2;
+		else if(c < 0x00010000) 
+			n += 3;
+		else if(c < 0x00200000)
+			n += 4;
+		else if(c < 0x04000000)
+			n += 5;
+		else if(c <= 0x7fffffff)
+			n += 6;
+		else
+			n += 1;  // Error
+	}
+
+	return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8CharSize
+//
+// Returns the byte length of the UTF8 multibyte char pointed to by p.
+// The input p must point to the beginning of a UTF8 multibyte sequence, 
+// else the return value is 1.
+//
+// 0x00-0x80 are single bytes.
+// 0x81-0xc1 are invalid values for a leading UTF8 char.
+// 0xc2-0xdf are first byte of a pair.
+// 0xe0-0xef are first byte of a triplet.
+// 0xf0-0xf7 are first byte of a quadruplet.
+// 0xf8-0xfb are first byte of a 5-tuplet.
+// 0xfc-0xfd are first byte of a 6-tuplet.
+// 0xfe-0xff are invalid values for a leading UTF8 char.
+//
+EASTDC_API size_t UTF8CharSize(const char8_t* p)
+{
+	// To do: Change this code to instead use the utf8lengthTable fropm EAString.cpp
+
+	const int c = (uint8_t)*p;
+
+	if     (c <= 0xc1)    // Any value greater than 0x80 and less than 0xc2 is an invalid leading UTF8 char.
+		return 1;
+	else if(c <= 0xdf)
+		return 2;
+	else if(c <= 0xef)
+		return 3;
+	else if(c <= 0xf7)    // This refers to a unicode point > char16_t
+		return 4;
+	else if(c <= 0xfb)    // This refers to a unicode point > char16_t
+		return 5;
+	else if(c <= 0xfd)    // This refers to a unicode point > char16_t
+		return 6;
+
+	return 1; // Error. We return 1 instead of 0 or -1 because the user is probably iterating a string and so this is safer.
+}
+
+
+EASTDC_API size_t UTF8CharSize(char16_t c)
+{
+	if(c < 0x00000080)
+		return 1;
+	else if(c < 0x00000800)
+		return 2;
+	else // if(c < 0x00010000) 
+		return 3;
+
+	// The following would be used if the input was 32 bit instead of 16 bit.
+	//else if(c < 0x00010000)
+	//    return 3;
+	//else if(c < 0x00200000)
+	//    return 4;
+	//else if(c < 0x04000000)
+	//    return 5;
+	//else if(c <= 0x7fffffff)
+	//    return 6;
+	//
+	//return 1; // Error
+}
+
+
+EASTDC_API size_t UTF8CharSize(char32_t c)
+{
+	if((uint32_t)c < 0x00000080)
+		return 1;
+	else if((uint32_t)c < 0x00000800)
+		return 2;
+	else if((uint32_t)c < 0x00010000)
+		return 3;
+	else if((uint32_t)c < 0x00200000)
+		return 4;
+	else if((uint32_t)c < 0x04000000)
+		return 5;
+	else if((uint32_t)c < 0x80000000)
+		return 6;
+	
+	return 1; // Error
+}
+
+
+EASTDC_API char16_t UTF8ReadChar(const char8_t* p, const char8_t** ppEnd)
+{
+	char16_t        c = 0;
+	const char8_t*  pCurrent;
+	uint8_t         cChar0((uint8_t)*p), cChar1, cChar2, cChar3;
+
+	//assert((cChar0 != 0xFE) && (cChar0 != 0xFF));     //  No byte can contain 0xFE or 0xFF
+
+	if(cChar0 < 0x80)
+	{
+		c = cChar0;
+		pCurrent = p + 1;
+	}
+	else
+	{
+		//assert((cChar0 & 0xC0) == 0xC0);              //  The top two bits need to be equal to 1
+
+		if((cChar0 & 0xE0) == 0xC0)
+		{
+			c = (char16_t)((cChar0 & 0x1F) << 6);
+
+			cChar1 = static_cast<uint8_t>(p[1]);
+			//assert((cChar1 & 0xC0) == 0x80);          //  All subsequent code should be b10xxxxxx
+			c |= cChar1 & 0x3F;
+
+			//assert(c >= 0x0080 && c < 0x0800);        //  Check that we have the smallest coding
+
+			pCurrent = p + 2;
+		}
+		else if((cChar0 & 0xF0) == 0xE0)
+		{
+			c = (char16_t)((cChar0 & 0xF) << 12);
+
+			cChar1 = static_cast<uint8_t>(p[1]);
+			//assert((cChar1 & 0xC0) == 0x80);           //  All subsequent code should be b10xxxxxx
+			c |= (cChar1 & 0x3F) << 6;
+
+			cChar2 = static_cast<uint8_t>(p[2]);
+			//assert((cChar2 & 0xC0) == 0x80);           //  All subsequent code should be b10xxxxxx
+			c |= cChar2 & 0x3F;
+
+			//assert(c >= 0x00000800 && c <  0x00010000); //  Check that we have the smallest coding
+
+			pCurrent = p + 3;
+		}
+		else
+		{
+			//assert((cChar0 & 0xf8) == 0xf0);          //  We handle the unicode but not UCS-4
+			c = (char16_t)((cChar0 & 0x7) << 18);
+
+			cChar1 = static_cast<uint8_t>(p[1]);
+			//assert((cChar1 & 0xC0) == 0x80);          //  All subsequent code should be b10xxxxxx
+			c |= (char16_t)((cChar1 & 0x3F) << 12);
+
+			cChar2 = static_cast<uint8_t>(p[2]);
+			//assert((cChar2 & 0xC0) == 0x80);          //  All subsequent code should be b10xxxxxx
+			c |= (cChar2 & 0x3F) << 6;
+
+			cChar3 = static_cast<uint8_t>(p[3]);
+			//assert((cChar3 & 0xC0) == 0x80);          //  All subsequent code should be b10xxxxxx
+			c |= cChar3 & 0x3F;
+
+			//assert(c >= 0x00010000 && c <= 0x0010FFFF); //  Check that we have the smallest coding, Unicode and not ucs-4
+
+			pCurrent = p + 4;
+		}
+	}
+
+	if(ppEnd)
+		*ppEnd = pCurrent;
+
+	return c;
+}
+
+
+// This function assumes that there is enough space at p to write the char.
+// At most three bytes are needed to write a char16_t value and 6 bytes are
+// needed to write a char32_t value.
+EASTDC_API char8_t* UTF8WriteChar(char8_t* p, char16_t c)
+{
+	if(c < 0x80)
+	{
+		*p++ = (char8_t)(uint8_t)c;
+	}
+	else if(c < 0x0800)
+	{
+		*p++ = (char8_t)(uint8_t)((c >> 6) | 0xC0);
+		*p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	}
+	else // if(c < 0x00010000)
+	{
+		*p++ = (char8_t)(uint8_t)((c >> 12) | 0xE0);
+		*p++ = (char8_t)(uint8_t)(((c >> 6) & 0x3F) | 0x80);
+		*p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	}
+	//else
+	//{
+	//    *p++ = (char8_t)(uint8_t)((c >> 18) | 0xF0);
+	//    *p++ = (char8_t)(uint8_t)(((c >> 12) & 0x3F) | 0x80);
+	//    *p++ = (char8_t)(uint8_t)(((c >> 6) & 0x3F) | 0x80);
+	//    *p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	//}
+
+	return p;
+}
+
+// This function assumes that there is enough space at p to write the char.
+// At most three bytes are needed to write a char32_t value and 6 bytes are
+// needed to write a char32_t value.
+EASTDC_API char8_t* UTF8WriteChar(char8_t* p, char32_t c)
+{
+	if((uint32_t)c < 0x80)
+	{
+		*p++ = (char8_t)(uint8_t)c;
+	}
+	else if((uint32_t)c < 0x0800)
+	{
+		*p++ = (char8_t)(uint8_t)((c >> 6) | 0xC0);
+		*p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	}
+	else if((uint32_t)c < 0x00010000)
+	{
+		*p++ = (char8_t)(uint8_t)((c >> 12) | 0xE0);
+		*p++ = (char8_t)(uint8_t)(((c >> 6) & 0x3F) | 0x80);
+		*p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	}
+	else
+	{
+		*p++ = (char8_t)(uint8_t)((c >> 18) | 0xF0);
+		*p++ = (char8_t)(uint8_t)(((c >> 12) & 0x3F) | 0x80);
+		*p++ = (char8_t)(uint8_t)(((c >> 6) & 0x3F) | 0x80);
+		*p++ = (char8_t)(uint8_t)((c & 0x3F) | 0x80);
+	}
+
+	return p;
+}
+
+
+/// UTF8TrimPartialChar
+///
+/// Trim the string to the last valid UTF8 character. This function has no effect on a UTF8 string that has 
+/// entirely valid UTF8 content. It only trims the string if there is an incomplete UTF8 sequence at the
+/// end. The resulting string will always be a valid UTF8 string, whereas the input string may not be.
+/// Returns the strlen of the trimmed string.
+size_t UTF8TrimPartialChar(char8_t* pString, size_t nLength)
+{
+	size_t validPos = 0;
+
+	while(validPos < nLength)
+	{
+		uint8_t ch = (uint8_t)pString[validPos];
+		size_t length = utf8lengthTable[ch];
+		
+		// length = 0 means invalid UTF8 marker
+		if((length == 0) || ((validPos + length) > nLength))
+			break;
+		else
+			validPos += length;
+	}
+
+	pString[validPos] = 0;
+	return validPos;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8ReplaceInvalidChar
+// 
+// This function replaces all invalidate UTF8 characters with the user provided
+// 8-bit replacement. The returned character array is guaranteed null-terminated.
+//
+EASTDC_API char8_t* UTF8ReplaceInvalidChar(const char8_t* pIn, size_t nLength, char8_t* pOut, char8_t replaceWith)
+{
+	size_t validPos = 0;
+
+	while(validPos < nLength)
+	{
+		uint8_t ch = (uint8_t)pIn[validPos];
+		size_t length = utf8lengthTable[ch];
+		
+		// length = 0 means invalid UTF8 marker
+		if((length == 0) || ((validPos + length) > nLength))
+		{
+			pOut[validPos++] = replaceWith;
+		}
+		else
+		{
+			for(auto i = validPos; i < validPos + length; i++)
+				pOut[i] = pIn[i];
+
+			validPos += length;
+		}
+	}
+
+	pOut[validPos] = 0;
+	return pOut + validPos;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// MatchPattern
+//
+// This function is recursively called on substrings. 
+// Used by the WildcardMatch function.
+//
+template <class CharT>
+bool MatchPattern(const CharT* pElement, const CharT* pPattern)
+{
+	if((*pPattern == (CharT)'*') && !pPattern[1])
+		return true;                                       // The pattern is set to match everything, so return true.
+	else if(!*pElement && *pPattern)
+		return false;                                      // The element is empty but the pattern is not, so return false.
+	else if(!*pElement)
+		return true;                                       // The element and pattern are both empty, so we are done. Return true.
+	else
+	{
+		if(*pPattern == (CharT)'*')
+		{
+			if(MatchPattern(pElement, pPattern+1))          // What this section does is try to match source segments to
+				return true;                                // the '*' portion of the pattern. As many parts of the source that
+			else                                            // can be assigned to the '*' portion of the pattern are done. If
+				return MatchPattern(pElement+1, pPattern);  // not possible, we pop out of the whole thing.
+		}
+		else if(*pPattern == (CharT)'?')
+			return MatchPattern(pElement+1, pPattern+1);    // The pattern accepts any character here, so move onto the next character.
+		else
+		{
+			if(*pElement == *pPattern)
+				return MatchPattern(pElement+1, pPattern+1); // The current element and pattern chars match, so move onto next character.
+			else
+				return false;                                // The current element char simply doesn't match the pattern char, so return false.
+		}
+	}
+	// return true;   // This should never get executed, but some compilers might not be smart enough to realize it.
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WildcardMatch
+//
+// We go through extra effort below to avoid doing memory allocation in most cases.
+//
+EASTDC_API bool WildcardMatch(const char8_t* pString, const char8_t* pPattern, bool bCaseSensitive)
+{
+	if(bCaseSensitive)
+		return MatchPattern(pString, pPattern);
+	else
+	{
+		// Do efficient string conversion to lower case...
+		char8_t  pStringLBuffer[384];
+		char8_t* pStringL;
+		char8_t* pStringLAllocated;
+		size_t   nStringLLength = Strlen(pString);
+
+		if(nStringLLength >= (sizeof(pStringLBuffer) / sizeof(pStringLBuffer[0]) - 1))
+		{
+			pStringLAllocated = EASTDC_NEW("EATextUtil/StringAllocated/char[]") char[nStringLLength + 1];
+			pStringL          = pStringLAllocated;
+		}
+		else
+		{
+			pStringLAllocated = NULL;
+			pStringL          = pStringLBuffer;
+		}
+		Strcpy(pStringL, pString);
+		Strlwr(pStringL);
+
+		// Do efficient pattern conversion to lower case...
+		char8_t  pPatternLBuffer[32];
+		char8_t* pPatternL;
+		char8_t* pPatternLAllocated;
+		size_t   nPatternLLength = Strlen(pPattern);
+
+		if(nPatternLLength >= (sizeof(pPatternLBuffer) / sizeof(pPatternLBuffer[0]) - 1))
+		{
+			pPatternLAllocated = EASTDC_NEW("EATextUtil/PatternAllocated/char[]") char[nPatternLLength + 1];
+			pPatternL          = pPatternLAllocated;
+		}
+		else
+		{
+			pPatternLAllocated = NULL;
+			pPatternL          = pPatternLBuffer;
+		}
+		Strcpy(pPatternL, pPattern);
+		Strlwr(pPatternL);
+
+		const bool bResult = MatchPattern(pStringL, pPatternL);
+
+		delete[] pStringLAllocated; // In most cases, this will be NULL and there will be no effect.
+		delete[] pPatternLAllocated;
+
+		return bResult;
+   }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WildcardMatch
+//
+// We go through extra effort below to avoid doing memory allocation in most cases.
+//
+EASTDC_API bool WildcardMatch(const char16_t* pString, const char16_t* pPattern, bool bCaseSensitive)
+{
+	if(bCaseSensitive)
+		return MatchPattern(pString, pPattern);
+	else
+	{
+		// Do efficient string conversion to lower case...
+		char16_t  pStringLBuffer[384];
+		char16_t* pStringL;
+		char16_t* pStringLAllocated;
+		size_t    nStringLLength = Strlen(pString);
+
+		if(nStringLLength >= (sizeof(pStringLBuffer) / sizeof(pStringLBuffer[0]) - 1))
+		{
+			pStringLAllocated = EASTDC_NEW("EATextUtil/StringAllocated/char16[]") char16_t[nStringLLength + 1];
+			pStringL          = pStringLAllocated;
+		}
+		else
+		{
+			pStringLAllocated = NULL;
+			pStringL          = pStringLBuffer;
+		}
+		Strcpy(pStringL, pString);
+		Strlwr(pStringL);
+
+		// Do efficient pattern conversion to lower case...
+		char16_t  pPatternLBuffer[32];
+		char16_t* pPatternL;
+		char16_t* pPatternLAllocated;
+		size_t    nPatternLLength = Strlen(pPattern);
+
+		if(nPatternLLength >= (sizeof(pPatternLBuffer) / sizeof(pPatternLBuffer[0]) - 1))
+		{
+			pPatternLAllocated = EASTDC_NEW("EATextUtil/PatternAllocated/char16[]") char16_t[nPatternLLength + 1];
+			pPatternL          = pPatternLAllocated;
+		}
+		else
+		{
+			pPatternLAllocated = NULL;
+			pPatternL          = pPatternLBuffer;
+		}
+		Strcpy(pPatternL, pPattern);
+		Strlwr(pPatternL);
+
+		const bool bResult = MatchPattern(pStringL, pPatternL);
+
+		delete[] pStringLAllocated; // In most cases, this will be NULL and there will be no effect.
+		delete[] pPatternLAllocated;
+
+		return bResult;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WildcardMatch
+//
+// We go through extra effort below to avoid doing memory allocation in most cases.
+//
+EASTDC_API bool WildcardMatch(const char32_t* pString, const char32_t* pPattern, bool bCaseSensitive)
+{
+	if(bCaseSensitive)
+		return MatchPattern(pString, pPattern);
+	else
+	{
+		// Do efficient string conversion to lower case...
+		char32_t  pStringLBuffer[384];
+		char32_t* pStringL;
+		char32_t* pStringLAllocated;
+		size_t    nStringLLength = Strlen(pString);
+
+		if(nStringLLength >= (sizeof(pStringLBuffer) / sizeof(pStringLBuffer[0]) - 1))
+		{
+			pStringLAllocated = EASTDC_NEW("EATextUtil/StringAllocated/char32[]") char32_t[nStringLLength + 1];
+			pStringL          = pStringLAllocated;
+		}
+		else
+		{
+			pStringLAllocated = NULL;
+			pStringL          = pStringLBuffer;
+		}
+		Strcpy(pStringL, pString);
+		Strlwr(pStringL);
+
+		// Do efficient pattern conversion to lower case...
+		char32_t  pPatternLBuffer[32];
+		char32_t* pPatternL;
+		char32_t* pPatternLAllocated;
+		size_t    nPatternLLength = Strlen(pPattern);
+
+		if(nPatternLLength >= (sizeof(pPatternLBuffer) / sizeof(pPatternLBuffer[0]) - 1))
+		{
+			pPatternLAllocated = EASTDC_NEW("EATextUtil/PatternAllocated/char32[]") char32_t[nPatternLLength + 1];
+			pPatternL          = pPatternLAllocated;
+		}
+		else
+		{
+			pPatternLAllocated = NULL;
+			pPatternL          = pPatternLBuffer;
+		}
+		Strcpy(pPatternL, pPattern);
+		Strlwr(pPatternL);
+
+		const bool bResult = MatchPattern(pStringL, pPatternL);
+
+		delete[] pStringLAllocated; // In most cases, this will be NULL and there will be no effect.
+		delete[] pPatternLAllocated;
+
+		return bResult;
+	}
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// GetTextLine
+//
+EASTDC_API const char8_t* GetTextLine(const char8_t* pText, const char8_t* pTextEnd, const char8_t** ppNewText)
+{
+	if(pText < pTextEnd)
+	{
+		while((pText < pTextEnd) && (*pText != '\r') && (*pText != '\n'))
+			++pText;
+
+		if(ppNewText)
+		{
+			*ppNewText = pText;
+
+			if(*ppNewText < pTextEnd)
+			{
+				if((++*ppNewText < pTextEnd) && (**ppNewText ^ *pText) == ('\r' ^ '\n'))
+					++*ppNewText;
+			}
+		}
+	}
+	else if(ppNewText)
+		*ppNewText = pTextEnd;
+
+	return pText;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// GetTextLine
+//
+EASTDC_API const char16_t* GetTextLine(const char16_t* pText, const char16_t* pTextEnd, const char16_t** ppNewText)
+{
+	if(pText < pTextEnd)
+	{
+		while((pText < pTextEnd) && (*pText != '\r') && (*pText != '\n'))
+			++pText;
+
+		if(ppNewText)
+		{
+			*ppNewText = pText;
+
+			if(*ppNewText < pTextEnd)
+			{
+				if((++*ppNewText < pTextEnd) && (**ppNewText ^ *pText) == ('\r' ^ '\n'))
+					++*ppNewText;
+			}
+		}
+	}
+	else if(ppNewText)
+		*ppNewText = pTextEnd;
+
+	return pText;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// GetTextLine
+//
+EASTDC_API const char32_t* GetTextLine(const char32_t* pText, const char32_t* pTextEnd, const char32_t** ppNewText)
+{
+	if(pText < pTextEnd)
+	{
+		while((pText < pTextEnd) && (*pText != '\r') && (*pText != '\n'))
+			++pText;
+
+		if(ppNewText)
+		{
+			*ppNewText = pText;
+
+			if(*ppNewText < pTextEnd)
+			{
+				if((++*ppNewText < pTextEnd) && (**ppNewText ^ *pText) == ('\r' ^ '\n'))
+					++*ppNewText;
+			}
+		}
+	}
+	else if(ppNewText)
+		*ppNewText = pTextEnd;
+
+	return pText;
+}
+
+
+
+
+EASTDC_API bool ParseDelimitedText(const char8_t* pText, const char8_t* pTextEnd, char8_t cDelimiter, 
+								   const char8_t*& pToken, const char8_t*& pTokenEnd, const char8_t** ppNewText)
+{
+	int  nQuoteLevel     = 0;
+	bool bDelimiterFound = false;
+
+	// We remove leading spaces.
+	for(pToken = pText; pToken < pTextEnd; ++pToken)
+	{
+		if((*pToken != ' ') && (*pToken != '\t'))
+			break;
+	}
+
+	for(pTokenEnd = pToken; pTokenEnd < pTextEnd; ++pTokenEnd)
+	{
+		const bool bLastCharacter = ((pTokenEnd + 1) == pTextEnd);
+
+		if(cDelimiter == ' ')  // The space char delimiter is a special case that means delimit by whitespace.
+			bDelimiterFound = ((*pTokenEnd == ' ') || (*pTokenEnd == '\t'));
+		else
+			bDelimiterFound = (*pTokenEnd == cDelimiter);
+
+		if(bDelimiterFound || bLastCharacter) // If we found a delimiter or if we are on the last character...
+		{
+			if(!bDelimiterFound)
+				++pTokenEnd;
+
+			const bool bInQuotes = ((nQuoteLevel & 1) != 0);
+
+			if(!bInQuotes || bLastCharacter) // If not within a quoted section...
+			{
+				if(ppNewText)
+					*ppNewText = pTokenEnd;
+
+				if((cDelimiter != ' ') && (pTokenEnd != pTextEnd))
+				{
+					// Eliminate spaces before the trailing delimiter.
+					while((pTokenEnd != pToken) && ((pTokenEnd[-1] == ' ') || (pTokenEnd[-1] == '\t')))
+						pTokenEnd--;
+				}
+
+				if((pToken != pTextEnd) && (*pToken == '"') && (pTokenEnd[-1] == '"'))
+				{
+					pToken++;
+					pTokenEnd--;
+				}
+
+				return true;
+			}
+		}
+		else if(*pTokenEnd == '"')
+			nQuoteLevel++;
+	}
+
+	if(ppNewText)
+		*ppNewText = pTokenEnd;
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// ParseDelimitedText
+//
+// This function takes a line text that has fields separated by delimiters
+// and parses the line into the component fields. It is common to read
+// command lines like this or to parse ini file settings like this.
+//
+EASTDC_API bool ParseDelimitedText(const char16_t* pText, const char16_t* pTextEnd, char16_t cDelimiter, 
+								   const char16_t*& pToken, const char16_t*& pTokenEnd, const char16_t** ppNewText)
+{
+	int  nQuoteLevel     = 0;
+	bool bDelimiterFound = false;
+
+	// We remove leading spaces.
+	for(pToken = pText; pToken < pTextEnd; ++pToken)
+	{
+		if((*pToken != ' ') && (*pToken != '\t'))
+			break;
+	}
+
+	for(pTokenEnd = pToken; pTokenEnd < pTextEnd; ++pTokenEnd)
+	{
+		const bool bLastCharacter = ((pTokenEnd + 1) == pTextEnd);
+
+		if(cDelimiter == ' ')  // The space char delimiter is a special case that means delimit by whitespace.
+			bDelimiterFound = ((*pTokenEnd == ' ') || (*pTokenEnd == '\t'));
+		else
+			bDelimiterFound = (*pTokenEnd == cDelimiter);
+
+		if(bDelimiterFound || bLastCharacter) // If we found a delimiter or if we are on the last character...
+		{
+			if(!bDelimiterFound)
+				++pTokenEnd;
+
+			const bool bInQuotes = ((nQuoteLevel & 1) != 0);
+
+			if(!bInQuotes || bLastCharacter) // If not within a quoted section...
+			{
+				if(ppNewText)
+					*ppNewText = pTokenEnd;
+
+				if((cDelimiter != ' ') && (pTokenEnd != pTextEnd))
+				{
+					// Eliminate spaces before the trailing delimiter.
+					while((pTokenEnd != pToken) && ((pTokenEnd[-1] == ' ') || (pTokenEnd[-1] == '\t')))
+						pTokenEnd--;
+				}
+
+				if((pToken != pTextEnd) && (*pToken == '"') && (pTokenEnd[-1] == '"'))
+				{
+					pToken++;
+					pTokenEnd--;
+				}
+
+				return true;
+			}
+		}
+		else if(*pTokenEnd == '"')
+			nQuoteLevel++;
+	}
+
+	if(ppNewText)
+		*ppNewText = pTokenEnd;
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// ParseDelimitedText
+//
+// This function takes a line text that has fields separated by delimiters
+// and parses the line into the component fields. It is common to read
+// command lines like this or to parse ini file settings like this.
+//
+EASTDC_API bool ParseDelimitedText(const char32_t* pText, const char32_t* pTextEnd, char32_t cDelimiter, 
+								   const char32_t*& pToken, const char32_t*& pTokenEnd, const char32_t** ppNewText)
+{
+	int  nQuoteLevel     = 0;
+	bool bDelimiterFound = false;
+
+	// We remove leading spaces.
+	for(pToken = pText; pToken < pTextEnd; ++pToken)
+	{
+		if((*pToken != ' ') && (*pToken != '\t'))
+			break;
+	}
+
+	for(pTokenEnd = pToken; pTokenEnd < pTextEnd; ++pTokenEnd)
+	{
+		const bool bLastCharacter = ((pTokenEnd + 1) == pTextEnd);
+
+		if(cDelimiter == ' ')  // The space char delimiter is a special case that means delimit by whitespace.
+			bDelimiterFound = ((*pTokenEnd == ' ') || (*pTokenEnd == '\t'));
+		else
+			bDelimiterFound = (*pTokenEnd == cDelimiter);
+
+		if(bDelimiterFound || bLastCharacter) // If we found a delimiter or if we are on the last character...
+		{
+			if(!bDelimiterFound)
+				++pTokenEnd;
+
+			const bool bInQuotes = ((nQuoteLevel & 1) != 0);
+
+			if(!bInQuotes || bLastCharacter) // If not within a quoted section...
+			{
+				if(ppNewText)
+					*ppNewText = pTokenEnd;
+
+				if((cDelimiter != ' ') && (pTokenEnd != pTextEnd))
+				{
+					// Eliminate spaces before the trailing delimiter.
+					while((pTokenEnd != pToken) && ((pTokenEnd[-1] == ' ') || (pTokenEnd[-1] == '\t')))
+						pTokenEnd--;
+				}
+
+				if((pToken != pTextEnd) && (*pToken == '"') && (pTokenEnd[-1] == '"'))
+				{
+					pToken++;
+					pTokenEnd--;
+				}
+
+				return true;
+			}
+		}
+		else if(*pTokenEnd == '"')
+			nQuoteLevel++;
+	}
+
+	if(ppNewText)
+		*ppNewText = pTokenEnd;
+
+	return false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ConvertBinaryDataToASCIIArray
+//
+// Since every binary byte converts to exactly 2 ascii bytes, the ASCII
+// array  must have space for at least twice the amount of bytes
+// as 'nBinaryDataLength' + 1.
+//
+EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData_, size_t nBinaryDataLength, char8_t* pASCIIArray)
+{
+	const uint8_t* pBinaryData = (uint8_t*)pBinaryData_;
+	const uint8_t* pEnd = pBinaryData + nBinaryDataLength;
+
+	while(pBinaryData < pEnd)
+	{
+		*pASCIIArray = (char8_t)('0' + ((*pBinaryData & 0xf0) >> 4));  // Convert the high byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		*pASCIIArray = (char8_t)('0' + (*pBinaryData & 0x0f));         // Convert the low byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		pBinaryData++;
+	}
+
+	*pASCIIArray = '\0';
+}
+
+EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData_, size_t nBinaryDataLength, char16_t* pASCIIArray)
+{
+	const uint8_t* pBinaryData = (uint8_t*)pBinaryData_;
+	const uint8_t* pEnd = pBinaryData + nBinaryDataLength;
+
+	while(pBinaryData < pEnd)
+	{
+		*pASCIIArray = (char16_t)('0' + ((*pBinaryData & 0xf0) >> 4));  // Convert the high byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		*pASCIIArray = (char16_t)('0' + (*pBinaryData & 0x0f));         // Convert the low byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		pBinaryData++;
+	}
+
+	*pASCIIArray = '\0';
+}
+
+EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData_, size_t nBinaryDataLength, char32_t* pASCIIArray)
+{
+	const uint8_t* pBinaryData = (uint8_t*)pBinaryData_;
+	const uint8_t* pEnd = pBinaryData + nBinaryDataLength;
+
+	while(pBinaryData < pEnd)
+	{
+		*pASCIIArray = (char32_t)('0' + ((*pBinaryData & 0xf0) >> 4));  // Convert the high byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		*pASCIIArray = (char32_t)('0' + (*pBinaryData & 0x0f));         // Convert the low byte to a number between 1 and 15.
+		if(*pASCIIArray > '9')
+			*pASCIIArray += 7; // Convert the ':' to 'A', for example.
+		pASCIIArray++;
+		pBinaryData++;
+	}
+
+	*pASCIIArray = '\0';
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// ConvertASCIIArrayToBinaryData (8 bit version)
+//
+// We have a boolean return value because it is possible that the ascii data is
+// corrupt. We check for this corruption and return false if so, while converting
+// all corrupt bytes to valid ones.
+//
+EASTDC_API bool ConvertASCIIArrayToBinaryData(const char8_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData)
+{
+	uint8_t*        pBinaryData8 = (uint8_t*)pBinaryData;
+	const char8_t*  pEnd = pASCIIArray + nASCIIArrayLength;
+	char8_t         cTemp;
+	bool            bReturnValue(true);
+
+	while(pASCIIArray < pEnd)
+	{
+		*pBinaryData8 = 0;
+
+		for(int j = 4; j >= 0; j -= 4)
+		{
+			cTemp = *pASCIIArray;
+
+			if(cTemp < '0') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp > 'F') // Do some bounds checking.
+			{
+				if(cTemp >= 'a' && cTemp <= 'f')
+					cTemp -= 39; // Convert 'a' to ':'.
+				else
+				{
+					cTemp = '0';
+					bReturnValue = false;
+				}
+			}
+			else if(cTemp > '9' && cTemp < 'A') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp >= 'A')
+				cTemp -= 7;
+
+			*pBinaryData8 = (uint8_t)(*pBinaryData8 + ((cTemp - '0') << j));
+			pASCIIArray++;
+		}
+
+		pBinaryData8++;
+	}
+
+	return bReturnValue;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ConvertASCIIArrayToBinaryData (16 bit version)
+//
+// We have a boolean return value because it is possible that the ascii data is
+// corrupt. We check for this corruption and return false if so, while converting
+// all corrupt bytes to valid ones.
+//
+EASTDC_API bool ConvertASCIIArrayToBinaryData(const char16_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData)
+{
+	uint8_t*        pBinaryData8 = (uint8_t*)pBinaryData;
+	const char16_t* pEnd = pASCIIArray + nASCIIArrayLength;
+	char16_t        cTemp;
+	bool            bReturnValue(true);
+
+	while(pASCIIArray < pEnd)
+	{
+		*pBinaryData8 = 0;
+
+		for(int j = 4; j >= 0; j -= 4)
+		{
+			cTemp = *pASCIIArray;
+
+			if(cTemp < '0') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp > 'F') // Do some bounds checking.
+			{
+				if(cTemp >= 'a' && cTemp <= 'f')
+					cTemp -= 39; // Convert 'a' to ':'.
+				else
+				{
+					cTemp = '0';
+					bReturnValue = false;
+				}
+			}
+			else if(cTemp > '9' && cTemp < 'A') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp >= 'A')
+				cTemp -= 7;
+
+			*pBinaryData8 = (uint8_t)(*pBinaryData8 + ((cTemp - '0') << j));
+			pASCIIArray++;
+		}
+
+		pBinaryData8++;
+	}
+
+	return bReturnValue;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ConvertASCIIArrayToBinaryData (32 bit version)
+//
+// We have a boolean return value because it is possible that the ascii data is
+// corrupt. We check for this corruption and return false if so, while converting
+// all corrupt bytes to valid ones.
+//
+EASTDC_API bool ConvertASCIIArrayToBinaryData(const char32_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData)
+{
+	uint8_t*        pBinaryData8 = (uint8_t*)pBinaryData;
+	const char32_t* pEnd = pASCIIArray + nASCIIArrayLength;
+	char32_t        cTemp;
+	bool            bReturnValue(true);
+
+	while(pASCIIArray < pEnd)
+	{
+		*pBinaryData8 = 0;
+
+		for(int j = 4; j >= 0; j -= 4)
+		{
+			cTemp = *pASCIIArray;
+
+			if(cTemp < '0') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp > 'F') // Do some bounds checking.
+			{
+				if(cTemp >= 'a' && cTemp <= 'f')
+					cTemp -= 39; // Convert 'a' to ':'.
+				else
+				{
+					cTemp = '0';
+					bReturnValue = false;
+				}
+			}
+			else if(cTemp > '9' && cTemp < 'A') // Do some bounds checking.
+			{
+				cTemp = '0';
+				bReturnValue = false;
+			}
+			else if(cTemp >= 'A')
+				cTemp -= 7;
+
+			*pBinaryData8 = (uint8_t)(*pBinaryData8 + ((cTemp - '0') << j));
+			pASCIIArray++;
+		}
+
+		pBinaryData8++;
+	}
+
+	return bReturnValue;
+}
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenDelimited (8 bit version)
+//  
+EASTDC_API bool SplitTokenDelimited(const char8_t* pSource, size_t nSourceLength, char8_t cDelimiter, 
+									char8_t* pToken, size_t nTokenLength, const char8_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+	if(pToken && nTokenLength)
+		*pToken = 0;
+
+	if(pSource && nSourceLength && *pSource)
+	{
+		// look for the delimiter
+		for(size_t i = 0; i < nSourceLength && *pSource; i++)
+		{
+			const char8_t cTemp(*pSource);
+
+			// update new source pointer if present
+			if(ppNewSource)
+				(*ppNewSource)++;
+
+			if(cTemp == cDelimiter) // If there is a delimiter match...
+				break; // We are done.
+			else
+			{
+				// keep moving characters into the token until we find the delimiter or reached the end of the token string
+				if(pToken && ((i + 1) < nTokenLength)) // we need an extra character for terminating null
+				{
+					*pToken = cTemp;    // add the character
+					 pToken++;          // increment the token pointer
+					*pToken = 0;        // insert terminating null character
+				}
+
+				pSource++; // increment source pointer
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenDelimited (16 bit version)
+//
+// Implemented by Blazej Stompel and Paul Pedriana
+//
+EASTDC_API bool SplitTokenDelimited(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, 
+									char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+	if(pToken && nTokenLength)
+		*pToken = 0;
+
+	if(pSource && nSourceLength && *pSource)
+	{
+		// look for the delimiter
+		for(size_t i = 0; i < nSourceLength && *pSource; i++)
+		{
+			const char16_t cTemp(*pSource);
+
+			// update new source pointer if present
+			if(ppNewSource)
+				(*ppNewSource)++;
+
+			if(cTemp == cDelimiter) // If there is a delimiter match...
+				break; // We are done.
+			else
+			{
+				// keep moving characters into the token until we find the delimiter or reached the end of the token string
+				if(pToken && ((i + 1) < nTokenLength)) // we need an extra character for terminating null
+				{
+					*pToken = cTemp;    // add the character
+					 pToken++;          // increment the token pointer
+					*pToken = 0;        // insert terminating null character
+				}
+
+				pSource++; // increment source pointer
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenDelimited (32 bit version)
+//
+// Implemented by Blazej Stompel and Paul Pedriana
+//
+EASTDC_API bool SplitTokenDelimited(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, 
+									char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+	if(pToken && nTokenLength)
+		*pToken = 0;
+
+	if(pSource && nSourceLength && *pSource)
+	{
+		// look for the delimiter
+		for(size_t i = 0; i < nSourceLength && *pSource; i++)
+		{
+			const char32_t cTemp(*pSource);
+
+			// update new source pointer if present
+			if(ppNewSource)
+				(*ppNewSource)++;
+
+			if(cTemp == cDelimiter) // If there is a delimiter match...
+				break; // We are done.
+			else
+			{
+				// keep moving characters into the token until we find the delimiter or reached the end of the token string
+				if(pToken && ((i + 1) < nTokenLength)) // we need an extra character for terminating null
+				{
+					*pToken = cTemp;    // add the character
+					 pToken++;          // increment the token pointer
+					*pToken = 0;        // insert terminating null character
+				}
+
+				pSource++; // increment source pointer
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenSeparated (8 bit version)
+//
+EASTDC_API bool SplitTokenSeparated(const char8_t* pSource, size_t nSourceLength, char8_t c, 
+									char8_t* pToken, size_t nTokenLength, const char8_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+
+	if(pToken && nTokenLength)
+		*pToken = '\0';
+
+	if(pSource)
+	{
+		// keep track of how many characters we have written to the token buffer
+		size_t nTokenIndex = 0;
+
+		// keep track whether we found the token and if we are done reading it
+		bool bFoundToken = false;
+		bool bReadToken  = false;
+
+		// look for the separators
+		for(size_t i = 0; i < nSourceLength; i++)
+		{
+			// get the character
+			const char8_t cTemp(*pSource);
+
+			// quit if we found the terminating null character
+			if(cTemp != '\0')
+			{
+				// is the character not a separator ?
+				if(cTemp != c)
+				{
+					// we have a token
+					bFoundToken = true;
+
+					// were we done reading the token ?
+					if(bReadToken)
+						return true;
+					else
+					{
+						// add the character to the token
+						if(pToken && (nTokenIndex + 1) < nTokenLength) // we need an extra character for terminating null
+						{
+							// add the character
+							*pToken = cTemp;
+
+							// increment the token pointer
+							pToken++;
+
+							// and index
+							nTokenIndex++;
+
+							// insert terminating null character
+							*pToken = '\0';
+						}
+					}
+				}
+				else
+				{
+					// the character is a separator - if we found our token then we are done reading it
+					if(bFoundToken)
+						bReadToken = true;
+				}
+
+				// update new source pointer if present
+				if(ppNewSource)
+					(*ppNewSource)++;
+
+				// increment source pointer
+				pSource++;
+			}
+			else
+			{
+				// we have reached the end of the string
+				break;
+			}
+		}
+
+		return bFoundToken;
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenSeparated (16 bit version)
+//
+// Implemented by Blazej Stompel
+//
+// Unit test can be found in Foundation\Test\UnitTests
+//
+EASTDC_API bool SplitTokenSeparated(const char16_t* pSource, size_t nSourceLength, char16_t c, 
+									char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+	if(pToken && nTokenLength)
+		*pToken = '\0';
+
+	if(pSource)
+	{
+		// keep track of how many characters we have written to the token buffer
+		size_t nTokenIndex = 0;
+
+		// keep track whether we found the token and if we are done reading it
+		bool bFoundToken = false;
+		bool bReadToken  = false;
+
+		// look for the separators
+		for(size_t i = 0; i < nSourceLength; i++)
+		{
+			// get the character
+			const char16_t cTemp(*pSource);
+
+			// quit if we found the terminating null character
+			if(cTemp != '\0')
+			{
+				// is the character not a separator ?
+				if(cTemp != c)
+				{
+					// we have a token
+					bFoundToken = true;
+
+					// were we done reading the token ?
+					if(bReadToken)
+						return true;
+					else
+					{
+						// add the character to the token
+						if(pToken && (nTokenIndex + 1) < nTokenLength) // we need an extra character for terminating null
+						{
+							// add the character
+							*pToken = cTemp;
+
+							// increment the token pointer
+							pToken++;
+
+							// and index
+							nTokenIndex++;
+
+							// insert terminating null character
+							*pToken = '\0';
+						}
+					}
+				}
+				else
+				{
+					// the character is a separator - if we found our token then we are done reading it
+					if(bFoundToken)
+						bReadToken = true;
+				}
+
+				// update new source pointer if present
+				if(ppNewSource)
+					(*ppNewSource)++;
+
+				// increment source pointer
+				pSource++;
+			}
+			else
+			{
+				// we have reached the end of the string
+				break;
+			}
+		}
+
+		return bFoundToken;
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SplitTokenSeparated (32 bit version)
+//
+// Implemented by Blazej Stompel
+//
+// Unit test can be found in Foundation\Test\UnitTests
+//
+EASTDC_API bool SplitTokenSeparated(const char32_t* pSource, size_t nSourceLength, char32_t c, 
+									char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource)
+{
+	// terminate the token (so it appears empty if we don't find anything)
+	if(pToken && nTokenLength)
+		*pToken = '\0';
+
+	if(pSource)
+	{
+		// keep track of how many characters we have written to the token buffer
+		size_t nTokenIndex = 0;
+
+		// keep track whether we found the token and if we are done reading it
+		bool bFoundToken = false;
+		bool bReadToken  = false;
+
+		// look for the separators
+		for(size_t i = 0; i < nSourceLength; i++)
+		{
+			// get the character
+			const char32_t cTemp(*pSource);
+
+			// quit if we found the terminating null character
+			if(cTemp != '\0')
+			{
+				// is the character not a separator ?
+				if(cTemp != c)
+				{
+					// we have a token
+					bFoundToken = true;
+
+					// were we done reading the token ?
+					if(bReadToken)
+						return true;
+					else
+					{
+						// add the character to the token
+						if(pToken && (nTokenIndex + 1) < nTokenLength) // we need an extra character for terminating null
+						{
+							// add the character
+							*pToken = cTemp;
+
+							// increment the token pointer
+							pToken++;
+
+							// and index
+							nTokenIndex++;
+
+							// insert terminating null character
+							*pToken = '\0';
+						}
+					}
+				}
+				else
+				{
+					// the character is a separator - if we found our token then we are done reading it
+					if(bFoundToken)
+						bReadToken = true;
+				}
+
+				// update new source pointer if present
+				if(ppNewSource)
+					(*ppNewSource)++;
+
+				// increment source pointer
+				pSource++;
+			}
+			else
+			{
+				// we have reached the end of the string
+				break;
+			}
+		}
+
+		return bFoundToken;
+	}
+
+	return false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Boyer-Moore string search
+//
+// This is the "turbo" implementation defined at http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140.
+// Boyer-Moore is a very fast string search compared to most others, including
+// those in the STL. However, you need to be searching a string of at least 100
+// chars and have a search pattern of at least 3 characters for the speed to show,
+// as Boyer-Moore has a startup precalculation that costs some cycles. 
+// This startup precalculation is proportional to the size of your search pattern
+// and the size of the alphabet in use. Thus, doing Boyer-Moore searches on the 
+// entire Unicode alphabet is going to incur a fairly expensive precalculation cost.
+//
+
+// This is a private function used by BoyerMooreSearch.
+// 
+static void BoyerMooreBadCharacterCalc(const char* pPattern, int nPatternLength, 
+									   int* pAlphabetBuffer, int nAlphabetBufferSize)
+{
+	int i;
+	 
+	for(i = 0; i < nAlphabetBufferSize; ++i)
+		pAlphabetBuffer[i] = nPatternLength;
+
+	for(i = 0; i < (nPatternLength - 1); ++i)
+		pAlphabetBuffer[(int)pPattern[i]] = (nPatternLength - i) - 1;
+}
+
+
+// This is a private function used by BoyerMooreSearch.
+// 
+static void BoyerMooreGoodSuffixCalc(const char* pPattern, int nPatternLength, 
+									 int* pPatternBuffer1, int* pPatternBuffer2)
+{
+	int i;
+	int j = 0;
+	int f = 0;
+	int g = nPatternLength - 1;
+
+	pPatternBuffer2[nPatternLength - 1] = nPatternLength;
+
+	for(i = nPatternLength - 2; i >= 0; --i)
+	{
+		if((i > g) && pPatternBuffer2[((i + nPatternLength) - 1) - f] < (i - g))
+			pPatternBuffer2[i] = pPatternBuffer2[((i + nPatternLength) - 1) - f];
+		else 
+		{
+			if(i < g)
+				g = i;
+
+			f = i;
+
+			while((g >= 0) && (pPattern[g] == pPattern[((g + nPatternLength) - 1) - f]))
+				--g;
+
+			pPatternBuffer2[i] = f - g;
+		}
+	}
+
+	for(i = 0; i < nPatternLength; ++i)
+		pPatternBuffer1[i] = nPatternLength;
+
+	for(i = nPatternLength - 1; i >= -1; --i)
+	{
+		if((i == -1) || (pPatternBuffer2[i] == (i + 1)))
+		{
+			for(; j < (nPatternLength - 1) - i; ++j)
+			{
+				if(pPatternBuffer1[j] == nPatternLength)
+					pPatternBuffer1[j] = (nPatternLength - 1) - i;
+			}
+		}
+	}
+
+	for(i = 0; i <= nPatternLength - 2; ++i)
+		pPatternBuffer1[(nPatternLength - 1) - pPatternBuffer2[i]] = (nPatternLength - 1) - i;
+}
+
+
+
+// Argument specification.
+//
+// patternBuffer1 is a user-supplied buffer and must be at least as long as the search pattern.
+// patternBuffer2 is a user-supplied buffer and must be at least as long as the search pattern.
+// alphabetBuffer is a user-supplied buffer and must be at least as long as the highest character value used in the searched string and search pattern.
+//
+EASTDC_API int BoyerMooreSearch(const char* pPattern, int nPatternLength, const char* pSearchString, int nSearchStringLength, 
+								int* pPatternBuffer1, int* pPatternBuffer2, int* pAlphabetBuffer, int nAlphabetBufferSize)
+{
+	// Do precalculations
+	BoyerMooreGoodSuffixCalc(pPattern, nPatternLength, pPatternBuffer1, pPatternBuffer2);
+	BoyerMooreBadCharacterCalc(pPattern, nPatternLength, pAlphabetBuffer, nAlphabetBufferSize);
+
+	// Do search
+	for(int j = 0, shift = nPatternLength, u = 0; j <= (nSearchStringLength - nPatternLength); j += shift)
+	{
+		int i = nPatternLength - 1;
+
+		while((i >= 0) && (pPattern[i] == pSearchString[i + j]))
+		{
+			--i;
+
+			if((u != 0) && (i == (nPatternLength - 1) - shift))
+				i -= u;
+		}
+
+		if(i < 0)
+		{
+			return j;
+
+			// Only used if we were iterating multiple found items:
+			//shift = pPatternBuffer1[0];
+			//u     = nPatternLength - shift;
+		}
+		else
+		{
+			const int v          = nPatternLength - 1 - i;
+			const int turboShift = u - v;
+			const int bcShift    = pAlphabetBuffer[(int)pSearchString[i + j]] - nPatternLength + 1 + i;
+			shift                = EATEXTUTIL_MAX(turboShift, bcShift);
+			shift                = EATEXTUTIL_MAX(shift, pPatternBuffer1[i]);
+
+			if(shift == pPatternBuffer1[i])
+				u = EATEXTUTIL_MIN(nPatternLength - shift, v);
+			else
+			{
+				if(turboShift < bcShift)
+					shift = EATEXTUTIL_MAX(shift, u + 1);
+				u = 0;
+			}
+		}
+	}
+
+	return nPatternLength;
+}
+
+
+#undef EATEXTUTIL_MIN
+#undef EATEXTUTIL_MAX
+
+
+} // namespace StdC
+} // namespace EA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 3879 - 0
source/Int128_t.cpp

@@ -0,0 +1,3879 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/Int128_t.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <EAAssert/eaassert.h>
+
+
+#if defined(_MSC_VER)
+	#pragma warning(push)
+	#pragma warning(disable: 4723) // potential divide by 0
+	#pragma warning(disable: 4365) // 'argument' : conversion from 'int' to 'uint32_t', signed/unsigned mismatch
+	#pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
+#endif
+
+
+namespace EA
+{
+namespace StdC
+{
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants
+
+// EASTDC_INT128_MIN is equal to: -170141183460469231731687303715884105728;
+const int128_t EASTDC_INT128_MIN(0x00000000, 0x00000000, 0x00000000, 0x80000000);
+
+// EASTDC_INT128_MAX is equal to:  170141183460469231731687303715884105727;
+const int128_t EASTDC_INT128_MAX(0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff);
+
+// EASTDC_UINT128_MIN is equal to:  0;
+const uint128_t EASTDC_UINT128_MIN(0x00000000, 0x00000000, 0x00000000, 0x00000000);
+
+// EASTDC_UINT128_MAX is equal to:  340282366920938463463374607431768211455;
+const uint128_t EASTDC_UINT128_MAX(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// int128_t
+///////////////////////////////////////////////////////////////////////////////
+
+int128_t_base::int128_t_base()
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = 0;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = 0;
+	#endif
+}
+
+int128_t_base::int128_t_base(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = ((uint64_t)nPart3 << 32) + nPart2;
+		mPart0 = ((uint64_t)nPart1 << 32) + nPart0;
+	#else
+		mPart3 = nPart3;
+		mPart2 = nPart2;
+		mPart1 = nPart1;
+		mPart0 = nPart0;
+	#endif
+}
+
+int128_t_base::int128_t_base(uint64_t nPart0, uint64_t nPart1)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = nPart1;
+		mPart0 = nPart0;
+	#else
+		mPart3 = (uint32_t)(nPart1 >> 32);
+		mPart2 = (uint32_t) nPart1;
+		mPart1 = (uint32_t)(nPart0 >> 32);
+		mPart0 = (uint32_t) nPart0;
+	#endif
+}
+
+int128_t_base::int128_t_base(uint8_t value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = value;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = value;
+	#endif
+}
+
+int128_t_base::int128_t_base(uint16_t value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = value;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = value;
+	#endif
+}
+
+int128_t_base::int128_t_base(uint32_t value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = value;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = value;
+	#endif
+}
+
+
+#if defined(INT128_UINT_TYPE)
+
+int128_t_base::int128_t_base(INT128_UINT_TYPE value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = value;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = value;
+	#endif
+}
+
+#endif
+
+
+int128_t_base::int128_t_base(uint64_t value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = value;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = (uint32_t) ((value >> 32) & 0xffffffff);
+		mPart0 = (uint32_t) ((value >>  0) & 0xffffffff);
+	#endif
+}
+
+
+int128_t_base::int128_t_base(const int128_t_base& value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = value.mPart1;
+		mPart0 = value.mPart0;
+	#else
+		mPart3 = value.mPart3;
+		mPart2 = value.mPart2;
+		mPart1 = value.mPart1;
+		mPart0 = value.mPart0;
+	#endif
+}
+
+
+int128_t_base& int128_t_base::operator=(const int128_t_base& value)
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = value.mPart1;
+		mPart0 = value.mPart0;
+	#else
+		mPart3 = value.mPart3;
+		mPart2 = value.mPart2;
+		mPart1 = value.mPart1;
+		mPart0 = value.mPart0;
+	#endif
+	return *this;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorPlus
+//
+// Returns: (value1 + value2) into result.
+// The output 'result' *is* allowed to point to the same memory as one of the inputs.
+// To consider: Fix 'defect' of this function whereby it doesn't implement overflow wraparound.
+//
+void int128_t_base::operatorPlus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result)
+{
+	#if defined(EA_ASM_STYLE_INTEL) && defined(EA_PROCESSOR_X86)
+		__asm
+		{
+			mov ebx, value1
+			mov ecx, value2
+			mov edx, result
+
+			mov eax, [ebx]
+			add eax, [ecx]      ;(nCarry, tmp) = value1.mPart0 + value2.mPart0
+			mov [edx], eax      ;result.mPart0 = value1.mPart0 + value2.mPart0
+
+			mov eax, [ebx+4]
+			adc eax, [ecx+4]    ;(nCarry, tmp) = value1.mPart1 + value2.mPart1
+			mov [edx+4], eax    ;result.mPart1 = value1.mPart1 + value2.mPart1 + nCarry
+
+			mov eax, [ebx+8]
+			adc eax, [ecx+8]    ;(nCarry, tmp) = value1.mPart2 + value2.mPart2
+			mov [edx+8], eax    ;result.mPart2 = value1.mPart2 + value2.mPart2 + nCarry
+
+			mov eax, [ebx+12]
+			adc eax, [ecx+12]   ;(nCarry, tmp) = value1.mPart3 + value2.mPart3
+			mov [edx+12], eax   ;result.mPart3 = value1.mPart3 + value2.mPart3 + nCarry
+		}
+	#elif EA_INT128_USE_INT64
+		uint64_t t      = value1.mPart0 + value2.mPart0;
+		uint64_t nCarry = (t < value1.mPart0) && (t < value2.mPart0);
+		result.mPart0   = t;
+		result.mPart1   = value1.mPart1 + value2.mPart1 + nCarry;
+	#else
+		uint64_t t      = ((uint64_t)value1.mPart0) + ((uint64_t)value2.mPart0);
+		uint32_t nCarry = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart0   = (uint32_t) t;
+
+		t               = ((uint64_t)value1.mPart1) + ((uint64_t)value2.mPart1) + nCarry;
+		nCarry          = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart1   = (uint32_t) t;
+
+		t               = ((uint64_t)value1.mPart2) + ((uint64_t)value2.mPart2) + nCarry;
+		nCarry          = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart2   = (uint32_t) t;
+
+		t               = ((uint64_t)value1.mPart3) + ((uint64_t)value2.mPart3) + nCarry;
+		//nCarry        = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart3   = (uint32_t) t;
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorMinus
+//
+// Returns: (value1 - value2) into result.
+// The output 'result' *is* allowed to point to the same memory as one of the inputs.
+// To consider: Fix 'defect' of this function whereby it doesn't implement overflow wraparound.
+//
+void int128_t_base::operatorMinus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		uint64_t t      = (value1.mPart0 - value2.mPart0);
+		uint64_t nCarry = (value1.mPart0 < value2.mPart0) ? 1 : 0;
+		result.mPart0   = t;
+		result.mPart1   = (value1.mPart1 - value2.mPart1) - nCarry;
+	#else
+		uint64_t t      = ((uint64_t)value1.mPart0) - ((uint64_t)value2.mPart0);
+		uint32_t nCarry = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart0   = (uint32_t) t;
+
+		t               = (((uint64_t)value1.mPart1) - ((uint64_t)value2.mPart1)) - nCarry;
+		nCarry          = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart1   = (uint32_t) t;
+
+		t               = (((uint64_t)value1.mPart2) - ((uint64_t)value2.mPart2)) - nCarry;
+		nCarry          = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart2   = (uint32_t) t;
+
+		t               = (((uint64_t)value1.mPart3) - ((uint64_t)value2.mPart3)) - nCarry;
+		//nCarry        = (uint32_t)((t > 0xffffffff) ? 1 : 0);
+		result.mPart3   = (uint32_t) t;
+	#endif
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorMul
+//
+// 32 bit systems:
+//    The way this works is like decimal multiplication by hand with a pencil and
+//    paper. The difference is that we work with blocks of 32 bits intead of blocks
+//    of ten. Here is a multiplication of 0x00000008000000040000000200000001 x 
+//    the same value done like you do with pencil and paper:
+//     
+//                                          Part 3         2         1         0
+//                                        00000008  00000004  00000002  00000001
+//                                     x  00000008  00000004  00000002  00000001
+//                                   -------------------------------------------
+//                                      | 00000008  00000004  00000002  00000001
+//                             00000010 | 00000008  00000004  00000002 (00000000)
+//                   00000020  00000010 | 00000008  00000004 (00000000)(00000000)
+//      +  00000040  00000020  00000010 | 00000008 (00000000)(00000000)(00000000)
+//     -------------------------------------------------------------------------
+//
+//    That the numbers above have columns each with the same values is a coincidence
+//    of the choice of the two multiplying numbers and in reality numbers would 
+//    likely be much more complicated. But the above is easy to show. Note that 
+//    the numbers to the left of the column with 00000008 are outside the range 
+//    of 128 bits. As a result, in our implementation below, we skip the steps that 
+//    create these values, as they would just get lost anyway.
+//
+// 64 bit systems:
+//    This is how it would be able to work if we could get a 128 bit result from
+//    two 64 bit values. None of the 64 bit systems that we are currently working
+//    with have C language support for multiplying two 64 bit numbers and retrieving
+//    the 128 bit result. However, many 64 bit platforms have support at the asm
+//    level for doing such a thing.
+//                                                      Part 1            Part 0
+//                                            0000000000000002  0000000000000001
+//                                         x  0000000000000002  0000000000000001
+//                                   -------------------------------------------
+//                                          | 0000000000000002  0000000000000001
+//                      +  0000000000000004 | 0000000000000002 (0000000000000000)
+//     -------------------------------------------------------------------------
+//
+void int128_t_base::operatorMul(const int128_t_base& a, const int128_t_base& b, int128_t_base& result)
+{
+	// To consider: Use compiler or OS-provided custom functionality here, such as
+	//              Windows UnsignedMultiply128 and GCC's built-in int128_t.
+
+	#if EA_INT128_USE_INT64
+		#if   defined(DISABLED_PLATFORM_WIN64)
+			// To do: Implement x86-64 asm here.
+
+		#else
+			// Else we are stuck doing something less efficient. In this case we 
+			// fall back to doing 32 bit multiplies as with 32 bit platforms.
+			result       = (a.mPart0 & 0xffffffff) *  (b.mPart0 & 0xffffffff);
+			int128_t v01 = (a.mPart0 & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff);
+			int128_t v02 = (a.mPart0 & 0xffffffff) *  (b.mPart1 & 0xffffffff);
+			int128_t v03 = (a.mPart0 & 0xffffffff) * ((b.mPart1 >> 32) & 0xffffffff);
+
+			int128_t v10 = ((a.mPart0 >> 32) & 0xffffffff) *  (b.mPart0 & 0xffffffff);
+			int128_t v11 = ((a.mPart0 >> 32) & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff);
+			int128_t v12 = ((a.mPart0 >> 32) & 0xffffffff) *  (b.mPart1 & 0xffffffff);
+
+			int128_t v20 = (a.mPart1 & 0xffffffff) *  (b.mPart0 & 0xffffffff);
+			int128_t v21 = (a.mPart1 & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff);
+
+			int128_t v30 = ((a.mPart1 >> 32) & 0xffffffff) * (b.mPart0 & 0xffffffff);
+
+			// Do row addition, shifting as needed. 
+			operatorPlus(result, v01 << 32, result);
+			operatorPlus(result, v02 << 64, result);
+			operatorPlus(result, v03 << 96, result);
+
+			operatorPlus(result, v10 << 32, result);
+			operatorPlus(result, v11 << 64, result);
+			operatorPlus(result, v12 << 96, result);
+
+			operatorPlus(result, v20 << 64, result);
+			operatorPlus(result, v21 << 96, result);
+
+			operatorPlus(result, v30 << 96, result);
+		#endif
+	#else
+		// Do part-by-part multiplication, skipping overflowing combinations.
+		result        = ((uint64_t)a.mPart0) * ((uint64_t)b.mPart0);
+		uint128_t v01 = ((uint64_t)a.mPart0) * ((uint64_t)b.mPart1);
+		uint128_t v02 = ((uint64_t)a.mPart0) * ((uint64_t)b.mPart2);
+		uint128_t v03 = ((uint64_t)a.mPart0) * ((uint64_t)b.mPart3);
+
+		uint128_t v10 = ((uint64_t)a.mPart1) * ((uint64_t)b.mPart0);
+		uint128_t v11 = ((uint64_t)a.mPart1) * ((uint64_t)b.mPart1);
+		uint128_t v12 = ((uint64_t)a.mPart1) * ((uint64_t)b.mPart2);
+
+		uint128_t v20 = ((uint64_t)a.mPart2) * ((uint64_t)b.mPart0);
+		uint128_t v21 = ((uint64_t)a.mPart2) * ((uint64_t)b.mPart1);
+
+		uint128_t v30 = ((uint64_t)a.mPart3) * ((uint64_t)b.mPart0);
+
+		// Do row addition, shifting as needed. 
+		operatorPlus(result, v01 << 32, result);
+		operatorPlus(result, v02 << 64, result);
+		operatorPlus(result, v03 << 96, result);
+
+		operatorPlus(result, v10 << 32, result);
+		operatorPlus(result, v11 << 64, result);
+		operatorPlus(result, v12 << 96, result);
+
+		operatorPlus(result, v20 << 64, result);
+		operatorPlus(result, v21 << 96, result);
+
+		operatorPlus(result, v30 << 96, result);
+	#endif
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorShiftRight
+//
+// Returns: value >> nShift into result
+// The output 'result' may *not* be the same as one the input.
+// With rightward shifts of negative numbers, shift in zero from the left side.
+//
+void int128_t_base::operatorShiftRight(const int128_t_base& value, int nShift, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		if(nShift >= 0)
+		{
+			if(nShift < 64)
+			{   // 0 - 63
+				result.mPart1 = (value.mPart1 >> nShift);
+
+				if(nShift == 0)
+					result.mPart0 = (value.mPart0 >> nShift);
+				else
+					result.mPart0 = (value.mPart0 >> nShift) | (value.mPart1 << (64 - nShift));
+			}
+			else
+			{   // 64+
+				result.mPart1 = 0;
+				result.mPart0 = (value.mPart1 >> (nShift - 64));
+			}
+		}
+		else // (nShift < 0)
+			operatorShiftLeft(value, -nShift, result);
+	#else
+		if(nShift >= 0)
+		{
+			if(nShift <= 32)
+			{
+				if(nShift == 32)
+				{   // We can't use the code further below for 0-31 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = value.mPart1;
+					result.mPart1 = value.mPart2;
+					result.mPart2 = value.mPart3;
+					result.mPart3 = 0;
+				}
+				else
+				{   // 0 - 31
+					result.mPart3 = (value.mPart3 >> nShift);
+					result.mPart2 = (value.mPart2 >> nShift) | (value.mPart3 << (32 - nShift));
+					result.mPart1 = (value.mPart1 >> nShift) | (value.mPart2 << (32 - nShift));
+					result.mPart0 = (value.mPart0 >> nShift) | (value.mPart1 << (32 - nShift));
+				}
+			}
+			else if(nShift <= 64)
+			{
+				if(nShift == 64)
+				{   // We can't use the code further below for 0-31 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = value.mPart2;
+					result.mPart1 = value.mPart3;
+					result.mPart2 = 0;
+					result.mPart3 = 0;
+				}
+				else
+				{   // 33 - 63
+					result.mPart3 = 0;
+					result.mPart2 = (value.mPart3 >> (nShift - 32));
+					result.mPart1 = (value.mPart2 >> (nShift - 32)) | (value.mPart3 << (64 - nShift));
+					result.mPart0 = (value.mPart1 >> (nShift - 32)) | (value.mPart2 << (64 - nShift));
+				}
+			}
+			else if(nShift <= 96)
+			{
+				if(nShift == 96)
+				{   // We can't use the code further below for 0-31 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = value.mPart3;
+					result.mPart1 = 0;
+					result.mPart2 = 0;
+					result.mPart3 = 0;
+				}
+				else
+				{   // 65 - 95
+					result.mPart3 = 0;
+					result.mPart2 = 0;
+					result.mPart1 = (value.mPart3 >> (nShift - 64));
+					result.mPart0 = (value.mPart2 >> (nShift - 64)) | (value.mPart3 << (96 - nShift));
+				}
+			}
+			else if(nShift < 128)
+			{   // 96 - 127
+				result.mPart3 = 0;
+				result.mPart2 = 0;
+				result.mPart1 = 0;
+				result.mPart0 = (value.mPart3 >> (nShift - 96));
+			}
+			else
+			{   // 128+
+				result.mPart3 = 0;
+				result.mPart2 = 0;
+				result.mPart1 = 0;
+				result.mPart0 = 0;
+			}
+		}
+		else // (nShift < 0)
+			operatorShiftLeft(value, -nShift, result);
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorShiftRight
+//
+// Returns: value << nShift into result
+// The output 'result' may *not* be the same as one the input.
+// With rightward shifts of negative numbers, shift in zero from the left side.
+//
+void int128_t_base::operatorShiftLeft(const int128_t_base& value, int nShift, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		if(nShift >= 0)
+		{
+			if(nShift < 64)
+			{
+				if(nShift) // We need to have a special case because CPUs convert a shift by 64 to a no-op.
+				{
+					// 1 - 63
+					result.mPart0 = (value.mPart0 << nShift);
+					result.mPart1 = (value.mPart1 << nShift) | (value.mPart0 >> (64 - nShift));
+				}
+				else
+				{
+					result.mPart0 = value.mPart0;
+					result.mPart1 = value.mPart1;
+				}
+			}
+			else
+			{   // 64+
+				result.mPart0 = 0;
+				result.mPart1 = (value.mPart0 << (nShift - 64));
+			}
+		}
+		else // (nShift < 0)
+			operatorShiftRight(value, -nShift, result);
+	#else
+		if(nShift >= 0)
+		{
+			if(nShift <= 32)
+			{
+				if(nShift == 32)
+				{   // We can't use the code further below for 32 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = 0;
+					result.mPart1 = value.mPart0;
+					result.mPart2 = value.mPart1;
+					result.mPart3 = value.mPart2;
+				}
+				else if(nShift)
+				{   // 1 - 31
+					result.mPart0 = (value.mPart0 << nShift);
+					result.mPart1 = (value.mPart1 << nShift) | (value.mPart0 >> (32 - nShift));
+					result.mPart2 = (value.mPart2 << nShift) | (value.mPart1 >> (32 - nShift));
+					result.mPart3 = (value.mPart3 << nShift) | (value.mPart2 >> (32 - nShift));
+				}
+				else
+				{
+					result.mPart0 = value.mPart0;
+					result.mPart1 = value.mPart1;
+					result.mPart2 = value.mPart2;
+					result.mPart3 = value.mPart3;
+				}
+			}
+			else if(nShift <= 64)
+			{
+				if(nShift == 64)
+				{   // We can't use the code further below for 0-31 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = 0;
+					result.mPart1 = 0;
+					result.mPart2 = value.mPart0;
+					result.mPart3 = value.mPart1;
+				}
+				else
+				{   // 33 - 63
+					result.mPart0 = 0;
+					result.mPart1 = (value.mPart0 << (nShift - 32));
+					result.mPart2 = (value.mPart1 << (nShift - 32)) | (value.mPart0 >> (64 - nShift));
+					result.mPart3 = (value.mPart2 << (nShift - 32)) | (value.mPart1 >> (64 - nShift));
+				}
+			}
+			else if(nShift <= 96)
+			{
+				if(nShift == 96)
+				{   // We can't use the code further below for 0-31 because 32 bit 
+					// processors (e.g. Intel) often implement a shift of 32 as a no-op.
+					result.mPart0 = 0;
+					result.mPart1 = 0;
+					result.mPart2 = 0;
+					result.mPart3 = value.mPart0;
+				}
+				else
+				{   // 65 - 95
+					result.mPart0 = 0;
+					result.mPart1 = 0;
+					result.mPart2 = (value.mPart0 << (nShift - 64));
+					result.mPart3 = (value.mPart1 << (nShift - 64)) | (value.mPart0 >> (96 - nShift));
+				}
+			}
+			else if(nShift < 128)
+			{   // 96 - 127
+				result.mPart0 = 0;
+				result.mPart1 = 0;
+				result.mPart2 = 0;
+				result.mPart3 = (value.mPart0 << (nShift - 96));
+			}
+			else
+			{   // 128+
+				result.mPart3 = 0;
+				result.mPart2 = 0;
+				result.mPart1 = 0;
+				result.mPart0 = 0;
+			}
+		}
+		else // (nShift < 0)
+			operatorShiftRight(value, -nShift, result);
+	#endif
+}
+
+
+bool int128_t_base::operator!() const
+{
+	#if EA_INT128_USE_INT64
+		return (mPart0 == 0) && (mPart1 == 0);
+	#else
+		return (mPart0 == 0) && (mPart1 == 0) && (mPart2 == 0) && (mPart3 == 0);
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorXOR
+//
+// Returns: value1 ^ value2 into result
+// The output 'result' may be the same as one the input.
+//
+void int128_t_base::operatorXOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		result.mPart0 = (value1.mPart0 ^ value2.mPart0);
+		result.mPart1 = (value1.mPart1 ^ value2.mPart1);
+	#else
+		result.mPart0 = (value1.mPart0 ^ value2.mPart0);
+		result.mPart1 = (value1.mPart1 ^ value2.mPart1);
+		result.mPart2 = (value1.mPart2 ^ value2.mPart2);
+		result.mPart3 = (value1.mPart3 ^ value2.mPart3);
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorOR
+//
+// Returns: value1 | value2 into result
+// The output 'result' may be the same as one the input.
+//
+void int128_t_base::operatorOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		result.mPart0 = (value1.mPart0 | value2.mPart0);
+		result.mPart1 = (value1.mPart1 | value2.mPart1);
+	#else
+		result.mPart0 = (value1.mPart0 | value2.mPart0);
+		result.mPart1 = (value1.mPart1 | value2.mPart1);
+		result.mPart2 = (value1.mPart2 | value2.mPart2);
+		result.mPart3 = (value1.mPart3 | value2.mPart3);
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operatorAND
+//
+// Returns: value1 & value2 into result
+// The output 'result' may be the same as one the input.
+//
+void int128_t_base::operatorAND(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result)
+{
+	#if EA_INT128_USE_INT64
+		result.mPart0 = (value1.mPart0 & value2.mPart0);
+		result.mPart1 = (value1.mPart1 & value2.mPart1);
+	#else
+		result.mPart0 = (value1.mPart0 & value2.mPart0);
+		result.mPart1 = (value1.mPart1 & value2.mPart1);
+		result.mPart2 = (value1.mPart2 & value2.mPart2);
+		result.mPart3 = (value1.mPart3 & value2.mPart3);
+	#endif
+}
+
+
+bool int128_t_base::AsBool() const
+{
+	#if EA_INT128_USE_INT64
+		return (mPart0 || mPart1);
+	#else
+		return (mPart0 || mPart1 || mPart2 || mPart3);
+	#endif
+}
+
+
+uint8_t int128_t_base::AsUint8() const
+{
+	// OK for EA_INT128_USE_INT64
+	return (uint8_t) mPart0;
+}
+
+
+uint16_t int128_t_base::AsUint16() const
+{
+	// OK for EA_INT128_USE_INT64
+	return (uint16_t) mPart0;
+}
+
+
+uint32_t int128_t_base::AsUint32() const
+{
+	// OK for EA_INT128_USE_INT64
+	return (uint32_t) mPart0;
+}
+
+
+uint64_t int128_t_base::AsUint64() const
+{
+	#if EA_INT128_USE_INT64
+		return mPart0;
+	#else
+		return (((uint64_t) mPart1) << 32) + mPart0;
+	#endif
+}
+
+
+int int128_t_base::GetBit(int nIndex) const
+{
+	// EA_ASSERT((nIndex >= 0) && (nIndex < 128));
+
+	#if EA_INT128_USE_INT64
+		const uint64_t nBitMask = ((uint64_t)1 << (nIndex % 64));
+
+		if(nIndex < 64)
+			return ((mPart0 & nBitMask) ? 1 : 0);
+		else if(nIndex < 128)
+			return ((mPart1 & nBitMask) ? 1 : 0);
+		return 0;
+	#else
+		const uint32_t nBitMask = ((uint32_t)1 << (nIndex % 32));
+
+		if(nIndex < 32)
+			return ((mPart0 & nBitMask) ? 1 : 0);
+		else if(nIndex < 64)
+			return ((mPart1 & nBitMask) ? 1 : 0);
+		else if(nIndex < 96)
+			return ((mPart2 & nBitMask) ? 1 : 0);
+		else if(nIndex < 128)
+			return ((mPart3 & nBitMask) ? 1 : 0);
+		return 0;
+	#endif
+}
+
+
+void int128_t_base::SetBit(int nIndex, int value)
+{
+	// EA_ASSERT((nIndex >= 0) && (nIndex < 128));
+
+	#if EA_INT128_USE_INT64
+		const uint64_t nBitMask = ((uint64_t)1 << (nIndex % 64));
+
+		if(nIndex < 64)
+		{
+			if(value)
+				mPart0 = mPart0 |  nBitMask;
+			else
+				mPart0 = mPart0 & ~nBitMask;
+		}
+		else if(nIndex < 128)
+		{
+			if(value)
+				mPart1 = mPart1 |  nBitMask;
+			else
+				mPart1 = mPart1 & ~nBitMask;
+		}
+	#else
+		const uint32_t nBitMask = ((uint32_t)1 << (nIndex % 32));
+
+		if(nIndex < 32)
+		{
+			if(value)
+				mPart0 = mPart0 |  nBitMask;
+			else
+				mPart0 = mPart0 & ~nBitMask;
+		}
+		else if(nIndex < 64)
+		{
+			if(value)
+				mPart1 = mPart1 |  nBitMask;
+			else
+				mPart1 = mPart1 & ~nBitMask;
+		}
+		else if(nIndex < 96)
+		{
+			if(value)
+				mPart2 = mPart2 |  nBitMask;
+			else
+				mPart2 = mPart2 & ~nBitMask;
+		}
+		else if(nIndex < 128)
+		{
+			if(value)
+				mPart3 = mPart3 |  nBitMask;
+			else
+				mPart3 = mPart3 & ~nBitMask;
+		}
+	#endif
+}
+
+
+// part is in the range of [0,15]
+uint8_t int128_t_base::GetPartUint8(int nIndex) const
+{
+	#if EA_INT128_USE_INT64
+		uint64_t value(0);
+
+		switch (nIndex / 8) 
+		{
+			case 0:
+				value = mPart0;
+				break;
+
+			case 1:
+				value = mPart1;
+				break;
+		}
+
+		nIndex = ((nIndex % 8) * 8);
+		return (uint8_t)((value & ((uint64_t)0xff << nIndex)) >> nIndex);
+	#else
+		uint32_t value(0);
+
+		switch (nIndex / 4) 
+		{
+			case 0:
+				value = mPart0;
+				break;
+
+			case 1:
+				value = mPart1;
+				break;
+
+			case 2:
+				value = mPart2;
+				break;
+
+			case 3:
+				value = mPart3;
+				break;
+		}
+
+		nIndex = ((nIndex % 4) * 8);
+		return (uint8_t)(((value & ((uint32_t)0xff << nIndex))) >> nIndex);
+	#endif
+}
+
+
+// part is in the range of [0,7]
+uint16_t int128_t_base::GetPartUint16(int nIndex) const
+{
+	#if EA_INT128_USE_INT64
+		uint64_t value(0);
+
+		switch (nIndex / 4) 
+		{
+			case 0:
+				value = mPart0;
+				break;
+
+			case 1:
+				value = mPart1;
+				break;
+		}
+
+		nIndex = ((nIndex % 4) * 16);
+		return (uint16_t)(((value & ((uint64_t)0xffff << nIndex))) >> nIndex);
+	#else
+		uint32_t value(0);
+
+		switch (nIndex / 2) 
+		{
+			case 0:
+				value = mPart0;
+				break;
+
+			case 1:
+				value = mPart1;
+				break;
+
+			case 2:
+				value = mPart2;
+				break;
+
+			case 3:
+				value = mPart3;
+				break;
+		}
+
+		if(nIndex % 2)
+			return (uint16_t)(value >> 16);
+		else
+			return (uint16_t)(value);
+	#endif
+}
+
+
+// part is in the range of [0,3]
+uint32_t int128_t_base::GetPartUint32(int nIndex) const
+{
+	#if EA_INT128_USE_INT64
+		switch (nIndex) 
+		{
+			case 0:
+				return (uint32_t) mPart0;
+			case 1:
+				return (uint32_t)(mPart0 >> 32);
+			case 2:
+				return (uint32_t) mPart1;
+			case 3:
+				return (uint32_t)(mPart1 >> 32);
+		}
+		return 0;
+	#else
+		switch (nIndex) 
+		{
+			case 0:
+				return mPart0;
+			case 1:
+				return mPart1;
+			case 2:
+				return mPart2;
+			case 3:
+				return mPart3;
+		}
+		return 0;
+	#endif
+}
+
+
+// part is in the range of [0,1]
+uint64_t int128_t_base::GetPartUint64(int nIndex) const
+{
+	#if EA_INT128_USE_INT64
+		if(nIndex == 0)
+			return mPart0;
+		else if(nIndex == 1)
+			return mPart1;
+		return 0;
+	#else
+		if(nIndex == 0)
+			return uint64_t((uint64_t(mPart1) << 32) + mPart0);
+		else if(nIndex == 1)
+			return uint64_t((uint64_t(mPart3) << 32) + mPart2);
+		return 0;
+	#endif
+}
+
+
+void int128_t_base::SetPartUint8(int nIndex, uint8_t value)
+{
+	#if EA_INT128_USE_INT64
+		uint64_t* pValue;
+
+		switch (nIndex / 8) 
+		{
+			case 0:
+				pValue = &mPart0;
+				break;
+
+			case 1:
+				pValue = &mPart1;
+				break;
+
+			default:
+				return;
+		}
+
+		nIndex %= 8;
+		*pValue = ((*pValue & ~(UINT64_C(0xff) << (nIndex * 8))) + ((uint64_t)value << (nIndex * 8)));
+
+	#else
+		uint32_t* pValue;
+
+		switch (nIndex / 4) 
+		{
+			case 0:
+				pValue = &mPart0;
+				break;
+
+			case 1:
+				pValue = &mPart1;
+				break;
+
+			case 2:
+				pValue = &mPart2;
+				break;
+
+			case 3:
+				pValue = &mPart3;
+				break;
+
+			default:
+				return;
+		}
+
+		switch (nIndex % 4)
+		{
+			case 0:
+				*pValue = ((*pValue & 0xffffff00) + (value <<  0));
+				break;
+
+			case 1:
+				*pValue = ((*pValue & 0xffff00ff) + (value <<  8));
+				break;
+
+			case 2:
+				*pValue = ((*pValue & 0xff00ffff) + (value << 16));
+				break;
+
+			case 3:
+				*pValue = ((*pValue & 0x00ffffff) + (value << 24));
+				break;
+		}
+	#endif
+}
+
+
+void int128_t_base::SetPartUint16(int nIndex, uint16_t value)
+{
+	#if EA_INT128_USE_INT64
+		uint64_t* pValue;
+
+		switch (nIndex / 4) 
+		{
+			case 0:
+				pValue = &mPart0;
+				break;
+
+			case 1:
+				pValue = &mPart1;
+				break;
+
+			default:
+				return;
+		}
+
+		nIndex %= 4;
+		*pValue = ((*pValue & ~(UINT64_C(0xffff) << (nIndex * 16))) + ((uint64_t)value << (nIndex * 16)));
+
+	#else
+		uint32_t* pValue;
+
+		switch (nIndex / 2) 
+		{
+			case 0:
+				pValue = &mPart0;
+				break;
+
+			case 1:
+				pValue = &mPart1;
+				break;
+
+			case 2:
+				pValue = &mPart2;
+				break;
+
+			case 3:
+				pValue = &mPart3;
+				break;
+
+			default:
+				return;
+		}
+
+		if(nIndex % 2)
+			*pValue = ((*pValue & 0x0000ffff) + (value << 16));
+		else
+			*pValue = ((*pValue & 0xffff0000) + (value));
+	#endif
+}
+
+
+void int128_t_base::SetPartUint32(int nIndex, uint32_t value)
+{
+	#if EA_INT128_USE_INT64
+		switch (nIndex) 
+		{
+			case 0:
+				mPart0 = (mPart0 & UINT64_C(0xffffffff00000000)) + value;
+				break;
+			case 1:
+				mPart0 = (mPart0 & UINT64_C(0x00000000ffffffff)) + ((uint64_t)value << 32);
+				break;
+			case 2:
+				mPart1 = (mPart1 & UINT64_C(0xffffffff00000000)) + value;
+				break;
+			case 3:
+				mPart1 = (mPart1 & UINT64_C(0x00000000ffffffff)) + ((uint64_t)value << 32);
+				break;
+		}
+	#else
+		switch (nIndex) 
+		{
+			case 0:
+				mPart0 = value;
+				break;
+			case 1:
+				mPart1 = value;
+				break;
+			case 2:
+				mPart2 = value;
+				break;
+			case 3:
+				mPart3 = value;
+				break;
+		}
+	#endif
+}
+
+
+void int128_t_base::SetPartUint64(int nIndex, uint64_t value)
+{
+	#if EA_INT128_USE_INT64
+		if(nIndex == 0)
+			mPart0 = value;
+		else if(nIndex == 1)
+			mPart1 = value;
+	#else
+		if(nIndex == 0)
+		{
+			mPart0 = (uint32_t)(value);
+			mPart1 = (uint32_t)(value >> 32);
+		}
+		else if(nIndex == 1)
+		{
+			mPart2 = (uint32_t)(value);
+			mPart3 = (uint32_t)(value >> 32);
+		}
+	#endif
+}
+
+
+bool int128_t_base::IsZero() const
+{
+	#if EA_INT128_USE_INT64
+		return (mPart0 == 0) && // Check mPart0 first as this will likely yield faster execution.
+			   (mPart1 == 0);
+	#else
+		return (mPart0 == 0) && // Check mPart0 first as this will likely yield faster execution.
+			   (mPart1 == 0) && 
+			   (mPart2 == 0) && 
+			   (mPart3 == 0);
+	#endif
+}
+
+
+void int128_t_base::SetZero()
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = 0;
+	#else
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = 0;
+		mPart0 = 0;
+	#endif
+}
+
+
+void int128_t_base::TwosComplement()
+{
+	#if EA_INT128_USE_INT64
+		mPart1 = ~mPart1;
+		mPart0 = ~mPart0;
+	#else
+		mPart3 = ~mPart3;
+		mPart2 = ~mPart2;
+		mPart1 = ~mPart1;
+		mPart0 = ~mPart0;
+	#endif
+
+	// What we want to do, but isn't available at this level:
+	// operator++();
+	// Alternative:
+	int128_t_base one((uint32_t)1);
+	operatorPlus(*this, one, *this);
+}
+
+
+void int128_t_base::InverseTwosComplement()
+{
+	// What we want to do, but isn't available at this level:
+	// operator--();
+	// Alternative:
+	int128_t_base one((uint32_t)1);
+	operatorMinus(*this, one, *this);
+
+	#if EA_INT128_USE_INT64
+		mPart1 = ~mPart1;
+		mPart0 = ~mPart0;
+	#else
+		mPart3 = ~mPart3;
+		mPart2 = ~mPart2;
+		mPart1 = ~mPart1;
+		mPart0 = ~mPart0;
+	#endif
+}
+
+
+void int128_t_base::DoubleToUint128(double value)
+{
+	// Currently this function is limited to 64 bits of integer input.
+	// We need to make a better version of this function. Perhaps we should implement 
+	// it via dissecting the IEEE floating point format (sign, exponent, matissa).
+	// EA_ASSERT(fabs(value) < 18446744073709551616.0); // Assert that the input is <= 64 bits of integer.
+
+	#if EA_INT128_USE_INT64
+		mPart1 = 0;
+		mPart0 = (value >= 0 ? (uint64_t)value : (uint64_t)-value);
+	#else
+		const uint64_t value64 = (value >= 0 ? (uint64_t)value : (uint64_t)-value);
+
+		mPart3 = 0;
+		mPart2 = 0;
+		mPart1 = (uint32_t) (value64 >> 32);
+		mPart0 = (uint32_t)((value64 >>  0) & 0xffffffff);
+
+		// Below is a version I have been working on a version that works up to the full 128 bits.
+		// The implementation below has a roundoff problem for some cases and would have to be reworked.
+		/*
+		double valueTemp(value);
+
+		if(value < 0)
+			valueTemp = -valueTemp;
+
+		//Get part3
+		mPart3      = (uint32_t)(valueTemp / 79228162514264337593543950336.0); // 79228162514264337593543950336.0 is the same as 0xffffffffffffffffffffffff + 1, or 0x1000000000000000000000000.
+		valueTemp -= (mPart3 * 79228162514264337593543950336.0);
+
+		//Get part2
+		mPart2      = (uint32_t)(valueTemp / 18446744073709551616.0); // 18446744073709551616.0 is the same as 0xffffffffffffffff + 1, or 0x10000000000000000.
+		valueTemp -= (mPart2 * 18446744073709551616.0);
+
+		//Get part1
+		mPart1      = (uint32_t)(valueTemp / 4294967296.0); // 4294967296.0 is the same as 0xffffffff + 1, or 0x100000000.
+		valueTemp -= (mPart1 * 4294967296.0);
+
+		//Get part0
+		mPart0      = (uint32_t)(valueTemp);
+		*/
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// int128_t
+///////////////////////////////////////////////////////////////////////////////
+
+int128_t::int128_t()
+	#if EA_INT128_USE_INT64
+	  : int128_t_base(0, 0)
+	#else
+	  : int128_t_base(0, 0, 0, 0)
+	#endif
+{
+}
+
+
+int128_t::int128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3)
+	: int128_t_base(nPart0, nPart1, nPart2, nPart3) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+int128_t::int128_t(uint64_t nPart0, uint64_t nPart1)
+	: int128_t_base(nPart0, nPart1) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+int128_t::int128_t(uint8_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+int128_t::int128_t(uint16_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+int128_t::int128_t(uint32_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+#if defined(INT128_UINT_TYPE)
+
+int128_t::int128_t(INT128_UINT_TYPE value)
+	: int128_t_base((uint64_t)value) // OK for EA_INT128_USE_INT64
+{
+}
+
+#endif
+
+
+int128_t::int128_t(uint64_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+int128_t::int128_t(int8_t value)
+{
+	if(value < 0)
+	{
+		*this = int128_t((uint8_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = value;
+		#endif
+	}
+}
+
+
+int128_t::int128_t(int16_t value)
+{
+	if(value < 0)
+	{
+		*this = int128_t((uint16_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = value;
+		#endif
+	}
+}
+
+
+int128_t::int128_t(int32_t value)
+{
+	if(value < 0)
+	{
+		*this = int128_t((uint32_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = (uint32_t)value;
+		#endif
+	}
+}
+
+
+#if defined(INT128_INT_TYPE)
+
+int128_t::int128_t(INT128_INT_TYPE value)
+{
+	operator=(int128_t((int64_t)value));
+}
+
+#endif
+
+
+int128_t::int128_t(int64_t value)
+{
+	if(value < 0)
+	{
+		*this = int128_t((int64_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = (uint64_t) (value);
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = (uint32_t) ((value >> 32) & 0xffffffff);
+			mPart0 = (uint32_t) (value & 0xffffffff);
+		#endif
+	}
+}
+
+
+int128_t::int128_t(const int128_t& value)
+	:  int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+// Not defined because doing so would make the compiler unable to 
+// decide how to choose binary functions involving int128/uint128.
+//int128_t::int128_t(const uint128_t& value)
+//    :  int128_t_base(value) // OK for EA_INT128_USE_INT64
+//{
+//}
+
+
+int128_t::int128_t(const float value)
+{
+	// OK for EA_INT128_USE_INT64
+	DoubleToUint128(value);
+	if(value < 0)
+		Negate();
+}
+
+
+int128_t::int128_t(const double value)
+{
+	// OK for EA_INT128_USE_INT64
+	DoubleToUint128(value);
+	if(value < 0)
+		Negate();
+}
+
+
+int128_t::int128_t(const char* pValue, int nBase){
+	// OK for EA_INT128_USE_INT64
+	const int128_t value(StrToInt128(pValue, NULL, nBase));
+	operator=(value);
+}
+
+
+int128_t::int128_t(const wchar_t* pValue, int nBase){
+	// OK for EA_INT128_USE_INT64
+	wchar_t* pTextEnd(NULL);
+	const int128_t value(StrToInt128(pValue, &pTextEnd, nBase));
+	operator=(value);
+}
+
+
+int128_t& int128_t::operator=(const int128_t_base& value)
+{
+	// C++ requires operator= to be subclassed, even if the subclassed 
+	// implementation is identical to the base implementation.
+	// OK for EA_INT128_USE_INT64
+	int128_t_base::operator=(value);
+	return *this;
+}
+
+
+int128_t int128_t::operator-() const
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t returnValue(*this);
+	returnValue.Negate();
+	return returnValue;
+}
+
+
+int128_t& int128_t::operator++()
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t_base one((uint32_t)1);
+	operatorPlus(*this, one, *this);
+	return *this;
+}
+
+
+int128_t& int128_t::operator--()
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t_base one((uint32_t)1);
+	operatorMinus(*this, one, *this);
+	return *this;
+}
+
+
+int128_t int128_t::operator++(int)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp((uint32_t)1);
+	operatorPlus(*this, temp, temp);
+	return temp;
+}
+
+
+int128_t int128_t::operator--(int)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp((uint32_t)1);
+	operatorMinus(*this, temp, temp);
+	return temp;
+}
+
+
+int128_t int128_t::operator+() const
+{
+	// OK for EA_INT128_USE_INT64
+	return *this;
+}
+
+
+int128_t int128_t::operator~() const
+{
+	#if EA_INT128_USE_INT64
+		return int128_t(~mPart0, ~mPart1);
+	#else
+		return int128_t(~mPart0, ~mPart1, ~mPart2, ~mPart3);
+	#endif
+}
+
+int128_t operator+(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	int128_t::operatorPlus(value1, value2, temp);
+	return temp;
+}
+
+
+int128_t operator-(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	int128_t::operatorMinus(value1, value2, temp);
+	return temp;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operator *
+//
+int128_t operator*(const int128_t& value1, const int128_t& value2)
+{
+	int128_t a(value1);
+	int128_t b(value2);
+	int128_t returnValue;
+
+	// Correctly handle negative values
+	bool bANegative(false);
+	bool bBNegative(false);
+
+	if(a.IsNegative())
+	{
+		bANegative = true;
+		a.Negate();
+	}
+
+	if(b.IsNegative())
+	{
+		bBNegative = true;
+		b.Negate();
+	}
+
+	int128_t_base::operatorMul(a, b, returnValue);
+
+	// Do negation as needed.
+	if(bANegative != bBNegative)
+		returnValue.Negate();
+
+	return returnValue;
+}
+
+
+int128_t operator/(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t remainder;
+	int128_t quotient;
+	value1.Modulus(value2, quotient, remainder);
+	return quotient;
+}
+
+
+int128_t operator%(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t remainder;
+	int128_t quotient;
+	value1.Modulus(value2, quotient, remainder);
+	return remainder;
+}
+
+
+int128_t& int128_t::operator+=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorPlus(*this, value, *this);
+	return *this;
+}
+
+
+int128_t& int128_t::operator-=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorMinus(*this, value, *this);
+	return *this;
+}
+
+
+int128_t& int128_t::operator*=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this * value;
+	return *this;
+}
+
+
+int128_t& int128_t::operator/=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this / value;
+	return *this;
+}
+
+
+int128_t& int128_t::operator%=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this % value;
+	return *this;
+}
+
+
+
+// With rightward shifts of negative numbers, shift in zero from the left side.
+int128_t int128_t::operator>>(int nShift) const
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	operatorShiftRight(*this, nShift, temp);
+	return temp;
+}
+
+
+// With rightward shifts of negative numbers, shift in zero from the left side.
+int128_t int128_t::operator<<(int nShift) const
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	operatorShiftLeft(*this, nShift, temp);
+	return temp;
+}
+
+
+int128_t& int128_t::operator>>=(int nShift)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	operatorShiftRight(*this, nShift, temp);
+	*this = temp;
+	return *this;
+}
+
+
+int128_t& int128_t::operator<<=(int nShift)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	operatorShiftLeft(*this, nShift, temp);
+	*this = temp;
+	return *this;
+}
+
+
+int128_t operator^(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	int128_t::operatorXOR(value1, value2, temp);
+	return temp;
+}
+
+
+int128_t operator|(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	int128_t::operatorOR(value1, value2, temp);
+	return temp;
+}
+
+
+int128_t operator&(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t temp;
+	int128_t::operatorAND(value1, value2, temp);
+	return temp;
+}
+
+
+int128_t& int128_t::operator^=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorXOR(*this, value, *this);
+	return *this;
+}
+
+
+int128_t& int128_t::operator|=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorOR(*this, value, *this);
+	return *this;
+}
+
+
+int128_t& int128_t::operator&=(const int128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorAND(*this, value, *this);
+	return *this;
+}
+
+
+// This function forms the basis of all logical comparison functions.
+// If value1 <  value2, the return value is -1.
+// If value1 == value2, the return value is 0.
+// If value1 >  value2, the return value is 1.
+int compare(const int128_t& value1, const int128_t& value2)
+{
+	// Cache some values. Positive means >= 0. Negative means < 0 and thus means '!positive'.
+	const bool bValue1IsPositive(value1.IsPositive());
+	const bool bValue2IsPositive(value2.IsPositive());
+
+	// Do positive/negative tests.
+	if(bValue1IsPositive != bValue2IsPositive)
+		return bValue1IsPositive ? 1 : -1;
+
+	// Compare individual parts. At this point, the two numbers have the same sign.
+	#if EA_INT128_USE_INT64
+		if(value1.mPart1 == value2.mPart1)
+		{
+			if(value1.mPart0 == value2.mPart0)
+				return 0;
+			else if(value1.mPart0 > value2.mPart0)
+				return 1;
+			// return -1; //Just fall through to the end.
+		}
+		else if(value1.mPart1 > value2.mPart1)
+			return 1;
+		return -1;
+	#else
+		if(value1.mPart3 == value2.mPart3)
+		{
+			if(value1.mPart2 == value2.mPart2)
+			{
+				if(value1.mPart1 == value2.mPart1)
+				{
+					if(value1.mPart0 == value2.mPart0)
+						return 0;
+					else if(value1.mPart0 > value2.mPart0)
+						return 1;
+					// return -1; //Just fall through to the end.
+				}
+				else if(value1.mPart1 > value2.mPart1)
+					return 1;
+				// return -1; //Just fall through to the end.
+			}
+			else if(value1.mPart2 > value2.mPart2)
+				return 1;
+			// return -1; //Just fall through to the end.
+		}
+		else if(value1.mPart3 > value2.mPart3)
+			return 1;
+		return -1;
+	#endif
+}
+
+
+bool operator==(const int128_t& value1, const int128_t& value2)
+{
+	#if EA_INT128_USE_INT64
+		return (value1.mPart0 == value2.mPart0) && // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 == value2.mPart1);
+	#else
+		return (value1.mPart0 == value2.mPart0) && // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 == value2.mPart1) && 
+			   (value1.mPart2 == value2.mPart2) && 
+			   (value1.mPart3 == value2.mPart3);
+	#endif
+}
+
+bool operator!=(const int128_t& value1, const int128_t& value2)
+{
+	#if EA_INT128_USE_INT64
+		return (value1.mPart0 != value2.mPart0) ||  // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 != value2.mPart1);
+	#else
+		return (value1.mPart0 != value2.mPart0) ||  // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 != value2.mPart1) || 
+			   (value1.mPart2 != value2.mPart2) || 
+			   (value1.mPart3 != value2.mPart3);
+	#endif
+}
+
+
+bool operator>(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) > 0);
+}
+
+
+bool operator>=(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) >= 0);
+}
+
+
+bool operator<(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) < 0);
+}
+
+
+bool operator<=(const int128_t& value1, const int128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) <= 0);
+}
+
+
+int8_t int128_t::AsInt8() const
+{
+	// OK for EA_INT128_USE_INT64
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return (int8_t)-t.AsInt8();
+	}
+
+	return (int8_t) mPart0;
+}
+
+
+int16_t int128_t::AsInt16() const
+{
+	// OK for EA_INT128_USE_INT64
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return (int16_t)-t.AsInt16();
+	}
+
+	return (int16_t) mPart0;
+}
+
+
+int32_t int128_t::AsInt32() const
+{
+	// OK for EA_INT128_USE_INT64
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return -t.AsInt32();
+	}
+
+	return (int32_t) mPart0;
+}
+
+
+int64_t int128_t::AsInt64() const
+{
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return -t.AsUint64(); // ensure mod2 behaviour
+	}
+
+	#if EA_INT128_USE_INT64
+		return (int64_t) mPart0;
+	#else
+		return (((int64_t) mPart1) << 32) + mPart0;
+	#endif
+}
+
+
+// I am not convinced that this is a reliable method of conversion.
+float int128_t::AsFloat() const
+{
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return -t.AsFloat();
+	}
+
+	float fReturnValue(0);
+
+	#if EA_INT128_USE_INT64
+		if(mPart1)
+			fReturnValue += (mPart1 * 18446744073709551616.f);
+		if(mPart0)
+			fReturnValue +=  (float)mPart0;
+	#else
+		if(mPart3)
+			fReturnValue += (mPart3 * 79228162514264337593543950336.f);
+		if(mPart2)
+			fReturnValue += (mPart2 * 18446744073709551616.f);
+		if(mPart1)
+			fReturnValue += (mPart1 * 4294967296.f);
+		if(mPart0)
+			fReturnValue +=  (float)mPart0;
+	#endif
+
+	return fReturnValue;
+}
+
+
+// I am not convinced that this is a reliable method of conversion.
+double int128_t::AsDouble() const
+{
+	if(IsNegative())
+	{
+		int128_t t(*this);
+		t.Negate();
+		return -t.AsDouble();
+	}
+
+	double fReturnValue(0);
+
+	#if EA_INT128_USE_INT64
+		if(mPart1)
+			fReturnValue += (mPart1 * 18446744073709551616.0);
+		if(mPart0)
+			fReturnValue +=  (double)mPart0;
+	#else
+		if(mPart3)
+			fReturnValue += (mPart3 * 79228162514264337593543950336.0);
+		if(mPart2)
+			fReturnValue += (mPart2 * 18446744073709551616.0);
+		if(mPart1)
+			fReturnValue += (mPart1 * 4294967296.0);
+		if(mPart0)
+			fReturnValue +=  (double)mPart0;
+	#endif
+
+	return fReturnValue;
+}
+
+
+void int128_t::Negate()
+{
+	// OK for EA_INT128_USE_INT64
+	if(IsPositive())
+		TwosComplement();
+	else
+		InverseTwosComplement();
+}
+
+
+bool int128_t::IsNegative() const
+{   // True if value < 0
+	#if EA_INT128_USE_INT64
+		return ((mPart1 & UINT64_C(0x8000000000000000)) != 0);
+	#else
+		return ((mPart3 & 0x80000000) != 0);
+	#endif
+}
+
+
+bool int128_t::IsPositive() const
+{   // True of value >= 0
+	#if EA_INT128_USE_INT64
+		return ((mPart1 & UINT64_C(0x8000000000000000)) == 0);
+	#else
+		return ((mPart3 & 0x80000000) == 0);
+	#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Modulus
+//
+// This is a generic function that does both division modulus calculations.
+//
+void int128_t::Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t tempDividend(*this);
+	int128_t tempDivisor(divisor);
+
+	bool bDividendNegative = false;
+	bool bDivisorNegative = false;
+
+	if(tempDividend.IsNegative())
+	{
+		bDividendNegative = true;
+		tempDividend.Negate();
+	}
+	if(tempDivisor.IsNegative())
+	{
+		bDivisorNegative = true;
+		tempDivisor.Negate();
+	}
+
+	// Handle the special cases
+	if(tempDivisor.IsZero())
+	{
+		// Force a divide by zero exception. 
+		// We know that tempDivisor.mPart0 is zero.
+		quotient.mPart0 /= tempDivisor.mPart0;
+	}
+	else if(tempDividend.IsZero())
+	{
+		quotient  = int128_t((uint32_t)0);
+		remainder = int128_t((uint32_t)0);
+	}
+	else
+	{
+		remainder.SetZero();
+
+		for(int i(0); i < 128; i++)
+		{
+			remainder += (uint32_t)tempDividend.GetBit(127 - i);
+			const bool bBit(remainder >= tempDivisor);
+			quotient.SetBit(127 - i, bBit);
+
+			if(bBit)
+				remainder -= tempDivisor;
+		 
+			if((i != 127) && !remainder.IsZero())
+				remainder <<= 1;
+		}
+	}
+
+	if((bDividendNegative && !bDivisorNegative) || (!bDividendNegative && bDivisorNegative))
+	{
+		// Ensure the following formula applies for negative dividends
+		// dividend = divisor * quotient + remainder
+		quotient.Negate();
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StrToInt128
+//
+// Same as C runtime strtol function but for int128_t. 
+// This is probably the most general and useful of the C atoi family of functions.
+//
+int128_t int128_t::StrToInt128(const char* pValue, char** ppEnd, int nBase)
+{
+	int128_t    value((uint32_t)0); // Current value
+	const char* p = pValue;         // Current position
+	const char* pBegin = NULL;      // Where the digits start.
+	const char* pEnd = NULL;        // Where the digits end. One-past the last digit.
+	char        chSign('+');        // One of either '+' or '-'
+
+	// Skip leading whitespace
+	while(isspace((unsigned char)*p))
+		++p;
+
+	// Check for sign.
+	if((*p == '-') || (*p == '+'))
+		chSign = *p++;
+
+	// Do checks on 'nBase'.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (char*)pValue;
+		return value;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 2, 8, 10, or 16. 
+		if(*p != '0')
+			nBase = 10;
+		else if((p[1] == 'x') || (p[1] == 'X')) // It's safe to read p[1] because p[0] is known to be '0'.
+			nBase = 16;
+		else if((p[1] == 'b') || (p[1] == 'B'))
+			nBase = 2;
+		else
+			nBase = 8;
+	}
+
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))
+			p += 2;
+	}
+	else if(nBase == 2){
+		// If there is a leading '0b', then skip past it.
+		if((*p == '0') && ((p[1] == 'b') || (p[1] == 'B')))
+			p += 2;
+	}
+
+	// Save the position where the digits start.
+	pBegin = p;
+
+	if(nBase == 2)  // Binary
+	{
+		while((*p == '0') || (*p == '1'))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 128) // There can be at most 128 binary digits in the string.
+		{
+			pEnd = pBegin + 128;
+			p    = pEnd;
+		}
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			--p;
+			if(*p == '1')
+				value.SetBit(i, true);
+		}
+	}
+	else if(nBase == 10) // Decimal
+	{
+		while(isdigit((unsigned char)*p))
+			++p;
+		pEnd = p;
+
+		if(pEnd > pBegin + 39) // With base 10, it is not enough to simply check against 39 digits, 
+		{                      // as you can have 39 '9's and overflow. But 39 is the most you could have.
+			pEnd = pBegin + 39;
+			p    = pEnd;
+		}
+
+		int128_t multiplier((uint32_t)1);
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			const uint32_t c = (uint32_t)(*(--p) - '0');
+
+			if(c)
+			{
+				// This can be optimized for faster speed by doing the smaller orders
+				// of ten on value.mPart0 with an int multiplier instead of on value 
+				// and a int128_t multiplier.
+				value += (multiplier * c);
+			}
+
+			multiplier *= (uint32_t)10;
+		}
+	}
+	else if(nBase == 16) // Hexadecimal
+	{
+		while(isxdigit((unsigned char)*p))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 32) // There can be at most 32 hexadecimal digits in the string.
+		{
+			pEnd = pBegin + 32;
+			p    = pEnd;
+		}
+
+		// There can be as many as 32 characters.
+		for(int i(0); p > pBegin; i++)
+		{
+			#if EA_INT128_USE_INT64
+				const int nPart = (int)((pEnd - p) / 16);
+				uint64_t c = *(--p); // c is an integer in the range of [0,15].
+			#else
+				const int nPart = (int)((pEnd - p) / 8);
+				uint32_t c = *(--p); // c is an integer in the range of [0,15].
+			#endif
+
+			if(c >= '0' && c <= '9')
+				c = (c - '0');
+			else if(c >= 'a' && c <= 'f')
+				c = 10 + (c - 'a');
+			else
+				c = 10 + (c - 'A');
+
+			if(c)
+			{
+				#if EA_INT128_USE_INT64
+					c <<= ((i % 16) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+				#else
+					c <<= ((i % 8) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+					else if(nPart == 2)
+						value.mPart2 |= c;
+					else if(nPart == 3)
+						value.mPart3 |= c;
+				#endif
+			}
+		}
+	}
+	else
+	{
+		// EA_ASSERT(false); // For the time being, we handle only the above bases. But that's all that's required by the standard.
+	}
+
+	if(chSign == '-')
+		value.Negate();
+
+	if(ppEnd)
+		*ppEnd = (char*)pEnd;
+
+	return value;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StrToInt128
+//
+// Same as C runtime strtol function but for int128_t. 
+// This is probably the most general and useful of the C atoi family of functions.
+//
+int128_t int128_t::StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int nBase)
+{
+	// This is simply a copy and paste of the char version of StrToInt128, with minor
+	// modifications for wchar_t. 
+	// To consider: Make an alternative implementation of this which converts the wchar_t
+	// buffer to char and uses the char version. Doing this properly would involve more
+	// than a trivial number of lines of code, and so for the time being we do the copy/paste.
+
+	int128_t       value((uint32_t)0); // Current value
+	const wchar_t* p = pValue;         // Current position
+	const wchar_t* pBegin = NULL;      // Where the digits start.
+	const wchar_t* pEnd = NULL;        // Where the digits end. One-past the last digit.
+	wchar_t        chSign('+');        // One of either '+' or '-'
+
+	// Skip leading whitespace
+	while((*p > 0) && (*p < 127) && isspace((uint8_t)*p)) // Compare to < 127 because ctype functions will crash for higher values.
+		++p;
+
+	// Check for sign.
+	if((*p == '-') || (*p == '+'))
+		chSign = *p++;
+
+	// Do checks on 'nBase'.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (wchar_t*)pValue;
+		return value;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 2, 8, 10, or 16. 
+		if(*p != '0')
+			nBase = 10;
+		else if((p[1] == 'x') || (p[1] == 'X'))
+			nBase = 16;
+		else if((p[1] == 'b') || (p[1] == 'B'))
+			nBase = 2;
+		else
+			nBase = 8;
+	}
+
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))
+			p += 2;
+	}
+	else if(nBase == 2){
+		// If there is a leading '0b', then skip past it.
+		if((*p == '0') && ((p[1] == 'b') || (p[1] == 'B')))
+			p += 2;
+	}
+
+	// Save the position where the digits start.
+	pBegin = p;
+
+	if(nBase == 2)  // Binary
+	{
+		while((*p == '0') || (*p == '1'))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 128) // There can be at most 128 binary digits in the string.
+		{
+			pEnd = pBegin + 128;
+			p    = pEnd;
+		}
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			--p;
+			if(*p == '1')
+				value.SetBit(i, true);
+		}
+	}
+	else if(nBase == 10) // Decimal
+	{
+		while((*p > 0) && (*p < 127) && isdigit((uint8_t)*p)) // Compare to < 127 because ctype functions will crash for higher values.
+			++p;
+		pEnd = p;
+
+		if(pEnd > pBegin + 39) // With base 10, it is not enough to simply check against 39 digits, 
+		{                      // as you can have 39 '9's and overflow. But 39 is the most you could have.
+			pEnd = pBegin + 39;
+			p    = pEnd;
+		}
+
+		int128_t multiplier((uint32_t)1);
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			const uint32_t c = (uint32_t)(*(--p) - '0');
+
+			if(c)
+			{
+				// This can be optimized for faster speed by doing the smaller orders
+				// of ten on value.mPart0 with an int multiplier instead of on value 
+				// and a int128_t multiplier.
+				value += (multiplier * c);
+			}
+
+			multiplier *= (uint32_t)10;
+		}
+	}
+	else if(nBase == 16) // Hexadecimal
+	{
+		while((*p > 0) && (*p < 127) && isxdigit(*p)) // Compare to < 127 because ctype functions will crash for higher values.
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 32) // There can be at most 32 hexadecimal digits in the string.
+		{
+			pEnd = pBegin + 32;
+			p    = pEnd;
+		}
+
+		// There can be as many as 32 characters.
+		for(int i(0); p > pBegin; i++)
+		{
+			#if EA_INT128_USE_INT64
+				const int nPart = (int)((pEnd - p) / 16);
+				uint64_t c = *(--p); // c is an integer in the range of [0,15].
+			#else
+				const int nPart = (int)((pEnd - p) / 8);
+				uint32_t c = *(--p); // c is an integer in the range of [0,15].
+			#endif
+
+			if(c >= '0' && c <= '9')
+				c = (c - '0');
+			else if(c >= 'a' && c <= 'f')
+				c = 10 + (c - 'a');
+			else
+				c = 10 + (c - 'A');
+
+			if(c)
+			{
+				#if EA_INT128_USE_INT64
+					c <<= ((i % 16) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+				#else
+					c <<= ((i % 8) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+					else if(nPart == 2)
+						value.mPart2 |= c;
+					else if(nPart == 3)
+						value.mPart3 |= c;
+				#endif
+			}
+		}
+	}
+	else
+	{
+		// EA_ASSERT(false); // For the time being, we handle only the above bases. But that's all that's required by the standard.
+	}
+
+	if(chSign == '-')
+		value.Negate();
+
+	if(ppEnd)
+		*ppEnd = (wchar_t*)pEnd;
+
+	return value;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Int128ToStr
+//
+// Returned string has a NULL appended to it.
+// Upon return, ppEnd points to the terminating NULL.
+// Thus, ppEnd - pValue => string length.
+//
+// bPrefix applies only to base 2 (0b) and base 16 (0x).
+//
+void int128_t::Int128ToStr(char* pValue, char** ppEnd, int nBase, LeadingZeroes lz, Prefix prefix) const
+{
+	if(nBase == 2)
+	{
+		bool bLeadingZeros = (lz == kLZEnable);         // By default leading zeroes are disabled.
+		bool bPrefix       = (prefix == kPrefixEnable); // By default prefix is disabled.
+
+		if(bPrefix)
+		{
+			*pValue++ = '0'; 
+			*pValue++ = 'b';
+		}
+
+		if(IsZero())
+		{
+			if(bLeadingZeros)
+			{
+				for(int i(0); i < 128; i++)
+					*pValue++ = '0';
+			}
+			else
+				*pValue++ = '0'; // This is all we need to write.
+		}
+		else
+		{
+			// Print out the text.
+			bool bNonZeroFound(false);
+
+			for(int i(127); i >= 0; --i)
+			{
+				const int bBitIsSet(GetBit(i));
+				if(bBitIsSet)
+					bNonZeroFound = true;
+				if(bLeadingZeros || bNonZeroFound)
+					*pValue++ = (bBitIsSet ? '1' : '0');
+			}
+		}
+	}
+	else if(nBase == 10)
+	{
+		// To do: Support leading zeroes and prefix for base 10.
+
+		if(*this == EASTDC_INT128_MIN)
+		{
+			// This code has a special pathway because negating EASTDC_INT128_MIN results 
+			// in EASTDC_INT128_MIN and thus the code below can't work.
+			static const char* pINT128_MIN = "-170141183460469231731687303715884105728";
+			for(const char* pCurrent = pINT128_MIN; *pCurrent; ++pCurrent, ++pValue)
+				*pValue = *pCurrent;
+		}
+		else
+		{
+			int128_t   value(*this);
+			char*      pValueInitial = pValue;
+			const bool bNegative(IsNegative());
+
+			if(bNegative)
+			{
+				value.Negate();
+				*pValue++ = '-';
+			}
+
+			// This part here isn't particularly fast.
+			const int128_t ten((uint32_t)10);
+
+			while (value >= ten)
+			{
+				const int128_t remainder = (value % ten);
+				*pValue++ = (char)('0' + remainder.mPart0);
+				value /= (uint32_t)10;
+			}
+
+			*pValue++ = (char)('0' + value.mPart0);
+
+			// Reverse the string.
+			char* pEnd = pValue - 1;
+
+			if(bNegative)
+				++pValueInitial;
+
+			while(pValueInitial < pEnd)
+			{
+				char temp      = *pValueInitial;
+				*pValueInitial = *pEnd;
+				*pEnd          = temp;
+				++pValueInitial;
+				--pEnd;
+			}
+		}
+	}
+	else if(nBase == 16)
+	{
+		bool bLeadingZeros = (lz != kLZDisable);         // By default leading zeroes are enabled.
+		bool bPrefix       = (prefix != kPrefixDisable); // By default prefix is enabled.
+
+		static const char* const pHexCharTable = "0123456789abcdef";
+
+		if(bPrefix)
+		{
+			*pValue++ = '0'; 
+			*pValue++ = 'x';
+		}
+
+		if(IsZero())
+		{
+			if(bLeadingZeros)
+			{
+				for(int i(0); i < 32; i++) // 32 is equal to (128 / 16)
+					*pValue++ = '0';
+			}
+			else
+				*pValue++ = '0'; // This is all we need to write.
+		}
+		else
+		{
+			// Print out the text.
+			bool bNonZeroFound(false);
+
+			// Work on each part in turn, starting with the high part.
+			#if EA_INT128_USE_INT64
+				for(int i(1); i >= 0; --i)
+				{
+					const uint64_t* pCurrent;
+
+					if(i == 1)
+						pCurrent = &mPart1;
+					else
+						pCurrent = &mPart0;
+
+					// Work on each sub-part (4 bits) or the current part (64 bits), starting with the high sub-part.
+					for(int j(60); j >= 0; j -= 4)
+					{
+						const char c = pHexCharTable[(*pCurrent >> j) & 0x0F];
+
+						if(c != '0')
+							bNonZeroFound = true;
+						if(bLeadingZeros || bNonZeroFound)
+							*pValue++ = c;
+					}
+				}
+			#else
+				for(int i(3); i >= 0; --i)
+				{
+					const uint32_t* pCurrent;
+
+					if(i == 3)
+						pCurrent = &mPart3;
+					else if(i == 2)
+						pCurrent = &mPart2;
+					else if(i == 1)
+						pCurrent = &mPart1;
+					else
+						pCurrent = &mPart0;
+
+					// Work on each sub-part (4 bits) or the current part (32 bits), starting with the high sub-part.
+					for(int j(28); j >= 0; j -= 4)
+					{
+						const char c = pHexCharTable[(*pCurrent >> j) & 0x0F];
+
+						if(c != '0')
+							bNonZeroFound = true;
+						if(bLeadingZeros || bNonZeroFound)
+							*pValue++ = c;
+					}
+				}
+			#endif
+		}
+	}
+	else
+	{
+		// To do: Implement this in a generic way.
+		EA_FAIL(); // Base not supported.
+	}
+
+	if(ppEnd)
+		*ppEnd = pValue;
+
+	*pValue = 0;
+}
+
+
+void int128_t::Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int nBase, LeadingZeroes lz, Prefix prefix) const
+{
+	char  str8[130];
+	char* pEnd = str8;
+
+	Int128ToStr(str8, &pEnd, nBase, lz, prefix);
+
+	for(char* p = str8; p < pEnd;)
+		*pValue++ = (wchar_t)(uint8_t)*p++;
+
+	if(ppEnd)
+		*ppEnd = pValue;
+
+	*pValue = 0;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// uint128_t
+///////////////////////////////////////////////////////////////////////////////
+
+uint128_t::uint128_t()
+	#if EA_INT128_USE_INT64
+	  : int128_t_base(0, 0)
+	#else
+	  : int128_t_base(0, 0, 0, 0)
+	#endif
+{
+}
+
+
+uint128_t::uint128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3)
+	: int128_t_base(nPart0, nPart1, nPart2, nPart3) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(uint64_t nPart0, uint64_t nPart1)
+	: int128_t_base(nPart0, nPart1) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(uint8_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(uint16_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(uint32_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+#if defined(INT128_UINT_TYPE)
+
+uint128_t::uint128_t(INT128_UINT_TYPE value)
+	: int128_t_base((uint64_t)value) // OK for EA_INT128_USE_INT64
+{
+}
+
+#endif
+
+
+uint128_t::uint128_t(uint64_t value)
+	: int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(int8_t value)
+{
+	if(value < 0)
+	{
+		*this = uint128_t((uint8_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = value;
+		#endif
+	}
+}
+
+uint128_t::uint128_t(int16_t value)
+{
+	if(value < 0)
+	{
+		*this = uint128_t((uint16_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = value;
+		#endif
+	}
+}
+
+uint128_t::uint128_t(int32_t value)
+{
+	if(value < 0)
+	{
+		*this = uint128_t((uint32_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = value;
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = 0;
+			mPart0 = (uint32_t)value;
+		#endif
+	}
+}
+
+
+#if defined(INT128_INT_TYPE)
+
+uint128_t::uint128_t(INT128_INT_TYPE value)
+{
+	operator=(uint128_t((int64_t)value));
+}
+
+#endif
+
+
+uint128_t::uint128_t(int64_t value)
+{
+	if(value < 0)
+	{
+		*this = uint128_t((uint64_t)-value);
+		TwosComplement();
+	}
+	else
+	{
+		#if EA_INT128_USE_INT64
+			mPart1 = 0;
+			mPart0 = (uint64_t) (value);
+		#else
+			mPart3 = 0;
+			mPart2 = 0;
+			mPart1 = (uint32_t) ((value >> 32) & 0xffffffff);
+			mPart0 = (uint32_t) (value & 0xffffffff);
+		#endif
+	}
+}
+
+
+uint128_t::uint128_t(const float value)
+{
+	DoubleToUint128(value); // OK for EA_INT128_USE_INT64
+}
+
+
+uint128_t::uint128_t(const double value)
+{
+	DoubleToUint128(value); // OK for EA_INT128_USE_INT64
+}
+
+
+uint128_t::uint128_t(const int128_t& value)
+	:  int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(const uint128_t& value)
+	:  int128_t_base(value) // OK for EA_INT128_USE_INT64
+{
+}
+
+
+uint128_t::uint128_t(const char* pValue, int nBase){
+	// OK for EA_INT128_USE_INT64
+	const uint128_t value(StrToInt128(pValue, NULL, nBase));
+	operator=(value);
+}
+
+
+uint128_t::uint128_t(const wchar_t* pValue, int nBase){
+	// OK for EA_INT128_USE_INT64
+	wchar_t* pTextEnd(NULL);
+	const uint128_t value(StrToInt128(pValue, &pTextEnd, nBase));
+	operator=(value);
+}
+
+
+uint128_t& uint128_t::operator=(const int128_t_base& value)
+{
+	// C++ requires operator= to be subclassed, even if the subclassed 
+	// implementation is identical to the base implementation.
+	// OK for EA_INT128_USE_INT64
+	int128_t_base::operator=(value);
+	return *this;
+}
+
+
+uint128_t uint128_t::operator-() const
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t returnValue(*this);
+	returnValue.Negate();
+	return returnValue;
+}
+
+
+uint128_t& uint128_t::operator++()
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t_base one((uint32_t)1);
+	operatorPlus(*this, one, *this);
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator--()
+{
+	// OK for EA_INT128_USE_INT64
+	int128_t_base one((uint32_t)1);
+	operatorMinus(*this, one, *this);
+	return *this;
+}
+
+
+uint128_t uint128_t::operator++(int)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp((uint32_t)1);
+	operatorPlus(*this, temp, temp);
+	return temp;
+}
+
+
+uint128_t uint128_t::operator--(int)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp((uint32_t)1);
+	operatorMinus(*this, temp, temp);
+	return temp;
+}
+
+
+uint128_t uint128_t::operator+() const
+{
+	// OK for EA_INT128_USE_INT64
+	return *this;
+}
+
+
+uint128_t uint128_t::operator~() const
+{
+	#if EA_INT128_USE_INT64
+		return uint128_t(~mPart0, ~mPart1);
+	#else
+		return uint128_t(~mPart0, ~mPart1, ~mPart2, ~mPart3);
+	#endif
+}
+
+
+uint128_t operator+(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	uint128_t::operatorPlus(value1, value2, temp);
+	return temp;
+}
+
+
+uint128_t operator-(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	uint128_t::operatorMinus(value1, value2, temp);
+	return temp;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operator *
+//
+uint128_t operator*(const uint128_t& value1, const uint128_t& value2)
+{
+	uint128_t returnValue;
+	int128_t_base::operatorMul(value1, value2, returnValue);
+	return returnValue;
+}
+
+
+uint128_t operator/(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t remainder;
+	uint128_t quotient;
+	value1.Modulus(value2, quotient, remainder);
+	return quotient;
+}
+
+
+uint128_t operator%(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t remainder;
+	uint128_t quotient;
+	value1.Modulus(value2, quotient, remainder);
+	return remainder;
+}
+
+
+uint128_t& uint128_t::operator+=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorPlus(*this, value, *this);
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator-=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorMinus(*this, value, *this);
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator*=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this * value;
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator/=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this / value;
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator%=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	*this = *this % value;
+	return *this;
+}
+
+
+
+// With rightward shifts of negative numbers, shift in zero from the left side.
+uint128_t uint128_t::operator>>(int nShift) const
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	operatorShiftRight(*this, nShift, temp);
+	return temp;
+}
+
+
+// With rightward shifts of negative numbers, shift in zero from the left side.
+uint128_t uint128_t::operator<<(int nShift) const
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	operatorShiftLeft(*this, nShift, temp);
+	return temp;
+}
+
+
+uint128_t& uint128_t::operator>>=(int nShift)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	operatorShiftRight(*this, nShift, temp);
+	*this = temp;
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator<<=(int nShift)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	operatorShiftLeft(*this, nShift, temp);
+	*this = temp;
+	return *this;
+}
+
+
+uint128_t operator^(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	uint128_t::operatorXOR(value1, value2, temp);
+	return temp;
+}
+
+
+uint128_t operator|(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	uint128_t::operatorOR(value1, value2, temp);
+	return temp;
+}
+
+
+uint128_t operator&(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t temp;
+	uint128_t::operatorAND(value1, value2, temp);
+	return temp;
+}
+
+
+uint128_t& uint128_t::operator^=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorXOR(*this, value, *this);
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator|=(const uint128_t& value)
+{
+	// EA_INT128_USE_INT64
+	operatorOR(*this, value, *this);
+	return *this;
+}
+
+
+uint128_t& uint128_t::operator&=(const uint128_t& value)
+{
+	// OK for EA_INT128_USE_INT64
+	operatorAND(*this, value, *this);
+	return *this;
+}
+
+
+// This function forms the basis of all logical comparison functions.
+// If value1 <  value2, the return value is -1.
+// If value1 == value2, the return value is 0.
+// If value1 >  value2, the return value is 1.
+int compare(const uint128_t& value1, const uint128_t& value2)
+{
+	// Compare individual parts. At this point, the two numbers have the same sign.
+	#if EA_INT128_USE_INT64
+		if(value1.mPart1 == value2.mPart1)
+		{
+			if(value1.mPart0 == value2.mPart0)
+				return 0;
+			else if(value1.mPart0 > value2.mPart0)
+				return 1;
+			// return -1; //Just fall through to the end.
+		}
+		else if(value1.mPart1 > value2.mPart1)
+			return 1;
+		return -1;
+	#else
+		if(value1.mPart3 == value2.mPart3)
+		{
+			if(value1.mPart2 == value2.mPart2)
+			{
+				if(value1.mPart1 == value2.mPart1)
+				{
+					if(value1.mPart0 == value2.mPart0)
+						return 0;
+					else if(value1.mPart0 > value2.mPart0)
+						return 1;
+					// return -1; //Just fall through to the end.
+				}
+				else if(value1.mPart1 > value2.mPart1)
+					return 1;
+				// return -1; //Just fall through to the end.
+			}
+			else if(value1.mPart2 > value2.mPart2)
+				return 1;
+			// return -1; //Just fall through to the end.
+		}
+		else if(value1.mPart3 > value2.mPart3)
+			return 1;
+		return -1;
+	#endif
+}
+
+
+bool operator==(const uint128_t& value1, const uint128_t& value2)
+{
+	#if EA_INT128_USE_INT64
+		return (value1.mPart0 == value2.mPart0) && // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 == value2.mPart1);
+	#else
+		return (value1.mPart0 == value2.mPart0) && // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 == value2.mPart1) && 
+			   (value1.mPart2 == value2.mPart2) && 
+			   (value1.mPart3 == value2.mPart3);
+	#endif
+}
+
+
+bool operator!=(const uint128_t& value1, const uint128_t& value2)
+{
+	#if EA_INT128_USE_INT64
+		return (value1.mPart0 != value2.mPart0) ||  // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 != value2.mPart1);
+	#else
+		return (value1.mPart0 != value2.mPart0) ||  // Check mPart0 first as this will likely yield faster execution.
+			   (value1.mPart1 != value2.mPart1) || 
+			   (value1.mPart2 != value2.mPart2) || 
+			   (value1.mPart3 != value2.mPart3);
+	#endif
+}
+
+
+bool operator>(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) > 0);
+}
+
+
+bool operator>=(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) >= 0);
+}
+
+
+bool operator<(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) < 0);
+}
+
+
+bool operator<=(const uint128_t& value1, const uint128_t& value2)
+{
+	// OK for EA_INT128_USE_INT64
+	return (compare(value1, value2) <= 0);
+}
+
+
+int8_t uint128_t::AsInt8() const
+{
+	// OK for EA_INT128_USE_INT64
+	// The C++ Standard, section 4.7, paragraph 3 states that the results of 
+	// conversion of an unsigned type to a signed type that cannot represent 
+	// the unsigned type are implementation-defined.
+	return (int8_t)mPart0;
+}
+
+
+int16_t uint128_t::AsInt16() const
+{
+	// OK for EA_INT128_USE_INT64
+	// The C++ Standard, section 4.7, paragraph 3 states that the results of 
+	// conversion of an unsigned type to a signed type that cannot represent 
+	// the unsigned type are implementation-defined.
+	return (int16_t)mPart0;
+}
+
+
+int32_t uint128_t::AsInt32() const
+{
+	// OK for EA_INT128_USE_INT64
+	// The C++ Standard, section 4.7, paragraph 3 states that the results of 
+	// conversion of an unsigned type to a signed type that cannot represent 
+	// the unsigned type are implementation-defined.
+	return (int32_t)mPart0;
+}
+
+
+int64_t uint128_t::AsInt64() const
+{
+	#if EA_INT128_USE_INT64
+		return (int64_t)mPart0;
+	#else
+		return (((int64_t) mPart1) << 32) + mPart0;
+	#endif
+}
+
+
+// I am not convinced that this is a reliable method of conversion.
+float uint128_t::AsFloat() const
+{
+	float fReturnValue(0);
+
+	#if EA_INT128_USE_INT64
+		if(mPart1)
+			fReturnValue += (mPart1 * 18446744073709551616.f);
+		if(mPart0)
+			fReturnValue +=  (float)mPart0;
+	#else
+		if(mPart3)
+			fReturnValue += (mPart3 * 79228162514264337593543950336.f);
+		if(mPart2)
+			fReturnValue += (mPart2 * 18446744073709551616.f);
+		if(mPart1)
+			fReturnValue += (mPart1 * 4294967296.f);
+		if(mPart0)
+			fReturnValue += (float)mPart0;
+	#endif
+
+	return fReturnValue;
+}
+
+
+// I am not convinced that this is a reliable method of conversion.
+double uint128_t::AsDouble() const
+{
+	double fReturnValue(0);
+
+	#if EA_INT128_USE_INT64
+		if(mPart1)
+			fReturnValue += (mPart1 * 18446744073709551616.0);
+		if(mPart0)
+			fReturnValue +=  (double)mPart0;
+	#else
+		if(mPart3)
+			fReturnValue += (mPart3 * 79228162514264337593543950336.0);
+		if(mPart2)
+			fReturnValue += (mPart2 * 18446744073709551616.0);
+		if(mPart1)
+			fReturnValue += (mPart1 * 4294967296.0);
+		if(mPart0)
+			fReturnValue += (double)mPart0;
+	#endif
+
+	return fReturnValue;
+}
+
+
+void uint128_t::Negate()
+{
+	// OK for EA_INT128_USE_INT64
+	TwosComplement();
+}
+
+
+bool uint128_t::IsNegative() const
+{   // True if value < 0
+	// OK for EA_INT128_USE_INT64
+	return false;
+}
+
+
+bool uint128_t::IsPositive() const
+{
+	// True of value >= 0
+	// OK for EA_INT128_USE_INT64
+	return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Modulus
+//
+// This is a generic function that does both division modulus calculations.
+//
+void uint128_t::Modulus(const uint128_t& divisor, uint128_t& quotient, uint128_t& remainder) const
+{
+	// OK for EA_INT128_USE_INT64
+	uint128_t tempDividend(*this);
+	uint128_t tempDivisor(divisor);
+
+	if(tempDivisor.IsZero())
+	{
+		// Force a divide by zero exception. 
+		// We know that tempDivisor.mPart0 is zero.
+		quotient.mPart0 /= tempDivisor.mPart0;
+	}
+	else if(tempDividend.IsZero())
+	{
+		quotient  = uint128_t((uint32_t)0);
+		remainder = uint128_t((uint32_t)0);
+	}
+	else
+	{
+		remainder.SetZero();
+
+		for(int i(0); i < 128; i++)
+		{
+			remainder += (uint32_t)tempDividend.GetBit(127 - i);
+			const bool bBit(remainder >= tempDivisor);
+			quotient.SetBit(127 - i, bBit);
+
+			if(bBit)
+				remainder -= tempDivisor;
+		 
+			if((i != 127) && !remainder.IsZero())
+				remainder <<= 1;
+		}
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StrToInt128
+//
+// Same as C runtime strtol function but for uint128_t. 
+// This is probably the most general and useful of the C atoi family of functions.
+//
+uint128_t uint128_t::StrToInt128(const char* pValue, char** ppEnd, int nBase)
+{
+	uint128_t   value((uint32_t)0); // Current value
+	const char* p = pValue;         // Current position
+	const char* pBegin = NULL;      // Where the digits start.
+	const char* pEnd = NULL;        // Where the digits end. One-past the last digit.
+	char        chSign('+');        // One of either '+' or '-'
+
+	// Skip leading whitespace
+	while(isspace((unsigned char)*p))
+		++p;
+
+	// Check for sign.
+	if((*p == '-') || (*p == '+'))
+		chSign = *p++;
+
+	// Do checks on 'nBase'.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (char*)pValue;
+		return value;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 2, 8, 10, or 16. 
+		if(*p != '0')
+			nBase = 10;
+		else if((p[1] == 'x') || (p[1] == 'X'))
+			nBase = 16;
+		else if((p[1] == 'b') || (p[1] == 'B'))
+			nBase = 2;
+		else
+			nBase = 8;
+	}
+
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))
+			p += 2;
+	}
+	else if(nBase == 2){
+		// If there is a leading '0b', then skip past it.
+		if((*p == '0') && ((p[1] == 'b') || (p[1] == 'B')))
+			p += 2;
+	}
+
+	// Save the position where the digits start.
+	pBegin = p;
+
+	if(nBase == 2)  // Binary
+	{
+		while((*p == '0') || (*p == '1'))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 128) // There can be at most 128 binary digits in the string.
+		{
+			pEnd = pBegin + 128;
+			p    = pEnd;
+		}
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			--p;
+			if(*p == '1')
+				value.SetBit(i, true);
+		}
+	}
+	else if(nBase == 10) // Decimal
+	{
+		while(isdigit((unsigned char)*p))
+			++p;
+		pEnd = p;
+
+		if(pEnd > pBegin + 39) // With base 10, it is not enough to simply check against 39 digits, 
+		{                      // as you can have 39 '9's and overflow. But 39 is the most you could have.
+			pEnd = pBegin + 39;
+			p    = pEnd;
+		}
+
+		uint128_t multiplier((uint32_t)1);
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			const uint32_t c = *(--p) - (uint32_t)'0';
+
+			if(c)
+			{
+				// This can be optimized for faster speed by doing the smaller orders
+				// of ten on value.mPart0 with an int multiplier instead of on value 
+				// and a uint128_t multiplier.
+				value += (multiplier * c);
+			}
+
+			multiplier *= (uint32_t)10;
+		}
+	}
+	else if(nBase == 16) // Hexadecimal
+	{
+		while(isxdigit((unsigned char)*p))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 32) // There can be at most 32 hexadecimal digits in the string.
+		{
+			pEnd = pBegin + 32;
+			p    = pEnd;
+		}
+
+		// There can be as many as 32 characters.
+		for(int i(0); p > pBegin; i++)
+		{
+			#if EA_INT128_USE_INT64
+				const int nPart = (int)((pEnd - p) / 16);
+				uint64_t c = *(--p);
+			#else
+				const int nPart = (int)((pEnd - p) / 8);
+				uint32_t c = *(--p);
+			#endif
+			
+			if(c >= '0' && c <= '9')
+				c = (c - '0');
+			else if(c >= 'a' && c <= 'f')
+				c = 10 + (c - 'a');
+			else
+				c = 10 + (c - 'A');
+
+			if(c)
+			{
+				#if EA_INT128_USE_INT64
+					c <<= ((i % 16) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+				#else
+					c <<= ((i % 8) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+					else if(nPart == 2)
+						value.mPart2 |= c;
+					else if(nPart == 3)
+						value.mPart3 |= c;
+				#endif
+			}
+		}
+	}
+	else
+	{
+		// EA_ASSERT(false); // For the time being, we handle only the above bases.
+	}
+
+	if(chSign == '-')
+		value.Negate();
+
+	if(ppEnd)
+		*ppEnd = (char*)pEnd;
+
+	return value;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// StrToInt128
+//
+// Same as C runtime strtol function but for uint128_t. 
+// This is probably the most general and useful of the C atoi family of functions.
+//
+uint128_t uint128_t::StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int nBase)
+{
+	// This is simply a copy and paste of the char version of StrToInt128, with minor
+	// modifications for wchar_t. 
+	uint128_t      value((uint32_t)0); // Current value
+	const wchar_t* p = pValue;         // Current position
+	const wchar_t* pBegin = NULL;      // Where the digits start.
+	const wchar_t* pEnd = NULL;        // Where the digits end. One-past the last digit.
+	wchar_t        chSign('+');        // One of either '+' or '-'
+
+	// Skip leading whitespace
+	while((*p > 0) && (*p < 127) && isspace((uint8_t)*p)) // Compare to < 127 because ctype functions will crash for higher values.
+		++p;
+
+	// Check for sign.
+	if((*p == '-') || (*p == '+'))
+		chSign = *p++;
+
+	// Do checks on 'nBase'.
+	if((nBase < 0) || (nBase == 1) || (nBase > 36)){
+		if(ppEnd)
+			*ppEnd = (wchar_t*)pValue;
+		return value;
+	}
+	else if(nBase == 0){
+		// Auto detect one of base 2, 8, 10, or 16. 
+		if(*p != '0')
+			nBase = 10;
+		else if((p[1] == 'x') || (p[1] == 'X'))
+			nBase = 16;
+		else if((p[1] == 'b') || (p[1] == 'B'))
+			nBase = 2;
+		else
+			nBase = 8;
+	}
+
+	if(nBase == 16){
+		// If there is a leading '0x', then skip past it.
+		if((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))
+			p += 2;
+	}
+	else if(nBase == 2){
+		// If there is a leading '0b', then skip past it.
+		if((*p == '0') && ((p[1] == 'b') || (p[1] == 'B')))
+			p += 2;
+	}
+
+	// Save the position where the digits start.
+	pBegin = p;
+
+	if(nBase == 2)  // Binary
+	{
+		while((*p == '0') || (*p == '1'))
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 128) // There can be at most 128 binary digits in the string.
+		{
+			pEnd = pBegin + 128;
+			p    = pEnd;
+		}
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			--p;
+			if(*p == '1')
+				value.SetBit(i, true);
+		}
+	}
+	else if(nBase == 10) // Decimal
+	{
+		while((*p > 0) && (*p < 127) && isdigit((uint8_t)*p)) // Compare to < 127 because ctype functions will crash for higher values.
+			++p;
+		pEnd = p;
+
+		if(pEnd > pBegin + 39) // With base 10, it is not enough to simply check against 39 digits, 
+		{                      // as you can have 39 '9's and overflow. But 39 is the most you could have.
+			pEnd = pBegin + 39;
+			p    = pEnd;
+		}
+
+		uint128_t multiplier((uint32_t)1);
+
+		for(int i(0); p > pBegin; ++i)
+		{
+			const uint32_t c = *(--p) - (uint32_t)'0';
+
+			if(c)
+			{
+				// This can be optimized for faster speed by doing the smaller orders
+				// of ten on value.mPart0 with an int multiplier instead of on value 
+				// and a uint128_t multiplier.
+				value += (multiplier * c);
+			}
+
+			multiplier *= (uint32_t)10;
+		}
+	}
+	else if(nBase == 16) // Hexadecimal
+	{
+		while((*p > 0) && (*p < 127) && isxdigit((uint8_t)*p)) // Compare to < 127 because ctype functions will crash for higher values.
+			p++;
+		pEnd = p;
+
+		if(pEnd > pBegin + 32) // There can be at most 32 hexadecimal digits in the string.
+		{
+			pEnd = pBegin + 32;
+			p    = pEnd;
+		}
+
+		// There can be as many as 32 characters.
+		for(int i(0); p > pBegin; i++)
+		{
+			#if EA_INT128_USE_INT64
+				const int nPart = (int)((pEnd - p) / 16);
+				uint64_t c = *(--p);
+			#else
+				const int nPart = (int)((pEnd - p) / 8);
+				uint32_t c = *(--p);
+			#endif
+			
+			if(c >= '0' && c <= '9')
+				c = (c - '0');
+			else if(c >= 'a' && c <= 'f')
+				c = 10 + (c - 'a');
+			else
+				c = 10 + (c - 'A');
+
+			if(c)
+			{
+				#if EA_INT128_USE_INT64
+					c <<= ((i % 16) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+				#else
+					c <<= ((i % 8) * 4);
+
+					if(nPart == 0)
+						value.mPart0 |= c;
+					else if(nPart == 1)
+						value.mPart1 |= c;
+					else if(nPart == 2)
+						value.mPart2 |= c;
+					else if(nPart == 3)
+						value.mPart3 |= c;
+				#endif
+			}
+		}
+	}
+	else
+	{
+		// EA_ASSERT(false); // For the time being, we handle only the above bases.
+	}
+
+	if(chSign == '-')
+		value.Negate();
+
+	if(ppEnd)
+		*ppEnd = (wchar_t*)pEnd;
+
+	return value;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Int128ToStr
+//
+// Returned string has a NULL appended to it.
+// Upon return, ppEnd points to the terminating NULL.
+// Thus, ppEnd - pValue => string length.
+//
+// bPrefix applies only to base 2 (0b) and base 16 (0x).
+//
+void uint128_t::Int128ToStr(char* pValue, char** ppEnd, int nBase, LeadingZeroes lz, Prefix prefix) const
+{
+	if(nBase == 2)
+	{
+		bool bLeadingZeros = (lz == kLZEnable);         // By default leading zeroes are disabled.
+		bool bPrefix       = (prefix == kPrefixEnable); // By default prefix is disabled.
+
+		if(bPrefix)
+		{
+			*pValue++ = '0'; 
+			*pValue++ = 'b';
+		}
+
+		if(IsZero())
+		{
+			if(bLeadingZeros)
+			{
+				for(int i(0); i < 128; i++)
+					*pValue++ = '0';
+			}
+			else
+				*pValue++ = '0'; // This is all we need to write.
+		}
+		else
+		{
+			// Print out the text.
+			bool bNonZeroFound(false);
+
+			for(int i(127); i >= 0; --i)
+			{
+				const int bBitIsSet(GetBit(i));
+				if(bBitIsSet)
+					bNonZeroFound = true;
+				if(bLeadingZeros || bNonZeroFound)
+					*pValue++ = (bBitIsSet ? '1' : '0');
+			}
+		}
+	}
+	else if(nBase == 10)
+	{
+		// To do: Support leading zeroes and prefix for base 10.
+
+		uint128_t  value(*this);
+		char*      pValueInitial = pValue;
+
+		// This part here isn't particularly fast.
+		const uint128_t ten((uint32_t)10);
+
+		while (value >= ten)
+		{
+			const uint128_t remainder = (value % ten);
+			*pValue++ = (char)('0' + remainder.mPart0);
+			value /= (uint32_t)10;
+		}
+
+		*pValue++ = (char)('0' + value.mPart0);
+
+		// Reverse the string.
+		char* pEnd = pValue - 1;
+
+		while(pValueInitial < pEnd)
+		{
+			char temp      = *pValueInitial;
+			*pValueInitial = *pEnd;
+			*pEnd          = temp;
+			++pValueInitial;
+			--pEnd;
+		}
+	}
+	else if(nBase == 16)
+	{
+		bool bLeadingZeros = (lz != kLZDisable);         // By default leading zeroes are enabled.
+		bool bPrefix       = (prefix != kPrefixDisable); // By default prefix is enabled.
+
+		static const char* const pHexCharTable = "0123456789abcdef";
+
+		if(bPrefix)
+		{
+			*pValue++ = '0'; 
+			*pValue++ = 'x';
+		}
+
+		if(IsZero())
+		{
+			if(bLeadingZeros)
+			{
+				for(int i(0); i < 32; i++) // 32 is equal to (128 / 16)
+					*pValue++ = '0';
+			}
+			else
+				*pValue++ = '0'; // This is all we need to write.
+		}
+		else
+		{
+			// Print out the text.
+			bool bNonZeroFound(false);
+
+			// Work on each part in turn, starting with the high part.
+			#if EA_INT128_USE_INT64
+				for(int i(1); i >= 0; --i)
+				{
+					const uint64_t* pCurrent;
+
+					if(i == 1)
+						pCurrent = &mPart1;
+					else
+						pCurrent = &mPart0;
+
+					// Work on each sub-part (4 bits) or the current part (64 bits), starting with the high sub-part.
+					for(int j(60); j >= 0; j -= 4)
+					{
+						const char c = pHexCharTable[(*pCurrent >> j) & 0x0F];
+
+						if(c != '0')
+							bNonZeroFound = true;
+						if(bLeadingZeros || bNonZeroFound)
+							*pValue++ = c;
+					}
+				}
+			#else
+				for(int i(3); i >= 0; --i)
+				{
+					const uint32_t* pCurrent;
+
+					if(i == 3)
+						pCurrent = &mPart3;
+					else if(i == 2)
+						pCurrent = &mPart2;
+					else if(i == 1)
+						pCurrent = &mPart1;
+					else
+						pCurrent = &mPart0;
+
+					// Work on each sub-part (4 bits) or the current part (32 bits), starting with the high sub-part.
+					for(int j(28); j >= 0; j -= 4)
+					{
+						const char c = pHexCharTable[(*pCurrent >> j) & 0x0F];
+
+						if(c != '0')
+							bNonZeroFound = true;
+						if(bLeadingZeros || bNonZeroFound)
+							*pValue++ = c;
+					}
+				}
+			#endif
+		}
+	}
+	else
+	{
+		// To do: Implement this in a generic way.
+		EA_FAIL(); // Base not supported.
+	}
+
+	if(ppEnd)
+		*ppEnd = pValue;
+
+	*pValue++ = 0;
+}
+
+
+void uint128_t::Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int nBase, LeadingZeroes lz, Prefix prefix) const
+{
+	char  str8[130];
+	char* pEnd = str8;
+
+	Int128ToStr(str8, &pEnd, nBase, lz, prefix);
+
+	for(char* p = str8; p < pEnd;)
+		*pValue++ = (wchar_t)(uint8_t)*p++;
+
+	if(ppEnd)
+		*ppEnd = pValue;
+
+	*pValue = 0;
+}
+
+
+} // namespace StdC
+} // namespace EA
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+
+
+
+
+
+
+

+ 200 - 0
test/include/EAStdCTest/EAStdCTest.h

@@ -0,0 +1,200 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTDC_EASTDCTEST_H
+#define EASTDC_EASTDCTEST_H
+
+
+EA_DISABLE_ALL_VC_WARNINGS()
+EA_DISABLE_VC_WARNING(4530) // C++ exception handler used, but unwind semantics are not enabled.
+#include <stddef.h>
+EA_RESTORE_VC_WARNING()
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+int TestCallback();
+int TestString();
+int TestCType();
+int TestBitTricks();
+int TestAlignment();
+int TestByteCrackers();
+int TestDateTime();
+int TestEndian();
+int TestFixedPoint();
+int TestRandom();
+int TestHash();
+int TestGlobal();
+int TestMathHelp();
+int TestMemory();
+int TestProcess();
+int TestScanf();
+int TestSingleton();
+int TestSprintf();
+int TestStopwatch();
+int TestTextUtil();
+int TestInt128();
+
+extern volatile int gWriteToEnsureFunctionCalled;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_WCHAR / EA_CHAR16 / EA_CHAR32
+//
+// We define these in the case that EABase doesn't define them so that our 
+// tests can exercize them. For example, on Windows EABase doesn't define 
+// EA_CHAR32, as it's impossible to do so in an efficient way and would be
+// bad for user code. But for our unit tests we don't care about this and 
+// it's useful to have it.
+///////////////////////////////////////////////////////////////////////////////
+
+// Always defind as L""
+// The purpose of this in our unit tests is to make it explicit in 
+// the test code that we really mean L""/L'' and not EA_CHAR16 or EA_CHAR32.
+#if !defined(EA_WCHAR)
+	#define EA_WCHAR(s) L ## s
+#endif
+
+
+#if !defined(EA_CHAR16)
+
+	#include <stdlib.h>
+	#include <string.h>
+	#include <EASTL/map.h>
+	#include <EAStdC/EAString.h>
+
+	// This class has no purpose other than to convert 32 bit wchar_t strings to 16 bit char16_t strings.
+	// The code below probably wouldn't compile or make sense unless wchar_t was 32 bits or more.
+	class ea_char16_str
+	{
+	public:
+		ea_char16_str() : mpData(NULL), mLength(0) {}
+		
+		ea_char16_str(const wchar_t* pText) 
+		{
+			mLength = EA::StdC::Strlen(pText);
+			mpData  = (char16_t*)malloc((mLength + 1) * sizeof(char16_t));
+			for(size_t i = 0; i < (mLength + 1); ++i)
+				mpData[i] = (char16_t)pText[i];
+		}
+		
+		ea_char16_str(const ea_char16_str& str)
+		{
+			CopyFrom(str);
+		}
+
+		ea_char16_str& operator=(const ea_char16_str& str)
+		{
+			if(&str != this)
+			{
+				if(mpData)
+					free(mpData); 
+				CopyFrom(str);
+			}
+			return *this;
+		}
+
+		void CopyFrom(const ea_char16_str& str)
+		{
+			mLength = str.mLength;
+			mpData  = (char16_t*)malloc((mLength + 1) * sizeof(char16_t));
+			for(size_t i = 0; i < (mLength + 1); ++i)
+				mpData[i] = str.mpData[i];
+		}
+
+		~ea_char16_str()
+		{
+			if(mpData)
+				free(mpData); 
+		}
+
+		char16_t* mpData;
+		size_t    mLength;
+	};
+
+	char16_t* char16_convert(const wchar_t* pText);
+
+	char16_t char16_convert(wchar_t c);
+
+	#define EA_CHAR16(s) char16_convert(L ## s)
+	#define EASTDCTEST_DEFINES_CHAR16
+#endif
+
+
+#if !defined(EA_CHAR32)
+
+	#include <stdlib.h>
+	#include <string.h>
+	#include <EASTL/map.h>
+	#include <EAStdC/EAString.h>
+
+	// This class has no purpose other than to convert 16 bit wchar_t strings to 32 bit char32_t strings.
+	// The code below probably wouldn't compile or make sense unless wchar_t was 16 bits or less.
+	class ea_char32_str
+	{
+	public:
+		ea_char32_str() : mpData(NULL), mLength(0) {}
+		
+		ea_char32_str(const wchar_t* pText) 
+		{
+			mLength = EA::StdC::Strlen(pText);
+			mpData  = (char32_t*)malloc((mLength + 1) * sizeof(char32_t));
+			if(mpData)
+			{
+				for(size_t i = 0; i < (mLength + 1); ++i)
+					mpData[i] = (char32_t)pText[i];
+			}
+		}
+		
+		ea_char32_str(const ea_char32_str& str)
+		{
+			CopyFrom(str);
+		}
+
+		ea_char32_str& operator=(const ea_char32_str& str)
+		{
+			if(&str != this)
+			{
+				if(mpData)
+					free(mpData); 
+				CopyFrom(str);
+			}
+			return *this;
+		}
+
+		void CopyFrom(const ea_char32_str& str)
+		{
+			mLength = str.mLength;
+			mpData  = (char32_t*)malloc((mLength + 1) * sizeof(char32_t));
+			if(mpData)
+			{
+				for(size_t i = 0; i < (mLength + 1); ++i)
+					mpData[i] = str.mpData[i];
+			}
+		}
+
+		~ea_char32_str()
+		{
+			if(mpData)
+				free(mpData); 
+		}
+
+		char32_t* mpData;
+		size_t    mLength;
+	};
+
+	char32_t* char32_convert(const wchar_t* pText);
+
+	char32_t char32_convert(wchar_t character);
+
+	#define EA_CHAR32(s) char32_convert(L ## s)
+	#define EASTDCTEST_DEFINES_CHAR32
+
+#endif
+
+
+#endif // Header include guard
+
+
+

+ 206 - 0
test/source/EAStdCTest.cpp

@@ -0,0 +1,206 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#if defined(_MSC_VER)
+	#pragma warning (disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
+#endif
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAStdC.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/version.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EATest/EAMissingImpl.inl>
+#include <EAMain/EAEntryPointMain.inl>
+#include <new>
+
+#if defined(EASTDCTEST_DEFINES_CHAR16)
+	char16_t* char16_convert(const wchar_t* pText)
+	{
+		typedef eastl::map<const wchar_t*, ea_char16_str> WChar16Map; // Map of wchar_t* to char16_t* 
+
+		static WChar16Map sTable; // Not thread safe.
+		ea_char16_str s = ea_char16_str(pText);
+		eastl::pair<WChar16Map::iterator, bool> result = sTable.insert(eastl::make_pair(pText, s));
+		return result.first->second.mpData;
+	}
+
+	char16_t char16_convert(wchar_t c)
+	{
+		return (char16_t)c;
+	}
+#endif
+
+#if defined(EASTDCTEST_DEFINES_CHAR32)
+	char32_t* char32_convert(const wchar_t* pText)
+	{
+		typedef eastl::map<const wchar_t*, ea_char32_str> WChar32Map; // Map of wchar_t* to char32_t* 
+
+		static WChar32Map sTable; // Not thread safe.
+		ea_char32_str s = ea_char32_str(pText);
+		eastl::pair<WChar32Map::iterator, bool> result = sTable.insert(eastl::make_pair(pText, s));
+		return result.first->second.mpData;
+	}
+	char32_t char32_convert(wchar_t character)
+	{
+		return (char32_t)character;
+	}
+#endif
+
+
+// EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION test
+//
+// It might be useful to test this macro under some circumstances, but doing 
+// so (intentionally) causes the CPU cycle-based timing to not work. So we'd 
+// have to disable those tests if we want to test this macro.
+//
+//#if defined(_MSC_VER)
+//    #pragma warning(disable: 4074)
+//    #pragma init_seg(compiler)
+//#endif
+//
+//EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// operator new
+//
+// EASTL requires some of the following new operators to be defined, while 
+// various platform SDKs require others.
+///////////////////////////////////////////////////////////////////////////////
+
+void* operator new(size_t size, const char*, int, unsigned, const char*, int)
+{
+	return ::operator new(size);
+}
+
+void* operator new[](size_t size, const char*, int, unsigned, const char*, int)
+{
+	return ::operator new[](size);
+}
+
+void* operator new[](size_t size, size_t, size_t, const char*, int, unsigned, const char*, int)
+{
+	return ::operator new[](size);
+}
+
+void* operator new(size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char* /*name*/, 
+					int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+	// We will have a problem if alignment is non-default.
+	return ::operator new(size);
+}
+
+
+void operator delete(void* p, size_t, size_t, const char*, int, unsigned, const char*, int)
+{
+	if (p)
+		::operator delete(p);
+}
+
+void operator delete(void* p, char const*, int, unsigned, char const*, int)
+{
+	if (p)
+		::operator delete(p);
+}
+
+
+#if !defined(EA_STDCTEST_ENABLE_NEW_REPLACEMENT)
+	#define EA_STDCTEST_ENABLE_NEW_REPLACEMENT 0
+#endif
+
+#if EA_STDCTEST_ENABLE_NEW_REPLACEMENT
+
+	void* operator new (size_t size, const ::std::nothrow_t&) EA_THROW_SPEC_NEW_NONE()
+	{
+		return malloc(size);
+	}
+
+	void* operator new[] (size_t size, const ::std::nothrow_t&) EA_THROW_SPEC_NEW_NONE()
+	{
+		return malloc(size);
+	}
+
+	void* operator new[] (size_t size) EA_THROW_SPEC_NEW(std::bad_alloc)
+	{
+		return malloc(size);
+	}
+
+	void* operator new (size_t size) EA_THROW_SPEC_NEW(std::bad_alloc)
+	{
+		return malloc(size);
+	}
+
+	void operator delete (void* p) EA_THROW_SPEC_DELETE_NONE()
+	{
+		if (p)
+			free(p);
+	}
+
+	void operator delete[] (void* p) EA_THROW_SPEC_DELETE_NONE()
+	{
+		if (p)
+			free(p);
+	}
+
+#endif
+
+
+// On some Sony platforms, you must explicitly set the LibC heap size. Because the EAStdC 
+// tests use direct calls to the LibC memory functions, we cannot get around this. 
+// This sets the LibC heap size to 128MB.
+#if defined(EA_PLATFORM_SONY) 
+	size_t sceLibcHeapSize = 128*1024*1024;
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMain
+//
+
+int EAMain(int argc, char** argv)
+{
+	using namespace EA::UnitTest;
+
+	EA::EAMain::PlatformStartup();
+	EA::StdC::Init();
+
+	// Add the tests
+	TestApplication testSuite("EAStdC Unit Tests", argc, argv);
+
+	testSuite.AddTest("Callback",     TestCallback);
+	testSuite.AddTest("String",       TestString);
+	testSuite.AddTest("Sprintf",      TestSprintf);
+	testSuite.AddTest("Random",       TestRandom);
+	testSuite.AddTest("TextUtil",     TestTextUtil);
+	testSuite.AddTest("Scanf",        TestScanf);
+	testSuite.AddTest("Memory",       TestMemory);
+	testSuite.AddTest("Endian",       TestEndian);
+	testSuite.AddTest("DateTime",     TestDateTime);
+	testSuite.AddTest("Singleton",    TestSingleton);
+	testSuite.AddTest("Process",      TestProcess);
+	testSuite.AddTest("Stopwatch",    TestStopwatch);
+	testSuite.AddTest("CType",        TestCType);
+	testSuite.AddTest("BitTricks",    TestBitTricks);
+	testSuite.AddTest("Alignment",    TestAlignment);
+	testSuite.AddTest("ByteCrackers", TestByteCrackers);
+	testSuite.AddTest("FixedPoint",   TestFixedPoint);
+	testSuite.AddTest("Hash",         TestHash);
+	testSuite.AddTest("Global",       TestGlobal);
+	testSuite.AddTest("MathHelp",     TestMathHelp);
+	testSuite.AddTest("Int128",       TestInt128);
+
+	// Parse command line arguments
+	const int testResult = testSuite.Run();
+
+	EA::StdC::Shutdown();
+	EA::EAMain::PlatformShutdown(testResult);
+ 
+	return testResult;
+}
+

+ 490 - 0
test/source/TestAlignment.cpp

@@ -0,0 +1,490 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAAlignment.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+// Conditionally include SSE headers which some of the tests conditionally rely upon.
+#if EA_SSE
+	#if EA_SSE >= 1
+		#include <xmmintrin.h>
+	#endif
+	#if EA_SSE >= 2
+		#include <emmintrin.h>
+	#endif
+
+	#if EA_SSE >= 1
+		EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128)
+		EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128i)
+	#endif
+#endif
+
+
+using namespace EA::StdC;
+using namespace EA::UnitTest;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestStruct
+//
+// Helper class for our unit test.
+//
+struct TestStruct
+{
+	uint32_t a;
+	uint64_t b;
+	char     c;
+	void*    d;
+
+	TestStruct() : a(0), b(0), c(0), d(0) {}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestTemplate
+//
+// Helper class for our unit test.
+//
+template <typename T>
+struct TestTemplate
+{
+	uint32_t a;
+	uint64_t b;
+	char     c;
+	void*    d;
+
+	TestTemplate() : a(0), b(0), c(0), d(0) {}
+};
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestAlignment
+//
+int TestAlignment()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestAlignment\n");
+
+	///////////////////////////////////////////////////////////////////////////
+	// EAAlignOf
+	///////////////////////////////////////////////////////////////////////////
+	{
+		size_t a;
+
+		// Type-based AlignOf
+		a = EAAlignOf(uint32_t);
+		EATEST_VERIFY(a == sizeof(uint32_t));
+
+		#if (EA_PLATFORM_WORD_SIZE >= 8)
+			a = EAAlignOf(uint64_t);
+			EATEST_VERIFY(a == sizeof(uint64_t));
+		#endif
+
+		a = EAAlignOf(TestStruct);
+		EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true.
+
+		a = EAAlignOf(TestTemplate<int>);
+		EATEST_VERIFY(((a * 2) / 2) ==  a); // This should always be true.
+
+
+		// Instance-based AlignOf
+		uint32_t x32 = 0;
+		a = AlignOf(x32);
+		EATEST_VERIFY(a == sizeof(uint32_t));
+
+		#if (EA_PLATFORM_WORD_SIZE >= 8)
+			uint64_t x64 = 0;
+			a = AlignOf(x64);
+			EATEST_VERIFY(a == sizeof(uint64_t));
+		#endif
+
+		TestStruct testStruct;
+		a = AlignOf(testStruct);
+		EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true.
+
+		TestTemplate<int> testTemplate;
+		a = AlignOf(testTemplate);
+		EATEST_VERIFY(((a * 2) / 2) == a); // This should always be true.
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignAddressUp, AlignObjectUp, AlignAddressDown, AlignObjectDown, GetAlignment
+	///////////////////////////////////////////////////////////////////////////
+	{
+		void*  pResult;
+
+		for(char* p = 0; p < (char*)100000; p++)
+		{
+			for(size_t i = 0; i < 18; i++)
+			{
+				uintptr_t a = ((size_t)1 << i);
+
+				pResult = AlignAddressUp(p, a);
+				EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0);
+
+				pResult = AlignObjectUp(p, a);
+				EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0);
+
+				pResult = AlignAddressDown(p, ((size_t)1 << i));
+				EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0);
+
+				pResult = AlignObjectDown(p, ((size_t)1 << i));
+				EATEST_VERIFY(((uintptr_t)pResult % a) == (uintptr_t)0);
+
+				size_t alignment = GetAlignment(pResult);
+				EATEST_VERIFY(alignment >= a); // We test >= because sometimes values are coincidentally aligned to a higher value than intended. Which is OK.
+
+				bool bResult = IsAligned(pResult, a);
+				EATEST_VERIFY(bResult);
+			}
+		}
+
+		for(TestStruct* p = 0, *pTestStruct; p < (TestStruct*)100000; p++)
+		{
+			for(size_t i = 0; i < 18; i++)
+			{
+				uintptr_t a = ((size_t)1 << i);
+
+				pTestStruct = AlignObjectUp(p, a);
+				EATEST_VERIFY(((uintptr_t)pTestStruct % a) == (uintptr_t)0);
+
+				pTestStruct = AlignObjectDown(p, ((size_t)1 << i));
+				EATEST_VERIFY(((uintptr_t)pTestStruct % a) == (uintptr_t)0);
+
+				size_t alignment = GetAlignment(pTestStruct);
+				EATEST_VERIFY(alignment >= a); // We test >= because sometimes values are coincidentally aligned to a higher value than intended. Which is OK.
+
+				bool bResult = IsAligned(pTestStruct, a);
+				EATEST_VERIFY(bResult);
+			}
+		}
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignAddressUp
+	///////////////////////////////////////////////////////////////////////////
+	{
+		for(uintptr_t address = 0; address < 1024; ++address)
+		{
+			for(size_t a = 1; a < 1024*1024; a <<= 1)
+			{
+				const void* up   = AlignAddressUp  ((void*)address, a);
+				const void* down = AlignAddressDown((void*)address, a);
+
+				EATEST_VERIFY(((uintptr_t)up   >= address) && IsAligned(up,   a));
+				EATEST_VERIFY(((uintptr_t)down <= address) && IsAligned(down, a));
+			}
+		}
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignUp, AlignDown
+	///////////////////////////////////////////////////////////////////////////
+	{
+		for(uint8_t u8 = 0; u8 < 150; ++u8)
+		{
+			for(size_t a = 1; a < 64; a <<= 1)
+			{
+				uint8_t n = AlignUp(u8, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n >= u8));
+
+				n = AlignDown(u8, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n <= u8));
+			}
+		}
+
+		for(int16_t i16 = 0; i16 < 10000; ++i16)
+		{
+			for(size_t a = 1; a < 1024; a <<= 1)
+			{
+				int16_t n = AlignUp(i16, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n >= i16));
+
+				n = AlignDown(i16, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n <= i16));
+			}
+		}
+
+		for(uint32_t u32 = 0; u32 < 10000; ++u32)
+		{
+			for(size_t a = 1; a < 1024; a <<= 1)
+			{
+				uint32_t n = AlignUp(u32, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n >= u32));
+
+				n = AlignDown(u32, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n <= u32));
+			}
+		}
+
+		for(int64_t i64 = 0; i64 < 10000; ++i64)
+		{
+			for(size_t a = 1; a < 1024; a <<= 1)
+			{
+				int64_t n = AlignUp(i64, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n >= i64));
+
+				n = AlignDown(i64, a);
+				EATEST_VERIFY(IsAligned(n, a) && (n <= i64));
+			}
+		}
+
+		for(char* p8 = 0; p8 < (char*)(uintptr_t)1000; ++p8)
+		{
+			for(size_t a = 1; a < 64; a <<= 1)
+			{
+				TestStruct* pTestStruct = (TestStruct*)(uintptr_t)p8;
+				pTestStruct = AlignUp(pTestStruct, a);
+				EATEST_VERIFY(IsAligned(pTestStruct, a) && ((uintptr_t)pTestStruct >= (uintptr_t)p8));
+
+				pTestStruct = (TestStruct*)(uintptr_t)p8;
+				pTestStruct = AlignDown(pTestStruct, a);
+				EATEST_VERIFY(IsAligned(pTestStruct, a) && ((uintptr_t)pTestStruct <= (uintptr_t)p8));
+			}
+		}
+
+		for(uint64_t x = 0; x < 1024; ++x)
+		{
+			for(size_t a = 1; a < 1024*1024; a <<= 1)
+			{
+				const uint64_t up   = AlignUp(x, a);
+				const uint64_t down = AlignDown(x, a);
+
+				EATEST_VERIFY((up   >= x) && IsAligned(up, a));
+				EATEST_VERIFY((down <= x) && IsAligned(up, a));
+
+				const uint64_t up1   = AlignUp  <uint64_t, 1>(x);
+				const uint64_t down1 = AlignDown<uint64_t, 1>(x);
+
+				EATEST_VERIFY((up1   >= x) && IsAligned(up1, 1));
+				EATEST_VERIFY((down1 <= x) && IsAligned(up1, 1));
+
+				const uint64_t up2   = AlignUp  <uint64_t, 2>(x);
+				const uint64_t down2 = AlignDown<uint64_t, 2>(x);
+
+				EATEST_VERIFY((up2   >= x) && IsAligned(up2, 2));
+				EATEST_VERIFY((down2 <= x) && IsAligned(up2, 2));
+
+				const uint64_t up4   = AlignUp  <uint64_t, 4>(x);
+				const uint64_t down4 = AlignDown<uint64_t, 4>(x);
+
+				EATEST_VERIFY((up4   >= x) && IsAligned(up4, 4));
+				EATEST_VERIFY((down4 <= x) && IsAligned(up4, 4));
+
+				const uint64_t up8   = AlignUp  <uint64_t, 8>(x);
+				const uint64_t down8 = AlignDown<uint64_t, 8>(x);
+
+				EATEST_VERIFY((up8   >= x) && IsAligned(up8, 8));
+				EATEST_VERIFY((down8 <= x) && IsAligned(up8, 8));
+
+				const uint64_t up16   = AlignUp  <uint64_t, 16>(x);
+				const uint64_t down16 = AlignDown<uint64_t, 16>(x);
+
+				EATEST_VERIFY((up16   >= x) && IsAligned(up16, 16));
+				EATEST_VERIFY((down16 <= x) && IsAligned(up16, 16));
+			}
+		}
+
+		// Try again with address values around INT32_MAX
+		for(uint64_t x = 0x7ffffff0; x < 0x800000ff; ++x)
+		{
+			for(size_t a = 1; a < 1024*1024; a <<= 1)
+			{
+				const uint64_t up   = AlignUp(x, a);
+				const uint64_t down = AlignDown(x, a);
+
+				EATEST_VERIFY((up   >= x) && IsAligned(up, a));
+				EATEST_VERIFY((down <= x) && IsAligned(up, a));
+
+				const uint64_t up1   = AlignUp  <uint64_t, 1>(x);
+				const uint64_t down1 = AlignDown<uint64_t, 1>(x);
+
+				EATEST_VERIFY((up1   >= x) && IsAligned(up1, 1));
+				EATEST_VERIFY((down1 <= x) && IsAligned(up1, 1));
+
+				const uint64_t up2   = AlignUp  <uint64_t, 2>(x);
+				const uint64_t down2 = AlignDown<uint64_t, 2>(x);
+
+				EATEST_VERIFY((up2   >= x) && IsAligned(up2, 2));
+				EATEST_VERIFY((down2 <= x) && IsAligned(up2, 2));
+
+				const uint64_t up4   = AlignUp  <uint64_t, 4>(x);
+				const uint64_t down4 = AlignDown<uint64_t, 4>(x);
+
+				EATEST_VERIFY((up4   >= x) && IsAligned(up4, 4));
+				EATEST_VERIFY((down4 <= x) && IsAligned(up4, 4));
+
+				const uint64_t up8   = AlignUp  <uint64_t, 8>(x);
+				const uint64_t down8 = AlignDown<uint64_t, 8>(x);
+
+				EATEST_VERIFY((up8   >= x) && IsAligned(up8, 8));
+				EATEST_VERIFY((down8 <= x) && IsAligned(up8, 8));
+
+				const uint64_t up16   = AlignUp  <uint64_t, 16>(x);
+				const uint64_t down16 = AlignDown<uint64_t, 16>(x);
+
+				EATEST_VERIFY((up16   >= x) && IsAligned(up16, 16));
+				EATEST_VERIFY((down16 <= x) && IsAligned(up16, 16));
+			}
+		}
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// IsAligned
+	///////////////////////////////////////////////////////////////////////////
+	{
+		// template <typename T, int a>
+		// inline bool IsAligned(T x)
+		bool b = IsAligned<int, 8>(64);
+		EATEST_VERIFY(b);
+		b = IsAligned<int, 8>(67);
+		EATEST_VERIFY(!b);
+
+		// template <typename T, int a>
+		// inline bool IsAligned(T* p)
+		b = IsAligned<int, 64>((int*)NULL + 0);
+		EATEST_VERIFY(b);
+		b = IsAligned<int, 64>((int*)NULL + 1);
+		EATEST_VERIFY(!b);
+
+		// template <typename T>
+		// inline bool IsAligned(T x, size_t a)
+		EATEST_VERIFY( IsAligned(64, 8));
+		EATEST_VERIFY(!IsAligned(67, 8));
+
+
+		// template <typename T>
+		// inline bool IsAligned(T* p, size_t a)
+		EATEST_VERIFY( IsAligned((int*)NULL + 0, 64));
+		EATEST_VERIFY(!IsAligned((int*)NULL + 1, 64));
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignedType
+	///////////////////////////////////////////////////////////////////////////
+	{
+
+		// Disabled until we can find a way that works for all compilers.
+		#if 0 // !defined(__GNUC__) || (__GNUC__ < 4) // GCC4 lost the ability to do this
+			const size_t kAlignment = 128;
+
+			AlignedType<int, kAlignment>::Type intAlignedAt128;
+
+			EATEST_VERIFY(((uintptr_t)&intAlignedAt128) % kAlignment == (uintptr_t)0);
+
+			intAlignedAt128 = 12345;
+
+			EATEST_VERIFY(intAlignedAt128 == 12345);
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignedArray
+	///////////////////////////////////////////////////////////////////////////
+	{
+		const size_t kSize = 10;
+		const size_t kAlignment = 64;
+
+		{
+			AlignedArray<TestStruct, kSize, kAlignment> mTestStructArray;
+			
+			// Note that as of this writing the convention is that only the first element 
+			// of the array should be aligned. Other elements should follow the first as
+			// they naturally would otherwise.
+			EATEST_VERIFY(((uintptr_t)&mTestStructArray[0]) % kAlignment == (uintptr_t)0);
+
+			mTestStructArray[0] = mTestStructArray[1];
+
+			AlignedArray<TestStruct, kSize, kAlignment> mTestStructArray2(mTestStructArray);
+
+			mTestStructArray = mTestStructArray2;
+		}
+
+		// These tests instantiate SSE types to ensure they are supported by the AlignedArray.  Previously,
+		// these tests didn't work on Clang.
+		#if defined EA_SSE && (EA_SSE >= 1)
+			{
+				AlignedArray<__m128, kSize, kAlignment> mTestVectorArray;
+				
+				// Note that as of this writing the convention is that only the first element 
+				// of the array should be aligned. Other elements should follow the first as
+				// they naturally would otherwise.
+				EATEST_VERIFY(((uintptr_t)&mTestVectorArray[0]) % kAlignment == (uintptr_t)0);
+
+				mTestVectorArray[0] = mTestVectorArray[1];
+			}
+		#endif
+
+		#if defined EA_SSE && (EA_SSE >= 2)
+			{
+				AlignedArray<__m128i, kSize, kAlignment> mTestVectorArray;
+				
+				// Note that as of this writing the convention is that only the first element 
+				// of the array should be aligned. Other elements should follow the first as
+				// they naturally would otherwise.
+				EATEST_VERIFY(((uintptr_t)&mTestVectorArray[0]) % kAlignment == (uintptr_t)0);
+
+				mTestVectorArray[0] = mTestVectorArray[1];
+			}
+		#endif
+	}
+
+
+	///////////////////////////////////////////////////////////////////////////
+	// AlignedObject
+	///////////////////////////////////////////////////////////////////////////
+	{
+		const size_t kAlignment = 64;
+		AlignedObject<TestStruct, kAlignment> mTestStruct;
+		
+		EATEST_VERIFY((uintptr_t)&mTestStruct % kAlignment == (uintptr_t)0);
+
+		mTestStruct->a = 1;
+		EATEST_VERIFY(mTestStruct->a == (unsigned)1);
+
+		// (*(&mTestStruct))->b = 2; This is not possible; we need to use '.'
+		(*(&mTestStruct)).b = 2;
+		EATEST_VERIFY(mTestStruct->b == (unsigned)2);
+
+		AlignedObject<TestStruct, kAlignment> mTestStruct2(mTestStruct);
+
+		mTestStruct = mTestStruct2;
+
+		// Todo: Test the following
+		// T* operator &()
+		// T* Get()
+		// const T* Get() const
+		// operator T&()
+		// operator const T&() const
+		// T* operator->()
+		// const T* operator->() const
+		// operator T*()
+		// operator const T*() const
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+

+ 1309 - 0
test/source/TestBitTricks.cpp

@@ -0,0 +1,1309 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EABitTricks.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EAAssert/eaassert.h>
+
+
+struct OffsetofTest
+{
+	uint32_t x0;
+	uint32_t x1;
+};
+
+struct OffsetofTest1
+{
+	uint32_t x[2];
+};
+
+
+struct A{ int a; };
+struct B{ int b; };
+struct C : public A, public B{ int c; };
+
+
+int TestBitTricks()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestBitTricks\n");
+
+	int       n1 = 0, n2 = 0;
+	bool       b = false;
+	int        i = 0;
+	unsigned   u = 0;
+	int8_t    i8 = 0;
+	uint8_t   u8 = 0;
+	int16_t  i16 = 0;
+	uint16_t u16 = 0;
+	int32_t  i32 = 0;
+	uint32_t u32 = 0;
+	int64_t  i64 = 0;
+	uint64_t u64 = 0;
+
+	// TurnOffLowestBit
+	i16 = TurnOffLowestBit((int16_t)0x0005);    // 000000101
+	EATEST_VERIFY(i16 == 0x0004);               // 000000100
+
+	u16 = TurnOffLowestBit((uint16_t)0x0005);
+	EATEST_VERIFY(u16 == 0x0004);
+
+	i32 = TurnOffLowestBit((int32_t)0x00000058);    // 01011000
+	EATEST_VERIFY(i32 == 0x00000050);               // 01010000
+
+	u32 = TurnOffLowestBit((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x00000050);
+
+	u32 = TurnOffLowestBit((uint32_t)0x00000000);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	// IsolateLowestBit
+	i16 = IsolateLowestBit((int16_t)0x000C);    // 1100
+	EATEST_VERIFY(i16 == 0x0004);               // 0100
+
+	u16 = IsolateLowestBit((uint16_t)0x000C);
+	EATEST_VERIFY(u16 == 0x0004);
+
+	i32 = IsolateLowestBit((int32_t)0x00000058);    // 01011000
+	EATEST_VERIFY(i32 == 0x00000008);               // 00001000
+
+	u32 = IsolateLowestBit((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x00000008);
+
+	u32 = IsolateLowestBit((uint32_t)0x00000000);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	// IsolateLowest0Bit
+	i16 = IsolateLowest0Bit((int16_t)0x0003);   // 0011
+	EATEST_VERIFY(i16 == 0x0004);               // 0100
+
+	u16 = IsolateLowest0Bit((uint16_t)0x0003);
+	EATEST_VERIFY(u16 == 0x0004);
+
+	i32 = IsolateLowest0Bit((int32_t)0x000000a7);   // 10100111
+	EATEST_VERIFY(i32 == 0x00000008);               // 00001000
+
+	u32 = IsolateLowest0Bit((uint32_t)0x000000a7);
+	EATEST_VERIFY(u32 == 0x00000008);
+
+	u32 = IsolateLowest0Bit((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	// GetTrailing0Bits
+	i16 = GetTrailing0Bits((int16_t)0x000C);    // 1100
+	EATEST_VERIFY(i16 == 0x0003);               // 0011
+
+	u16 = GetTrailing0Bits((uint16_t)0x000C);
+	EATEST_VERIFY(u16 == 0x0003);
+
+	i32 = GetTrailing0Bits((int32_t)0x00000058);    // 01011000
+	EATEST_VERIFY(i32 == 0x00000007);               // 00000111
+
+	u32 = GetTrailing0Bits((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x00000007);
+
+	u32 = GetTrailing0Bits((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	// GetTrailing1And0Bits
+	i16 = GetTrailing1And0Bits((int16_t)0x000C);    // 1100
+	EATEST_VERIFY(i16 ==0x0007);                    // 0111
+
+	u16 = GetTrailing1And0Bits((uint16_t)0x000C);
+	EATEST_VERIFY(u16 == 0x0007);
+
+	i32 = GetTrailing1And0Bits((int32_t)0x00000058);    // 01011000
+	EATEST_VERIFY(i32 == 0x0000000f);                   // 00001111
+
+	u32 = GetTrailing1And0Bits((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x0000000f);
+
+	u32 = GetTrailing1And0Bits((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 0x00000001);
+
+
+	// PropogateLowestBitDownward
+	i16 = PropogateLowestBitDownward((int16_t)0x0004);  // 0100
+	EATEST_VERIFY(i16 == 0x007);                               // 0111
+
+	u16 = PropogateLowestBitDownward((uint16_t)0x0004);
+	EATEST_VERIFY(u16 == 0x007);
+
+	i32 = PropogateLowestBitDownward((int32_t)0x00000058);  // 01011000
+	EATEST_VERIFY(i32 == 0x0000005f);                              // 01011111
+
+	u32 = PropogateLowestBitDownward((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x0000005f);
+
+	u32 = PropogateLowestBitDownward((uint32_t)0x00000000);
+	EATEST_VERIFY(u32 == 0xffffffff);
+
+
+	// TurnOffLowestContiguousBits
+	i16 = TurnOffLowestContiguousBits((int16_t)0x000b); // 1011
+	EATEST_VERIFY(i16 == 0x0008);                              // 1000
+
+	u16 = TurnOffLowestContiguousBits((uint16_t)0x000b);
+	EATEST_VERIFY(u16 == 0x0008);
+
+	i32 = TurnOffLowestContiguousBits((int32_t)0x00000058); // 01011000
+	EATEST_VERIFY(i32 == 0x00000040);                              // 01000000
+
+	u32 = TurnOffLowestContiguousBits((uint32_t)0x00000058);
+	EATEST_VERIFY(u32 == 0x00000040);
+
+	u32 = TurnOffLowestContiguousBits((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+	u32 = TurnOffLowestContiguousBits((uint32_t)0x00000000);
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	/// TurnOnLowest0Bit
+	i16 = TurnOnLowest0Bit((int16_t)0x0003);    // 0011
+	EATEST_VERIFY(i16 == 0x0007);               // 0111
+
+	u16 = TurnOnLowest0Bit((uint16_t)0x0003);
+	EATEST_VERIFY(u16 == 0x0007);
+
+	i32 = TurnOnLowest0Bit((int32_t)0x000000a7);    // 10100111
+	EATEST_VERIFY(i32 == 0x000000af);               // 10101111
+
+	u32 = TurnOnLowest0Bit((uint32_t)0x000000a7);
+	EATEST_VERIFY(u32 == 0x000000af);
+
+	u32 = TurnOnLowest0Bit((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 0xffffffff);
+
+
+	// GetNextWithEqualBitCount
+	u = 45;
+	for(u64 = 0; u64 < UINT32_MAX; u64++)
+	{
+		unsigned u2;
+		n1 = CountBits(u);
+		u2 = GetNextWithEqualBitCount(u);
+		if(u2 < u)
+			break; // This function can only go up to max int.
+		n2 = CountBits(u2);
+		EATEST_VERIFY(n1 == n2);
+		u = u2;
+	}
+
+
+	// IsolateSingleBits
+	u = IsolateSingleBits((unsigned)0x2ba9aba3);          // 00101011101010011010101110100011
+	EATEST_VERIFY(u == 0x28282820);             		  // 00101000001010000010100000100000
+
+
+
+	// IsolateSingle0Bits
+	u = IsolateSingle0Bits((unsigned)0x2ba9aba3);         // 00101011101010011010101110100011
+	EATEST_VERIFY(u == 0x14505440);                       // 00010100010100000101010001000000
+
+
+
+	// IsolateSingle0And1Bits
+	u = IsolateSingle0And1Bits(0xabababab);     // 10101011101010111010101110101011
+	EATEST_VERIFY(u == 0xfc7c7c7c);             // 11111100011111000111110001111100
+
+
+
+	// ShiftRightSigned
+	i32 = ShiftRightSigned((int32_t)0xc0000000, 2);      // 11000000 00000000 00000000 00000000
+	EATEST_VERIFY(i32 == (int32_t)0xf0000000);           // 11110000 00000000 00000000 00000000
+
+	i32 = ShiftRightSigned((int32_t)0x00000000, 2);      // 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i32 == 0x00000000);                    // 00000000 00000000 00000000 00000000
+
+
+
+	// CountTrailing0Bits
+	i = CountTrailing0Bits((uint32_t)0x00000000); // 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 32);
+
+	i = CountTrailing0Bits((uint32_t)0x000000a8); // 00000000 00000000 00000000 10101000
+	EATEST_VERIFY(i == 3);
+
+	i = CountTrailing0Bits((uint32_t)0xffffffff); // 11111111 11111111 11111111 11111111
+	EATEST_VERIFY(i == 0);
+
+
+	// CountLeading0Bits
+	i = CountLeading0Bits((uint32_t)0x00000000);  // 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 32);
+
+	i = CountLeading0Bits((uint32_t)0x37000000);  // 00110111 00000000 00000000 00000000
+	EATEST_VERIFY(i == 2);
+
+	i = CountLeading0Bits((uint32_t)0xffffffff);  // 11111111 11111111 11111111 11111111
+	EATEST_VERIFY(i == 0);
+
+
+
+	// CountTrailing0Bits
+	i = CountTrailing0Bits(UINT64_C(0x0000000000000000)); // 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 64);
+
+	i = CountTrailing0Bits(UINT64_C(0x00000000000000a8)); // 00000000 00000000 00000000 00000000 00000000 00000000 00000000 10101000
+	EATEST_VERIFY(i == 3);
+
+	i = CountTrailing0Bits(UINT64_C(0x0f00000000000000)); // 00001111 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 56);
+
+	i = CountTrailing0Bits(UINT64_C(0xffffffffffffffff)); // 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+	EATEST_VERIFY(i == 0);
+
+
+	// CountLeading0Bits
+	i = CountLeading0Bits(UINT64_C(0x0000000000000000));  // 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 64);
+
+	i = CountLeading0Bits(UINT64_C(0x0000000037000000));  // 00000000 00000000 00000000 00000000 00110111 00000000 00000000 00000000
+	EATEST_VERIFY(i == 34);
+
+	i = CountLeading0Bits(UINT64_C(0x0f00000000000000));  // 00000000 11111111 00000000 00000000 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(i == 4);
+
+	i = CountLeading0Bits(UINT64_C(0xffffffffffffffff));  // 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+	EATEST_VERIFY(i == 0);
+
+
+	// CountBits
+	i = CountBits(0x11001010);
+	EATEST_VERIFY(i == 4);
+
+	i = CountBits64(UINT64_C(0x1100101011001010));
+	EATEST_VERIFY(i == 8);
+
+	i = CountBits64(UINT64_C(0xEBADD76DEBADD76D));
+	EATEST_VERIFY(i == 44);
+
+
+	// RotateLeft
+	u32 = RotateLeft(0xf0000000, 2);        // 11110000 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0xc0000003);       // 11000000 00000000 00000000 00000011
+
+
+	// RotateRight
+	u32 = RotateRight(0xf0000000, 2);       // 11110000 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0x3c000000);       // 00111100 00000000 00000000 00000000
+
+
+	// ReverseBits
+	u32 = ReverseBits(0xe0000001);          // 11100000 00000000 00000000 00000001
+	EATEST_VERIFY(u32 == 0x80000007);       // 10000000 00000000 00000000 00000111
+
+	u64 = ReverseBits(UINT64_C(0xE00CE00400000001));    // 11100000 00001100 11100000 00000100 00000000 00000000 00000000 00000001
+	EATEST_VERIFY(u64 == UINT64_C(0x8000000020073007)); // 10000000 00000000 00000000 00000000 00100000 00000111 00110000 00000111
+
+
+	// IsolateHighestBit
+	u32 = IsolateHighestBit(UINT32_C(0x00000044));    // 01000100
+	EATEST_VERIFY(u32 == 0x00000040);                 // 01000000
+
+	u32 = IsolateHighestBit(UINT32_C(0x00000000));
+	EATEST_VERIFY(u32 == 0x00000000);
+
+
+	// IsolateHighest0Bit
+	u32 = IsolateHighest0Bit(UINT32_C(0x44000000)); // 01000100 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0x80000000);               // 10000000 00000000 00000000 00000000
+
+	u32 = IsolateHighest0Bit(UINT32_C(0xffffffff)); // 11111111 11111111 11111111 11111111
+	EATEST_VERIFY(u32 == 0x00000000);               // 00000000 00000000 00000000 00000000
+
+
+	// PropogateHighestBitDownward
+	u32 = PropogateHighestBitDownward(UINT32_C(0x48000000));  // 01001000 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0x7FFFFFFF);                         // 01111111 11111111 11111111 11111111
+
+	u32 = PropogateHighestBitDownward(UINT32_C(0x00000000));  // 00000000 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0x00000000);                         // 00000000 00000000 00000000 00000000
+
+
+	// GetHighestContiguous0Bits
+	u32 = GetHighestContiguous0Bits(UINT32_C(0x19000000));  // 00011001 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0xe0000000);                       // 11100000 00000000 00000000 00000000
+
+	u32 = GetHighestContiguous0Bits(UINT32_C(0xff000000));  // 11111111 00000000 00000000 00000000
+	EATEST_VERIFY(u32 == 0x00000000);                       // 00000000 00000000 00000000 00000000
+
+
+	// GetBitwiseEquivalence
+	u32 = GetBitwiseEquivalence((uint32_t)0x000000cc, (uint32_t)0x000000f0);    // 11001100, 11110000
+	EATEST_VERIFY(u32 == 0xffffffc3);                                                  // 11000011
+
+
+	// AreLessThan2BitsSet
+	b = AreLessThan2BitsSet((uint32_t)0x00001000);
+	EATEST_VERIFY(b);
+
+	b = AreLessThan2BitsSet((uint32_t)0x01001110);
+	EATEST_VERIFY(!b);
+
+	b = AreLessThan2BitsSet((uint64_t)0x00001000);
+	EATEST_VERIFY(b);
+
+	b = AreLessThan2BitsSet((uint64_t)0x01001110);
+	EATEST_VERIFY(!b);
+
+	b = AreLessThan2BitsSet((uint32_t)0);
+	EATEST_VERIFY(b);
+
+	b = AreLessThan2BitsSet((uint64_t)0);
+	EATEST_VERIFY(b);
+
+
+	// GetHighestBit
+	u8 = GetHighestBit((uint8_t)0);
+	EATEST_VERIFY(u8 == 0x80);
+
+	u16 = GetHighestBit((uint16_t)0);
+	EATEST_VERIFY(u16 == 0x8000);
+
+	u32 = GetHighestBit((uint32_t)0);
+	EATEST_VERIFY(u32 == 0x80000000);
+
+	u64 = GetHighestBit((uint64_t)0);
+	EATEST_VERIFY(u64 == UINT64_C(0x8000000000000000));
+
+
+	// IsPowerOf2
+	u8 = 0; // This version returns true for zero.
+	b = IsPowerOf2(u8);
+	EATEST_VERIFY(b);
+
+	u8 = 37;
+	b = IsPowerOf2(u8);
+	EATEST_VERIFY(!b);
+
+	u8 = 128;
+	b = IsPowerOf2(u8);
+	EATEST_VERIFY(b);
+
+	u32 = 65537;
+	b = IsPowerOf2(u32);
+	EATEST_VERIFY(!b);
+
+	u32 = 65536;
+	b = IsPowerOf2(u32);
+	EATEST_VERIFY(b);
+
+	u32 = 0;
+	b = IsPowerOf2(u32);
+	EATEST_VERIFY(b);
+
+	u64 = 0;
+	b = IsPowerOf2(u64);
+	EATEST_VERIFY(b);
+
+	u64 = 65537;
+	b = IsPowerOf2(u64);
+	EATEST_VERIFY(!b);
+
+	u64 = 65536;
+	b = IsPowerOf2(u64);
+	EATEST_VERIFY(b);
+
+
+	// RoundUpToPowerOf2
+	u32 = RoundUpToPowerOf2(UINT32_C(66));
+	EATEST_VERIFY(u32 == 128);
+
+	u32 = RoundUpToPowerOf2(UINT32_C(12));
+	EATEST_VERIFY(u32 == 16);
+
+	u32 = RoundUpToPowerOf2(UINT32_C(4));
+	EATEST_VERIFY(u32 == 4);
+
+	u32 = RoundUpToPowerOf2(UINT32_C(0));
+	EATEST_VERIFY(u32 == 0);
+
+
+	// IsMultipleOf
+	b = IsMultipleOf<int, 4>(3);
+	EATEST_VERIFY(!b);
+
+	b = IsMultipleOf<int, 16>(32);
+	EATEST_VERIFY(b);
+
+
+	// IsPowerOf2Minus1
+	b = IsPowerOf2Minus1((uint32_t)0x0000000f);
+	EATEST_VERIFY(b);
+
+	b = IsPowerOf2Minus1((uint32_t)0x0000000b);
+	EATEST_VERIFY(!b);
+
+	b = IsPowerOf2Minus1((uint32_t)0x00000000);
+	EATEST_VERIFY(b);
+
+
+	// CrossesPowerOf2
+	b = CrossesPowerOf2((int)4, (int)5, (int)8);
+	EATEST_VERIFY(!b);
+
+	b = CrossesPowerOf2((int)5, (int)9, (int)8);
+	EATEST_VERIFY(b);
+
+
+	// CrossesPowerOf2
+	b = CrossesPowerOf2<int, 8>(4, 5);
+	EATEST_VERIFY(!b);
+
+	b = CrossesPowerOf2<int, 8>(5, 9);
+	EATEST_VERIFY(b);
+
+
+	// GetHighestBitPowerOf2
+	u32 = GetHighestBitPowerOf2((uint32_t)0);
+	EATEST_VERIFY(u32 == 0);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)1);
+	EATEST_VERIFY(u32 == 0);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)2);
+	EATEST_VERIFY(u32 == 1);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)3);
+	EATEST_VERIFY(u32 == 1);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)4);
+	EATEST_VERIFY(u32 == 2);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)7);
+	EATEST_VERIFY(u32 == 2);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)8);
+	EATEST_VERIFY(u32 == 3);
+
+	u32 = GetHighestBitPowerOf2((uint32_t)0xffffffff);
+	EATEST_VERIFY(u32 == 31);
+
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000000000000000));
+	EATEST_VERIFY(u32 == 0);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000000000000001));
+	EATEST_VERIFY(u32 == 0);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000000000001010));
+	EATEST_VERIFY(u32 == 12);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000000001101010));
+	EATEST_VERIFY(u32 == 24);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000000010110111));
+	EATEST_VERIFY(u32 == 28);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0000001010110111));
+	EATEST_VERIFY(u32 == 36);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x0001000010110111));
+	EATEST_VERIFY(u32 == 48);
+
+	u32 = GetHighestBitPowerOf2(UINT64_C(0x1100000010110111));
+	EATEST_VERIFY(u32 == 60);
+
+
+	// GetNextGreaterEven
+	i = GetNextGreaterEven((int)-3);  
+	EATEST_VERIFY(i == -2);
+
+	i = GetNextGreaterEven((int)-2);  
+	EATEST_VERIFY(i == 0);
+
+	i = GetNextGreaterEven((int)-1);  
+	EATEST_VERIFY(i == 0);
+
+	i = GetNextGreaterEven((int)0);  
+	EATEST_VERIFY(i == 2);
+
+	i = GetNextGreaterEven((int)1);  
+	EATEST_VERIFY(i == 2);
+
+	i = GetNextGreaterEven((int)2);  
+	EATEST_VERIFY(i == 4);
+
+	i = GetNextGreaterEven((int)3);  
+	EATEST_VERIFY(i == 4);
+
+
+	// GetNextGreaterOdd
+	i = GetNextGreaterOdd((int)-3);  
+	EATEST_VERIFY(i == -1);
+
+	i = GetNextGreaterOdd((int)-2);  
+	EATEST_VERIFY(i == -1);
+
+	i = GetNextGreaterOdd((int)-1);  
+	EATEST_VERIFY(i == 1);
+
+	i = GetNextGreaterOdd((int)0);  
+	EATEST_VERIFY(i == 1);
+
+	i = GetNextGreaterOdd((int)1);  
+	EATEST_VERIFY(i == 3);
+
+	i = GetNextGreaterOdd((int)2);  
+	EATEST_VERIFY(i == 3);
+
+	i = GetNextGreaterOdd((int)3);  
+	EATEST_VERIFY(i == 5);
+
+
+	// RoundUpTo
+	i32 = RoundUpTo<int32_t, 4>(3);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundUpTo<uint32_t, 4>(3);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundUpTo<int8_t, 4>(3);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundUpTo<uint8_t, 4>(3);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundUpTo<int64_t, 4>(3);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundUpTo<uint64_t, 4>(3);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundUpTo<int32_t, 4>(8);
+	EATEST_VERIFY(i32 == 8);
+	u32 = RoundUpTo<uint32_t, 4>(8);
+	EATEST_VERIFY(u32 == 8);
+	i8 = RoundUpTo<int8_t, 4>(8);
+	EATEST_VERIFY(i8 == 8);
+	u8 = RoundUpTo<uint8_t, 4>(8);
+	EATEST_VERIFY(u8 == 8);
+	i64 = RoundUpTo<int64_t, 4>(8);
+	EATEST_VERIFY(i64 == 8);
+	u64 = RoundUpTo<uint64_t, 4>(8);
+	EATEST_VERIFY(u64 == 8);
+
+	i32 = RoundUpTo<int32_t, 4>(0);
+	EATEST_VERIFY(i32 == 0);
+	u32 = RoundUpTo<uint32_t, 4>(0);
+	EATEST_VERIFY(u32 == 0);
+	i8 = RoundUpTo<int8_t, 4>(0);
+	EATEST_VERIFY(i8 == 0);
+	u8 = RoundUpTo<uint8_t, 4>(0);
+	EATEST_VERIFY(u8 == 0);
+	i64 = RoundUpTo<int64_t, 4>(0);
+	EATEST_VERIFY(i64 == 0);
+	u64 = RoundUpTo<uint64_t, 4>(0);
+	EATEST_VERIFY(u64 == 0);
+
+	i32 = RoundUpTo<int32_t, 4>(-7);
+	EATEST_VERIFY(i32 == -4);
+	i8 = RoundUpTo<int8_t, 4>(-7);
+	EATEST_VERIFY(i8 == -4);
+	i64 = RoundUpTo<int64_t, 4>(-7);
+	EATEST_VERIFY(i64 == -4);
+
+
+	// RoundUpToEx
+	i32 = RoundUpToEx<int32_t, 4>(3);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundUpToEx<uint32_t, 4>(3);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundUpToEx<int8_t, 4>(3);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundUpToEx<uint8_t, 4>(3);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundUpToEx<int64_t, 4>(3);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundUpToEx<uint64_t, 4>(3);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundUpToEx<int32_t, 4>(8);
+	EATEST_VERIFY(i32 == 8);
+	u32 = RoundUpToEx<uint32_t, 4>(8);
+	EATEST_VERIFY(u32 == 8);
+	i8 = RoundUpToEx<int8_t, 4>(8);
+	EATEST_VERIFY(i8 == 8);
+	u8 = RoundUpToEx<uint8_t, 4>(8);
+	EATEST_VERIFY(u8 == 8);
+	i64 = RoundUpToEx<int64_t, 4>(8);
+	EATEST_VERIFY(i64 == 8);
+	u64 = RoundUpToEx<uint64_t, 4>(8);
+	EATEST_VERIFY(u64 == 8);
+
+	i32 = RoundUpToEx<int32_t, 4>(0);
+	EATEST_VERIFY(i32 == 0);
+	u32 = RoundUpToEx<uint32_t, 4>(0);
+	EATEST_VERIFY(u32 == 0);
+	i8 = RoundUpToEx<int8_t, 4>(0);
+	EATEST_VERIFY(i8 == 0);
+	u8 = RoundUpToEx<uint8_t, 4>(0);
+	EATEST_VERIFY(u8 == 0);
+	i64 = RoundUpToEx<int64_t, 4>(0);
+	EATEST_VERIFY(i64 == 0);
+	u64 = RoundUpToEx<uint64_t, 4>(0);
+	EATEST_VERIFY(u64 == 0);
+
+	i32 = RoundUpToEx<int32_t, 4>(-7);
+	EATEST_VERIFY(i32 == -8);
+	i8 = RoundUpToEx<int8_t, 4>(-7);
+	EATEST_VERIFY(i8 == -8);
+	i64 = RoundUpToEx<int64_t, 4>(-7);
+	EATEST_VERIFY(i64 == -8);
+
+
+	// RoundDownTo
+	i32 = RoundDownTo<int32_t, 4>(5);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundDownTo<uint32_t, 4>(5);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundDownTo<int8_t, 4>(5);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundDownTo<uint8_t, 4>(5);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundDownTo<int64_t, 4>(5);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundDownTo<uint64_t, 4>(5);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundDownTo<int32_t, 4>(4);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundDownTo<uint32_t, 4>(4);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundDownTo<int8_t, 4>(4);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundDownTo<uint8_t, 4>(4);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundDownTo<int64_t, 4>(4);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundDownTo<uint64_t, 4>(4);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundDownTo<int32_t, 4>(0);
+	EATEST_VERIFY(i32 == 0);
+	u32 = RoundDownTo<uint32_t, 4>(0);
+	EATEST_VERIFY(u32 == 0);
+	i8 = RoundDownTo<int8_t, 4>(0);
+	EATEST_VERIFY(i8 == 0);
+	u8 = RoundDownTo<uint8_t, 4>(0);
+	EATEST_VERIFY(u8 == 0);
+	i64 = RoundDownTo<int64_t, 4>(0);
+	EATEST_VERIFY(i64 == 0);
+	u64 = RoundDownTo<uint64_t, 4>(0);
+	EATEST_VERIFY(u64 == 0);
+
+	i32 = RoundDownTo<int32_t, 4>(-7);
+	EATEST_VERIFY(i32 == -8);
+	i8 = RoundDownTo<int8_t, 4>(-7);
+	EATEST_VERIFY(i8 == -8);
+	i64 = RoundDownTo<int64_t, 4>(-7);
+	EATEST_VERIFY(i64 == -8);
+
+
+	// RoundDownToEx
+	i32 = RoundDownToEx<int32_t, 4>(5);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundDownToEx<uint32_t, 4>(5);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundDownToEx<int8_t, 4>(5);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundDownToEx<uint8_t, 4>(5);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundDownToEx<int64_t, 4>(5);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundDownToEx<uint64_t, 4>(5);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundDownToEx<int32_t, 4>(4);
+	EATEST_VERIFY(i32 == 4);
+	u32 = RoundDownToEx<uint32_t, 4>(4);
+	EATEST_VERIFY(u32 == 4);
+	i8 = RoundDownToEx<int8_t, 4>(4);
+	EATEST_VERIFY(i8 == 4);
+	u8 = RoundDownToEx<uint8_t, 4>(4);
+	EATEST_VERIFY(u8 == 4);
+	i64 = RoundDownToEx<int64_t, 4>(4);
+	EATEST_VERIFY(i64 == 4);
+	u64 = RoundDownToEx<uint64_t, 4>(4);
+	EATEST_VERIFY(u64 == 4);
+
+	i32 = RoundDownToEx<int32_t, 4>(0);
+	EATEST_VERIFY(i32 == 0);
+	u32 = RoundDownToEx<uint32_t, 4>(0);
+	EATEST_VERIFY(u32 == 0);
+	i8 = RoundDownToEx<int8_t, 4>(0);
+	EATEST_VERIFY(i8 == 0);
+	u8 = RoundDownToEx<uint8_t, 4>(0);
+	EATEST_VERIFY(u8 == 0);
+	i64 = RoundDownToEx<int64_t, 4>(0);
+	EATEST_VERIFY(i64 == 0);
+	u64 = RoundDownToEx<uint64_t, 4>(0);
+	EATEST_VERIFY(u64 == 0);
+
+	i32 = RoundDownToEx<int32_t, 4>(-7);
+	EATEST_VERIFY(i32 == -4);
+	i8 = RoundDownToEx<int8_t, 4>(-7);
+	EATEST_VERIFY(i8 == -4);
+	i64 = RoundDownToEx<int64_t, 4>(-7);
+	EATEST_VERIFY(i64 == -4);
+
+
+	// RoundUpToMultiple
+	i32 = RoundUpToMultiple<int32_t, 6>(37);
+	EATEST_VERIFY(i32 == 42);
+	u32 = RoundUpToMultiple<uint32_t, 6>(37);
+	EATEST_VERIFY(u32 == 42);
+	i8 = RoundUpToMultiple<int8_t, 6>(37);
+	EATEST_VERIFY(i8 == 42);
+	u8 = RoundUpToMultiple<uint8_t, 6>(37);
+	EATEST_VERIFY(u8 == 42);
+	i64 = RoundUpToMultiple<int64_t, 6>(37);
+	EATEST_VERIFY(i64 == 42);
+	u64 = RoundUpToMultiple<uint64_t, 6>(37);
+	EATEST_VERIFY(u64 == 42);
+
+	i32 = RoundUpToMultiple<int32_t, 6>(41);
+	EATEST_VERIFY(i32 == 42);
+	u32 = RoundUpToMultiple<uint32_t, 6>(41);
+	EATEST_VERIFY(u32 == 42);
+	i8 = RoundUpToMultiple<int8_t, 6>(41);
+	EATEST_VERIFY(i8 == 42);
+	u8 = RoundUpToMultiple<uint8_t, 6>(41);
+	EATEST_VERIFY(u8 == 42);
+	i64 = RoundUpToMultiple<int64_t, 6>(41);
+	EATEST_VERIFY(i64 == 42);
+	u64 = RoundUpToMultiple<uint64_t, 6>(41);
+	EATEST_VERIFY(u64 == 42);
+
+	i32 = RoundUpToMultiple<int32_t, 6>(42);
+	EATEST_VERIFY(i32 == 42);
+	u32 = RoundUpToMultiple<uint32_t, 6>(42);
+	EATEST_VERIFY(u32 == 42);
+	i8 = RoundUpToMultiple<int8_t, 6>(42);
+	EATEST_VERIFY(i8 == 42);
+	u8 = RoundUpToMultiple<uint8_t, 6>(42);
+	EATEST_VERIFY(u8 == 42);
+	i64 = RoundUpToMultiple<int64_t, 6>(42);
+	EATEST_VERIFY(i64 == 42);
+	u64 = RoundUpToMultiple<uint64_t, 6>(42);
+	EATEST_VERIFY(u64 == 42);
+
+
+	// RoundDownToMultiple
+	i32 = RoundDownToMultiple<int32_t, 6>(37);
+	EATEST_VERIFY(i32 == 36);
+	u32 = RoundDownToMultiple<uint32_t, 6>(37);
+	EATEST_VERIFY(u32 == 36);
+	i8 = RoundDownToMultiple<int8_t, 6>(37);
+	EATEST_VERIFY(i8 == 36);
+	u8 = RoundDownToMultiple<uint8_t, 6>(37);
+	EATEST_VERIFY(u8 == 36);
+	i64 = RoundDownToMultiple<int64_t, 6>(37);
+	EATEST_VERIFY(i64 == 36);
+	u64 = RoundDownToMultiple<uint64_t, 6>(37);
+	EATEST_VERIFY(u64 == 36);
+
+	i32 = RoundDownToMultiple<int32_t, 6>(41);
+	EATEST_VERIFY(i32 == 36);
+	u32 = RoundDownToMultiple<uint32_t, 6>(41);
+	EATEST_VERIFY(u32 == 36);
+	i8 = RoundDownToMultiple<int8_t, 6>(41);
+	EATEST_VERIFY(i8 == 36);
+	u8 = RoundDownToMultiple<uint8_t, 6>(41);
+	EATEST_VERIFY(u8 == 36);
+	i64 = RoundDownToMultiple<int64_t, 6>(41);
+	EATEST_VERIFY(i64 == 36);
+	u64 = RoundDownToMultiple<uint64_t, 6>(41);
+	EATEST_VERIFY(u64 == 36);
+
+	i32 = RoundDownToMultiple<int32_t, 6>(36);
+	EATEST_VERIFY(i32 == 36);
+	u32 = RoundDownToMultiple<uint32_t, 6>(36);
+	EATEST_VERIFY(u32 == 36);
+	i8 = RoundDownToMultiple<int8_t, 6>(36);
+	EATEST_VERIFY(i8 == 36);
+	i64 = RoundDownToMultiple<int64_t, 6>(36);
+	EATEST_VERIFY(i64 == 36);
+
+	// ZeroPresent8
+	b = ZeroPresent8(UINT32_C(0xffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent8(UINT32_C(0x01010101));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent8(UINT32_C(0x00ffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT32_C(0xff00ffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT32_C(0xffff00ff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT32_C(0xffffff00));
+	EATEST_VERIFY(b);
+
+	b = ZeroPresent8(UINT64_C(0xffffffffffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent8(UINT64_C(0x0101010101010101));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent8(UINT64_C(0x00ffffffffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xff00ffffffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffff00ffffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffffff00ffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffffffff00ffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffffffffff00ffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffffffffffff00ff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent8(UINT64_C(0xffffffffffffff00));
+	EATEST_VERIFY(b);
+
+	/// ZeroPresent16
+	b = ZeroPresent16(UINT32_C(0xffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT32_C(0xff0000ff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT32_C(0x0000ffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent16(UINT32_C(0xffff0000));
+	EATEST_VERIFY(b);
+
+	b = ZeroPresent16(UINT64_C(0xffffffffffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT64_C(0xff0000ffffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT64_C(0xffffff0000ffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT64_C(0xffffffffff0000ff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent16(UINT64_C(0x0000ffffffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent16(UINT64_C(0xffff0000ffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent16(UINT64_C(0xffffffff0000ffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent16(UINT64_C(0xffffffffffff0000));
+	EATEST_VERIFY(b);
+
+	/// ZeroPresent32
+	b = ZeroPresent32(UINT64_C(0xffffffffffffffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent32(UINT64_C(0xff00000000fffff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent32(UINT64_C(0xffffff00000000ff));
+	EATEST_VERIFY(!b);
+	b = ZeroPresent32(UINT64_C(0x00000000ffffffff));
+	EATEST_VERIFY(b);
+	b = ZeroPresent32(UINT64_C(0xffffffff00000000));
+	EATEST_VERIFY(b);
+
+
+	// Log2
+	u32 = Log2(UINT32_C(4));
+	EATEST_VERIFY(u32 == 2);
+
+	u32 = Log2(UINT32_C(8));
+	EATEST_VERIFY(u32 == 3);
+
+	u32 = Log2(UINT32_C(11));
+	EATEST_VERIFY(u32 == 3);
+
+	u32 = Log2(UINT32_C(16));
+	EATEST_VERIFY(u32 == 4);
+
+
+	u64 = Log2(UINT64_C(4));
+	EATEST_VERIFY(u64 == 2);
+
+	u64 = Log2(UINT64_C(8));
+	EATEST_VERIFY(u64 == 3);
+
+	u64 = Log2(UINT64_C(11));
+	EATEST_VERIFY(u64 == 3);
+
+	u64 = Log2(UINT64_C(16));
+	EATEST_VERIFY(u64 == 4);
+
+
+	// Log2Uint32 / Log2Int32 / Log2Uint64 / Log2Int64
+	u32 = Log2Uint32<1>::value;
+	EATEST_VERIFY(u32 == 0);
+
+	u32 = Log2Uint32<8>::value;
+	EATEST_VERIFY(u32 == 3);
+
+	u32 = Log2Uint32<0xffffffff>::value;
+	EATEST_VERIFY(u32 == 31);
+
+	i32 = Log2Int32<1>::value;
+	EATEST_VERIFY(i32 == 0);
+
+	i32 = Log2Int32<8>::value;
+	EATEST_VERIFY(i32 == 3);
+
+	i32 = Log2Int32<0x7fffffff>::value;
+	EATEST_VERIFY(i32 == 30);
+
+	u64 = Log2Uint64<UINT64_C(0x0000000000000001)>::value;
+	EATEST_VERIFY(u64 == 0);
+
+	u64 = Log2Uint64<UINT64_C(0x1000000000000000)>::value;
+	EATEST_VERIFY(u64 == 60);
+
+	u64 = Log2Uint64<UINT64_C(0xffffffffffffffff)>::value;
+	EATEST_VERIFY(u64 == 63);
+
+	i64 = Log2Int64<INT64_C(0x0000000000000001)>::value;
+	EATEST_VERIFY(i64 == 0);
+
+	i64 = Log2Int64<INT64_C(0x1000000000000000)>::value;
+	EATEST_VERIFY(i64 == 60);
+
+	i64 = Log2Int64<INT64_C(0x7fffffffffffffff)>::value;
+	EATEST_VERIFY(i64 == 62);
+
+
+	// SignedAdditionWouldOverflow
+	b = SignedAdditionWouldOverflow<int8_t>(2, 3);
+	EATEST_VERIFY(!b);
+
+	b = SignedAdditionWouldOverflow<int8_t>(INT8_MAX - 4, INT8_MAX - 10);
+	EATEST_VERIFY(b);
+
+	b = SignedAdditionWouldOverflow<int8_t>(INT8_MIN + 4, INT8_MIN + 10);
+	EATEST_VERIFY(b);
+
+	b = SignedAdditionWouldOverflow<int>(2, 3);
+	EATEST_VERIFY(!b);
+
+	b = SignedAdditionWouldOverflow<int>(INT32_MAX - 4, INT32_MAX - 10);
+	EATEST_VERIFY(b);
+
+	b = SignedAdditionWouldOverflow<int>(INT32_MIN + 4, INT32_MIN + 10);
+	EATEST_VERIFY(b);
+
+	b = SignedAdditionWouldOverflow<int64_t>(2, 3);
+	EATEST_VERIFY(!b);
+
+	b = SignedAdditionWouldOverflow<int64_t>(INT64_MAX - 4, INT64_MAX - 10);
+	EATEST_VERIFY(b);
+
+	b = SignedAdditionWouldOverflow<int64_t>(INT64_MIN + 4, INT64_MIN + 10);
+	EATEST_VERIFY(b);
+
+
+	// UnsignedAdditionWouldOverflow
+	b = UnsignedAdditionWouldOverflow<uint16_t>(2, 3);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedAdditionWouldOverflow<uint16_t>(UINT16_MAX - 4, UINT16_MAX - 10);
+	EATEST_VERIFY(b);
+
+	b = UnsignedAdditionWouldOverflow<uint64_t>(1000, 1);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedAdditionWouldOverflow<uint64_t>(UINT64_MAX - 4, 10);
+	EATEST_VERIFY(b);
+
+
+	// SignedSubtractionWouldOverflow
+	b = SignedSubtractionWouldOverflow<int8_t>(100, 1);
+	EATEST_VERIFY(!b);
+	
+	b = SignedSubtractionWouldOverflow<int8_t>(INT8_MAX - 4, INT8_MIN + 10);
+	EATEST_VERIFY(b);
+	
+	b = SignedSubtractionWouldOverflow<int8_t>(INT8_MIN + 10, INT8_MAX - 4);
+	EATEST_VERIFY(b);
+	
+	b = SignedSubtractionWouldOverflow<int>(1000, 1);
+	EATEST_VERIFY(!b);
+	
+	b = SignedSubtractionWouldOverflow<int>(INT32_MAX - 4, INT32_MIN + 10);
+	EATEST_VERIFY(b);
+	
+	b = SignedSubtractionWouldOverflow<int>(INT32_MIN + 10, INT32_MAX - 4);
+	EATEST_VERIFY(b);
+	
+	b = SignedSubtractionWouldOverflow<int64_t>(1000, 1);
+	EATEST_VERIFY(!b);
+	
+	b = SignedSubtractionWouldOverflow<int64_t>(INT64_MAX - 4, INT64_MIN + 10);
+	EATEST_VERIFY(b);
+	
+	b = SignedSubtractionWouldOverflow<int64_t>(INT64_MIN + 10, INT64_MAX - 4);
+	EATEST_VERIFY(b);
+	
+
+	// UnsignedSubtractionWouldOverflow
+	b = UnsignedSubtractionWouldOverflow<uint16_t>(1000, 1);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedSubtractionWouldOverflow<uint16_t>(UINT16_MAX - 10, UINT16_MAX - 4);
+	EATEST_VERIFY(b);
+
+	b = UnsignedSubtractionWouldOverflow<uint64_t>(1000, 1);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedSubtractionWouldOverflow<uint64_t>(UINT64_MAX - 10, UINT64_MAX - 4);
+	EATEST_VERIFY(b);
+
+
+	// UnsignedMultiplyWouldOverflow
+	// Disabled because some compilers mistakenly decide that the code will be dividing by zero.
+	//b = UnsignedMultiplyWouldOverflow((uint32_t)0, (uint32_t)0);
+	//EATEST_VERIFY(!b);
+
+	b = UnsignedMultiplyWouldOverflow((uint32_t)4, (uint32_t)5);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedMultiplyWouldOverflow((uint32_t)0xffffffff, (uint32_t)0xffffffff);
+	EATEST_VERIFY(b);
+
+	// Disabled because some compilers mistakenly decide that the code will be dividing by zero.
+	//b = UnsignedMultiplyWouldOverflow(UINT64_C(0x0000000000000000), UINT64_C(0x0000000000000000));
+	//EATEST_VERIFY(!b);
+
+	b = UnsignedMultiplyWouldOverflow(UINT64_C(0x000000000ffffff4), UINT64_C(0x000000000ffffff5));
+	EATEST_VERIFY(!b);
+
+	b = UnsignedMultiplyWouldOverflow(UINT64_C(0xffffffffffffffff), UINT64_C(0xffffffffffffffff));
+	EATEST_VERIFY(b);
+
+
+	// UnsignedDivisionWouldOverflow
+	b = UnsignedDivisionWouldOverflow(5, 4);
+	EATEST_VERIFY(!b);
+
+	b = UnsignedDivisionWouldOverflow(3, 0);
+	EATEST_VERIFY(b);
+
+
+	// SignedDivisionWouldOverflow
+	b = SignedDivisionWouldOverflow(5, 4);
+	EATEST_VERIFY(!b);
+
+	b = SignedDivisionWouldOverflow(3, 0);
+	EATEST_VERIFY(b);
+
+	b = SignedDivisionWouldOverflow(INT32_MIN, -1);
+	EATEST_VERIFY(b);
+
+
+	// GetAverage
+	i = GetAverage(3, 4);
+	EATEST_VERIFY(i == 3);
+
+	i = GetAverage(3, 3);
+	EATEST_VERIFY(i == 3);
+
+	i = GetAverage(-3, -4);
+	EATEST_VERIFY(i == -4);
+
+	i = GetAverage(-2, 1);
+	EATEST_VERIFY(i == -1);
+
+
+	// GetAverage_Ceiling
+	i = GetAverage_Ceiling(3, 4);
+	EATEST_VERIFY(i == 4);
+
+	i = GetAverage_Ceiling(3, 3);
+	EATEST_VERIFY(i == 3);
+
+	i = GetAverage_Ceiling(-3, -4);
+	EATEST_VERIFY(i == -3);
+
+	i = GetAverage_Ceiling(-2, 1);
+	EATEST_VERIFY(i == 0);
+
+
+	// GetParity
+	i = GetParity(UINT32_C(0x01100011));
+	EATEST_VERIFY(i == 0);
+
+	i = GetParity(UINT32_C(0x00101010));
+	EATEST_VERIFY(i == 1);
+
+
+	// GetIsBigEndian
+	b = GetIsBigEndian();
+	#ifdef EA_SYSTEM_BIG_ENDIAN
+		EATEST_VERIFY(b);
+	#else
+		EATEST_VERIFY(!b);
+	#endif
+
+
+	// ToggleBetween0And1
+	i = ToggleBetween0And1(0);
+	EATEST_VERIFY(i == 1);
+
+	i = ToggleBetween0And1(i);
+	EATEST_VERIFY(i == 0);
+
+	i = ToggleBetween0And1(i);
+	EATEST_VERIFY(i == 1);
+
+
+	// ToggleBetweenIntegers
+	i32 = 37;
+	int32_t i32a = 37, i32b = -38;
+	i32 = ToggleBetweenIntegers(i32, i32a, i32b);
+	EATEST_VERIFY(i32 == -38);
+
+	i32 = ToggleBetweenIntegers(i32, i32a, i32b);
+	EATEST_VERIFY(i32 == 37);
+
+
+	// IsBetween0AndValue
+	b = IsBetween0AndValue(3, 20);
+	EATEST_VERIFY(b);
+
+	b = IsBetween0AndValue(-37, 20);
+	EATEST_VERIFY(!b);
+
+	b = IsBetween0AndValue(37, 20);
+	EATEST_VERIFY(!b);
+
+	b = IsBetween0AndValue(370, 20);
+	EATEST_VERIFY(!b);
+
+
+	// ExchangeValues
+	n1 = 1; n2 = 2;
+	ExchangeValues(n1, n2);
+	EATEST_VERIFY((n1 == 2) && (n2 == 1));
+
+	u64 = 64; uint64_t u64_ = 65;
+	ExchangeValues(u64, u64_);
+	EATEST_VERIFY((u64 == 65) && (u64_ == 64));
+
+	// FloorMod
+	n1 = FloorMod( 10, 3 );
+	EATEST_VERIFY((n1 == 1));
+	n1 = FloorMod( -10, 3 );
+	EATEST_VERIFY((n1 == 2));
+
+	// GetSign
+	i = GetSign(INT32_MIN);
+	EATEST_VERIFY(i == -1);
+
+	i = GetSign(-1000);
+	EATEST_VERIFY(i == -1);
+
+	i = GetSign(0);
+	EATEST_VERIFY(i == 0);
+
+	i = GetSign(5);
+	EATEST_VERIFY(i == 1);
+
+	i = GetSign(INT32_MAX);
+	EATEST_VERIFY(i == 1);
+
+
+	// GetSignEx
+	i = GetSign(-1000);
+	EATEST_VERIFY(i == -1);
+
+	i = GetSign(0);
+	EATEST_VERIFY(i == 0);
+
+	i = GetSign(5);
+	EATEST_VERIFY(i == 1);
+
+	i = GetSign(INT32_MAX);
+	EATEST_VERIFY(i == 1);
+
+
+	// SignExtend12
+	i32 = SignExtend12(0x00000fff);
+	EATEST_VERIFY(i32 == (int32_t)0xffffffff);
+
+	i32 = SignExtend12(0x000007ff);
+	EATEST_VERIFY(i32 == 0x000007ff);
+
+	i32 = SignExtend24(0x00ffffff);
+	EATEST_VERIFY(i32 == (int32_t)0xffffffff);
+
+	i32 = SignExtend24(0x007fffff);
+	EATEST_VERIFY(i32 == 0x007fffff);
+
+
+	// IsUnsigned
+	EATEST_VERIFY( IsUnsigned( u8));
+	EATEST_VERIFY(!IsUnsigned( i8));
+	EATEST_VERIFY( IsUnsigned(u16));
+	EATEST_VERIFY(!IsUnsigned(i16));
+	EATEST_VERIFY( IsUnsigned(u32));
+	EATEST_VERIFY(!IsUnsigned(i32));
+	EATEST_VERIFY( IsUnsigned(u64));
+	EATEST_VERIFY(!IsUnsigned(i64));
+
+
+	// EAIsUnsigned
+	#ifdef _MSC_VER
+		#pragma warning(push, 0)
+		#pragma warning(disable: 4296) // '>=' : expression is always true
+		#pragma warning(disable: 4365) // '=' : conversion from 'int' to 'uint8_t', signed/unsigned mismatch
+		#pragma warning(disable: 4706) // assignment within conditional expression
+	#endif
+	#if !defined(__GNUC__) // GCC generates warnings which you can't disable from code. So just skip the test, as we need to be able to compile this in a "warnings as errors" environment.
+		EATEST_VERIFY( EAIsUnsigned( u8));
+		EATEST_VERIFY( EAIsUnsigned(u16));
+		EATEST_VERIFY( EAIsUnsigned(u32));
+		EATEST_VERIFY( EAIsUnsigned(u64));
+	#endif
+	EATEST_VERIFY(!EAIsUnsigned( i8));
+	EATEST_VERIFY(!EAIsUnsigned(i16));
+	EATEST_VERIFY(!EAIsUnsigned(i32));
+	EATEST_VERIFY(!EAIsUnsigned(i64));
+	#ifdef _MSC_VER
+		#pragma warning(pop)
+	#endif
+
+
+	// IsTwosComplement
+	// All current platforms are twos-complement
+	EATEST_VERIFY( IsTwosComplement());
+	EATEST_VERIFY(!IsOnesComplement());
+	EATEST_VERIFY(!IsSignMagnitude());
+	EATEST_VERIFY(!IsOffsetBinary());
+
+
+	// EAArrayCount
+	int testArray[37]; (void) testArray;
+	u = EAArrayCount(testArray);
+	EATEST_VERIFY(u == 37);
+
+
+	// EAOffsetOf
+	size_t o = EAOffsetOf(OffsetofTest, x1);
+	EATEST_VERIFY(o == 4);
+	EA_COMPILETIME_ASSERT(EAOffsetOf(OffsetofTest, x1) == 4); // Verify that this works at compile-time.
+
+	int ind = 1;
+	o = EAOffsetOf(OffsetofTest1, x[ind]);
+	EATEST_VERIFY(o == 4);
+
+
+	// EAOffsetOfBase   
+	o = EAOffsetOfBase<C, B>();
+	EATEST_VERIFY(o == EAOffsetOf(C, b));
+	//static_assert((EAOffsetOfBase<C, B>() == sizeof(int)), "EAOffsetOfDerived failure"); Not possible unless and until we can make EAOffsetOfBase a constexpr.
+
+	return nErrorCount;
+}
+
+
+
+

+ 108 - 0
test/source/TestByteCrackers.cpp

@@ -0,0 +1,108 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAByteCrackers.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+
+int TestByteCrackers()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestByteCrackers\n");
+
+	#ifdef _MSC_VER
+		#pragma warning (disable: 4310) // Cast truncates constant value
+	#endif
+
+	// uint8_t byte manipulators
+	uint8_t u8;
+
+	u8 = UINT8_0_FROM_UINT16(0x1100);
+	EATEST_VERIFY(u8 == 0x00);
+	u8 = UINT8_1_FROM_UINT16(0x1100);
+	EATEST_VERIFY(u8 == 0x11);
+
+	u8 = UINT8_0_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u8 == 0x00);
+	u8 = UINT8_1_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u8 == 0x11);
+	u8 = UINT8_2_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u8 == 0x22);
+	u8 = UINT8_3_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u8 == 0x33);
+
+	u8 = UINT8_0_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x00);
+	u8 = UINT8_1_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x11);
+	u8 = UINT8_2_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x22);
+	u8 = UINT8_3_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x33);
+	u8 = UINT8_4_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x44);
+	u8 = UINT8_5_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x55);
+	u8 = UINT8_6_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x66);
+	u8 = UINT8_7_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u8 == 0x77);
+
+
+	// uint16_t byte manipulators
+	uint16_t u16;
+
+	u16 = UINT16_0_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u16 == 0x1100);
+	u16 = UINT16_1_FROM_UINT32(0x33221100);
+	EATEST_VERIFY(u16 == 0x3322);
+
+	u16 = UINT16_0_FROM_UINT64(0x7766554433221100LL);
+	EATEST_VERIFY(u16 == 0x1100);
+	u16 = UINT16_1_FROM_UINT64(0x7766554433221100LL);
+	EATEST_VERIFY(u16 == 0x3322);
+	u16 = UINT16_2_FROM_UINT64(0x7766554433221100LL);
+	EATEST_VERIFY(u16 == 0x5544);
+	u16 = UINT16_3_FROM_UINT64(0x7766554433221100LL);
+	EATEST_VERIFY(u16 == 0x7766);
+
+	u16 = UINT16_FROM_UINT8(0x11, 0x00);
+	EATEST_VERIFY(u16 == 0x1100);
+
+
+	// uint32_t byte manipulators
+	uint32_t u32;
+
+	u32 = UINT32_0_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u32 == 0x33221100);
+	u32 = UINT32_1_FROM_UINT64(UINT64_C(0x7766554433221100));
+	EATEST_VERIFY(u32 == 0x77665544);
+
+	u32 = UINT32_FROM_UINT8(0x33, 0x22, 0x11, 0x00);
+	EATEST_VERIFY(u32 == 0x33221100);
+
+	u32 = UINT32_FROM_UINT16(0x3322, 0x1100);
+	EATEST_VERIFY(u32 == 0x33221100);
+
+
+	// uint64_t byte manipulators
+	uint64_t u64;
+
+	u64 = UINT64_FROM_UINT8(0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00);
+	EATEST_VERIFY(u64 == UINT64_C(0x7766554433221100));
+	u64 = UINT64_FROM_UINT16(0x7766, 0x5544, 0x3322, 0x1100);
+	EATEST_VERIFY(u64 == UINT64_C(0x7766554433221100));
+	u64 = UINT64_FROM_UINT32(0x77665544, 0x33221100);
+	EATEST_VERIFY(u64 == UINT64_C(0x7766554433221100));
+
+	return nErrorCount;
+}
+
+
+
+
+

+ 233 - 0
test/source/TestCType.cpp

@@ -0,0 +1,233 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EACType.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+
+int TestCType()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	EA::UnitTest::Report("TestCType\n");
+
+	// Character categorization
+	// To do: Change this from a macro to a template.
+	#define ISCHAR_CATEGORY_TEST(type, category, c_from, c_to, positive) \
+	{\
+		for(uint32_t c = (uint32_t)(unsigned char)(c_from); c <= (uint32_t)(unsigned char)(c_to); ++c)\
+			EATEST_VERIFY((Is##category((char##type##_t)c) != 0) == positive);\
+	}
+
+	// Make sure all chars are accepted
+	int32_t  i;
+	char8_t  n8;
+	char16_t n16;
+	int      n32;
+	char8_t  c8;
+	char16_t c16;
+
+	for(c8 = INT8_MIN, i = INT8_MIN; i <= INT8_MAX; ++c8, ++i)
+	{
+		n32 = Isalnum(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isalpha(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isdigit(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isxdigit(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isgraph(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Islower(c8);
+		EATEST_VERIFY(n32 != -1);
+		n8 = Tolower(c8);
+		EATEST_VERIFY(n32 != 99 || (n8 == 0));
+		n32 = Isupper(c8);
+		EATEST_VERIFY(n32 != -1);
+		n8 = Toupper(c8);
+		EATEST_VERIFY(n32 != 99 || (n8 == 0));
+		n32 = Isprint(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Ispunct(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isspace(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Iscntrl(c8);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isascii(c8);
+		EATEST_VERIFY(n32 != -1);
+	}
+
+	// Make sure all chars are accepted
+	for(c16 = 0, i = 0; i <= INT16_MAX; ++c16, ++i)
+	{
+		n32 = Isalnum(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isalpha(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isdigit(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isxdigit(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isgraph(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Islower(c16);
+		EATEST_VERIFY(n32 != -1);
+		n16 = Tolower(c16);
+		EATEST_VERIFY(n32 != 99 || (n16 == 0));
+		n32 = Isupper(c16);
+		EATEST_VERIFY(n32 != -1);
+		n16 = Toupper(c16);
+		EATEST_VERIFY(n32 != 99 || (n16 == 0));
+		n32 = Isprint(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Ispunct(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isspace(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Iscntrl(c16);
+		EATEST_VERIFY(n32 != -1);
+		n32 = Isascii(c16);
+		EATEST_VERIFY(n32 != -1);
+	}
+
+	// ********************* alnum
+	ISCHAR_CATEGORY_TEST(8,  alnum, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(8,  alnum, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(8,  alnum, 'A', 'Z', true);
+	ISCHAR_CATEGORY_TEST(8,  alnum, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, alnum, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(16, alnum, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(16, alnum, 'A', 'Z', true);
+
+	// ********************* alpha
+	ISCHAR_CATEGORY_TEST(8,  alpha, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(8,  alpha, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(8,  alpha, 'A', 'Z', true);
+	ISCHAR_CATEGORY_TEST(8,  alpha, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, alpha, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(16, alpha, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(16, alpha, 'A', 'Z', true);
+	ISCHAR_CATEGORY_TEST(16, alpha, '(', '('+10, false);
+
+	// digit
+	ISCHAR_CATEGORY_TEST(8,  digit, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(8,  digit, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(8,  digit, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(8,  digit, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, digit, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(16, digit, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(16, digit, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(16, digit, '\x70', '\x71', false);
+
+	// xdigit
+	ISCHAR_CATEGORY_TEST(8,  xdigit, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(8,  xdigit, 'a', 'f', true);
+	ISCHAR_CATEGORY_TEST(8,  xdigit, 'A', 'F', true);
+	ISCHAR_CATEGORY_TEST(8,  xdigit, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, xdigit, '0', '9', true);
+	ISCHAR_CATEGORY_TEST(16, xdigit, 'a', 'f', true);
+	ISCHAR_CATEGORY_TEST(16, xdigit, 'A', 'F', true);
+	ISCHAR_CATEGORY_TEST(16, xdigit, 'z'+1, 'z'+60, false);
+
+	// graph
+	ISCHAR_CATEGORY_TEST(8,  graph, '\x21','\x7e', true);
+	ISCHAR_CATEGORY_TEST(8,  graph, '\x0', '\x20', false);
+
+	ISCHAR_CATEGORY_TEST(16, graph, '!', '!'+14, true);
+	ISCHAR_CATEGORY_TEST(16,  graph, '0', 'z', true);
+	ISCHAR_CATEGORY_TEST(16,  graph, '\x0', '\x20', false);
+
+	// lower
+	ISCHAR_CATEGORY_TEST(8,  lower, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(8,  lower, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(8,  lower, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(8,  lower, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, lower, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(16, lower, 'a', 'z', true);
+	ISCHAR_CATEGORY_TEST(16, lower, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(16, lower, '!', '!'+14, false);
+
+	// upper
+	ISCHAR_CATEGORY_TEST(8,  upper, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(8,  upper, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(8,  upper, 'A', 'Z', true);
+	ISCHAR_CATEGORY_TEST(8,  upper, '\x0', '\x15', false);
+
+	ISCHAR_CATEGORY_TEST(16, upper, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(16, upper, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(16, upper, 'A', 'Z', true);
+	ISCHAR_CATEGORY_TEST(16, upper, '!', '!'+14, false);
+
+	// punct
+	ISCHAR_CATEGORY_TEST(8,  punct, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(8,  punct, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(8,  punct, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(8,  punct, '\x0', '\x15', false);
+	ISCHAR_CATEGORY_TEST(8, punct, '!', '!', true);
+	ISCHAR_CATEGORY_TEST(8, punct, '?', '?', true);
+	ISCHAR_CATEGORY_TEST(8, punct, '.', '.', true);
+	ISCHAR_CATEGORY_TEST(8, punct, ',', ',', true);
+
+	ISCHAR_CATEGORY_TEST(16, punct, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(16, punct, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(16, punct, 'A', 'Z', false);
+	ISCHAR_CATEGORY_TEST(16, punct, '!', '!', true);
+	ISCHAR_CATEGORY_TEST(16, punct, '?', '?', true);
+	ISCHAR_CATEGORY_TEST(16, punct, '.', '.', true);
+	ISCHAR_CATEGORY_TEST(16, punct, ',', ',', true);
+	ISCHAR_CATEGORY_TEST(16, punct, ':', ';', true);
+
+	// space
+	ISCHAR_CATEGORY_TEST(8,  space, '0', '9', false);
+	ISCHAR_CATEGORY_TEST(8,  space, ' ', ' ', true);
+	ISCHAR_CATEGORY_TEST(8,  space, '\x0a', '\x0a', true);
+
+	ISCHAR_CATEGORY_TEST(16,  space, 'a', 'z', false);
+	ISCHAR_CATEGORY_TEST(16,  space, '\x09', '\x09', true);
+	ISCHAR_CATEGORY_TEST(16,  space, '\x0d', '\x0d', true);
+
+	// cntrl
+	ISCHAR_CATEGORY_TEST(8,  cntrl, 0, '\x1f', true);
+	ISCHAR_CATEGORY_TEST(8,  cntrl, '\x7f', '\x7f', true);
+
+	ISCHAR_CATEGORY_TEST(16,  cntrl, 0, '\x1f', true);
+	ISCHAR_CATEGORY_TEST(16,  cntrl, '\x7f', '\x7f', true);
+
+	// ascii
+	ISCHAR_CATEGORY_TEST(8,   ascii, '\x0', '\x7f', true);
+	ISCHAR_CATEGORY_TEST(8,   ascii, '\x80', '\xff', false);
+
+	ISCHAR_CATEGORY_TEST(16,  ascii, '\x0', '\x7f', true);
+	ISCHAR_CATEGORY_TEST(16,  ascii, '\x80', '\xff', false);
+
+
+	// Toupper8 / Tolower8 / Toupper16 Tolower16
+	for (int c = 'a'; c < 'z'; ++c)
+	{
+		EATEST_VERIFY(Toupper((char8_t)c) == 'A' + c - 'a');
+		EATEST_VERIFY(Tolower((char8_t)('A' + c - 'a')) == (char8_t)c);
+
+		EATEST_VERIFY(Toupper((char16_t)c) == char16_t('A' + c - 'a'));
+		EATEST_VERIFY(Tolower((char16_t)('A' + c - 'a')) == char16_t((char16_t)c));
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+

+ 446 - 0
test/source/TestCallback.cpp

@@ -0,0 +1,446 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/internal/Config.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EAStdC/EACallback.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EARandomDistribution.h>
+#include <EATest/EATest.h>
+#include <eathread/eathread.h>
+#include <EAAssert/eaassert.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+#if defined(EA_PLATFORM_DESKTOP) // We need to test/debug this module in detail on machines before we enable the test formally for them. Given how threads work on other platforms this test or EACallback could fail on other platforms in async mode.
+	#define EASTDC_EACALLBACK_TESTS_ENABLED 1
+#else
+	#define EASTDC_EACALLBACK_TESTS_ENABLED 0
+#endif
+
+
+
+#if EASTDC_EACALLBACK_TESTS_ENABLED
+
+///////////////////////////////////////////////////////////////////////////////
+// CallbackTest
+///////////////////////////////////////////////////////////////////////////////
+
+class CallbackTest;
+
+class CallbackTest : public EA::StdC::CallbackT<CallbackTest>
+{
+public :
+	CallbackTest();
+
+	void  Reset();
+	float GetAccuracyRate() const;
+	void  CallbackFunction(EA::StdC::Callback* pCallback, uint64_t absoluteValue, uint64_t deltaValue);
+
+public:
+	enum TestParams
+	{
+		kTestTicks               =  100,
+		kTestTime                = 1000,
+		kUserModeEventPulseCount =   20
+	};
+
+	uint32_t            mIndex;               // Which test we are in the array of concurrent tests.
+	EA::StdC::Stopwatch mTestStopwatch;       // Causes us to stop the callbacks after kTestTime has passed.
+	EA::StdC::Stopwatch mStopwatch;           // Used to make sure that the deltaValue argument matches the actual time delta since last called.
+	uint64_t            mNextUnitsMin;        // The minimum time we should be next called back.
+	uint64_t            mNextUnitsMax;        // The maximum time we should be next called back.
+	uint64_t            mLastUnits;           // The last time we were called back.
+	uint32_t            mGoodSampleCount;     // Total number of times our callback function was called and it happened within the time period expected.
+	uint32_t            mBadSampleCount;      // Total number of times our callback function was called and it didn't happen within the time period expected.
+	uint32_t            mTotalSampleCount;    // Total number of times our callback function was called. Equals mGoodSampleCount + mBadSampleCount.
+	uint32_t            mAddRefCount;         // Number of times the AddRef message was sent.
+	uint32_t            mReleaseCount;        // Number of times the Release message was sent.
+	Mode                mMode;                // Used so we can tell how to validate the tests.
+};
+
+
+CallbackTest::CallbackTest()
+  : mIndex(0),
+	mTestStopwatch(EA::StdC::Stopwatch::kUnitsNanoseconds),
+	mStopwatch(EA::StdC::Stopwatch::kUnitsNanoseconds),
+	mNextUnitsMin(0),
+	mNextUnitsMax(0),
+	mLastUnits(0),
+	mGoodSampleCount(0),
+	mBadSampleCount(0),
+	mTotalSampleCount(0),
+	mAddRefCount(0),
+	mReleaseCount(0),
+	mMode(EA::StdC::Callback::kModeSync)
+{
+	SetFunctionInfo(&CallbackTest::CallbackFunction, this, true);
+}
+
+void CallbackTest::Reset()
+{
+	// Leave mIndex as-is.
+	mNextUnitsMin     = 0;
+	mNextUnitsMax     = 0;
+	mTotalSampleCount = 0;
+	mGoodSampleCount  = 0;
+	mBadSampleCount   = 0;
+	mLastUnits        = 0;
+	mAddRefCount      = 0;
+	mReleaseCount     = 0;
+	// Leave mMode as-is.
+}
+
+float CallbackTest::GetAccuracyRate() const
+{
+	// Returns a value between 0.0 and 100.0.
+	return (float)(100.0 * (mTotalSampleCount ? ((double)mGoodSampleCount / (double)mTotalSampleCount) : 1.0));
+}
+
+void CallbackTest::CallbackFunction(EA::StdC::Callback* /*pCallback*/, uint64_t absoluteValue, uint64_t deltaValue)
+{
+	using namespace EA::StdC;
+
+	if(absoluteValue == Callback::kMessageAddRef)
+		mAddRefCount++;
+	else if(absoluteValue == Callback::kMessageRelease)
+		mReleaseCount++;
+	else
+	{
+		const Callback::Type type      = GetType();
+		const uint64_t       period    = GetPeriod();
+		const uint64_t       precision = GetPrecision();
+
+		if(mLastUnits == 0) // If this is the first time being called...
+			mTestStopwatch.Restart();
+		else
+		{
+			++mTotalSampleCount;
+
+			if((absoluteValue >= mNextUnitsMin) && (absoluteValue <= mNextUnitsMax))
+			{
+				if(type == Callback::kTypeTime)
+				{
+					const uint64_t elapsedTime = mStopwatch.GetElapsedTime();
+					const uint64_t difference  = (deltaValue > elapsedTime) ? (deltaValue - elapsedTime) : (elapsedTime - deltaValue);
+					const double   diffPercent = ((double)(int64_t)difference / (double)(int64_t)elapsedTime) * 100.0; // Cast to int64_t because some compilers/platforms can't handle int64_t to double conversions.
+
+					if((mMode == Callback::kModeAsync) && (diffPercent < 10)) // In threaded mode there's more room for failure.
+						++mGoodSampleCount;
+					else if((mMode == Callback::kModeSync) && (diffPercent < 3))
+						++mGoodSampleCount;
+					else
+						++mBadSampleCount;
+				}
+				else // Else kTypeTick or kTypeUserEvent.
+					++mGoodSampleCount;
+			}
+			else
+			{
+				// It turns out that with kModeAsync (threaded), we can get a lot of these failures with kTypeUserEvent.
+				// This is because the main thread is updating the user event counter (because it has to) while this 
+				// callback thread is running independently. It's possible for the main thread to bump up the user event
+				// counter while this callback thread is in the process of issuing the next call. So timeNS could 
+				++mBadSampleCount;
+			}
+		}
+
+		mStopwatch.Restart();
+
+		// Set the next expectation.
+		mLastUnits    = absoluteValue;
+		mNextUnitsMin = ((absoluteValue + period) > precision) ? (absoluteValue + period - precision) : 0;   // Extra logic here to make sure mNextUnitsMin doesn't go negative and wrap around to a very large uint64_t number.
+		
+		// How to decide the next max expected value is not simple because in async mode we have 
+		// two threads, one of which is updating the user event count and the other which is 
+		// servicing callbacks. The former could increase the event count by theoretically any
+		// number while the servicing thread is in the middle of a single callback call.
+		// We can fix this by having the main thread wait until the servicing thread is not 
+		// busy, or we can put some cap on the expected value. Currently we do the latter, 
+		// because it's simpler, but the former is probably better because it lets us test more precisely.
+		if(type == Callback::kTypeUserEvent)
+			mNextUnitsMax = absoluteValue + period + precision + (kUserModeEventPulseCount * 3);
+		else
+			mNextUnitsMax = absoluteValue + period + precision + (int)(1 + (double)period * 0.05f);      // Add a little extra slop to account for CPU stalls and what-not.
+  
+		// See if the test is complete
+		switch (type)
+		{
+			case Callback::kTypeTick:
+				if(mTotalSampleCount >= kTestTicks)
+					Stop();
+				break;
+
+			case Callback::kTypeUserEvent:
+				if(mTotalSampleCount >= kTestTicks)
+					Stop();
+				break;
+
+			case Callback::kTypeTime:
+			{
+				const uint64_t elapsedTime = mTestStopwatch.GetElapsedTime();
+				if(elapsedTime > kTestTime)
+					Stop();
+				break;
+			}
+		}
+	}
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestCallback
+///////////////////////////////////////////////////////////////////////////////
+
+struct TestControlInfo 
+{
+	uint32_t                 mPeriodStart;
+	uint32_t                 mPeriodEnd;
+	uint32_t                 mPeriodStep;
+	uint32_t                 mPrecisionStart;
+	uint32_t                 mPrecisionEnd;
+	uint32_t                 mPrecisionStep;
+	EA::StdC::Callback::Type mType; 
+	uint32_t                 mTestTimeMs;
+};
+
+
+#if defined(EA_PLATFORM_DESKTOP)
+	const TestControlInfo tci[] = 
+	{
+		//            Period                            Precision                      Type                                 Test max time ms
+		//  --------------------------------      ------------------------------       ----------------------------------  ----------------
+		{          1,         51,         50,            0,         1,         1,      EA::StdC::Callback::kTypeTick,      10000          },
+		{          1,         51,         50,            0,         1,         1,      EA::StdC::Callback::kTypeUserEvent, 10000          },
+		{   20000000,   50000000,   30000000,     15000000,  30000000,  15000000,      EA::StdC::Callback::kTypeTime,      10000          }, // These are big numbers because time is in nanoseconds.
+		{  100000000,  200000000,  100000000,     50000000, 100000000,  50000000,      EA::StdC::Callback::kTypeTime,      10000          },
+		{ 1000000000, 2000000000, 1000000000,    200000000, 400000000, 200000000,      EA::StdC::Callback::kTypeTime,      10000          }
+	};
+#else
+	const TestControlInfo tci[] = 
+	{
+		//            Period                            Precision                      Type                                 Test max time ms
+		//  --------------------------------      ------------------------------       ----------------------------------  ----------------
+		{          1,          5,          4,            1,         1,         1,      EA::StdC::Callback::kTypeTick,      60000          },
+		{          1,          5,          4,            1,         1,         1,      EA::StdC::Callback::kTypeUserEvent, 60000          },
+		{   50000000,   50000000,          1,     30000000,  30000000,         1,      EA::StdC::Callback::kTypeTime,      60000          }, // These are big numbers because time is in nanoseconds.
+		{  500000000,  500000000,          1,    200000000, 200000000,         1,      EA::StdC::Callback::kTypeTime,      60000          }
+	};
+#endif
+
+#endif // EASTDC_EACALLBACK_TESTS_ENABLED
+
+
+
+int TestCallback()
+{
+	using namespace EA::StdC;
+
+	EA::UnitTest::Report("TestCallback\n");
+
+	int nErrorCount(0);
+
+	#if EASTDC_EACALLBACK_TESTS_ENABLED
+
+		EA::StdC::RandomFast      random;  
+		EA::StdC::CallbackManager callbackManager;
+		const uint32_t            kCallbackCount = (uint32_t)(100 * EA::UnitTest::GetSystemSpeed(EA::UnitTest::kSpeedTypeCPU));  // Determines the number of callbacks in the callback tests.
+		CallbackTest*             pCallbackTestArray = new CallbackTest[kCallbackCount];        
+
+		EA::StdC::SetCallbackManager(&callbackManager);
+
+		EA::UnitTest::ReportVerbosity(1, "Callback test using %u callbacks. The test shows how well (on average)\n" 
+										 "the callback system is able to satisfy the demands of each callback object.\n", (unsigned)kCallbackCount);
+
+		// For both kModeAsync kModeSync...
+		for(int a = 0; a < 2; a++)
+		{
+			#if EASTDC_THREADING_SUPPORTED
+				const Callback::Mode mode = ((a % 2) ? Callback::kModeAsync : Callback::kModeSync);
+			#else
+				const Callback::Mode mode = Callback::kModeSync;
+			#endif
+
+			callbackManager.Shutdown();
+
+			switch(mode)
+			{
+				case Callback::kModeAsync:
+				{
+					callbackManager.Init(true, true);    // Run in asynchronous (threaded) mode.
+					break;
+				}
+				case Callback::kModeSync:            
+				default:
+				{
+					callbackManager.Init(false, false);  // Run in synchronous (polled, non-threaded) mode.
+					break;
+				}
+			}
+			
+			// For each type of TestControlInfo above...
+			for(size_t cn = 0, n = EAArrayCount(tci); cn < n; ++cn)
+			{
+
+
+				const TestControlInfo& ci = tci[cn];
+
+				for(uint32_t t = 0; t < kCallbackCount; ++t)
+				{
+					CallbackTest& callbackTest = pCallbackTestArray[t];
+
+					callbackTest.mIndex = t;
+					callbackTest.mMode  = mode;
+					callbackTest.SetType(ci.mType);
+				}
+
+				// For an array of periods between begin and end period...
+				for(uint32_t period = ci.mPeriodStart; period <= ci.mPeriodEnd; period += ci.mPeriodStep)
+				{
+					// For an array of precisions between begin and end precision...
+					for(uint32_t precision = ci.mPrecisionStart; precision <= ci.mPrecisionEnd; precision += ci.mPrecisionStep)
+					{
+						// print to avoid test timeout
+						EA::UnitTest::ReportVerbosity(0, "%s", ".");
+
+						EA::UnitTest::ReportVerbosity(1, "Callback test: mode = %s, type = %s, period = %i, precision = %i\n", 
+													  (mode     == Callback::kModeSync) ? "Sync" : "Async", 
+													  (ci.mType == Callback::kTypeTick)  ? "Tick"  : ((ci.mType == Callback::kTypeTime) ? "Time" : "UserEvent"), 
+													  period, 
+													  precision);
+
+						for(uint32_t t = 0; t < kCallbackCount; ++t)
+						{
+							CallbackTest& callbackTest = pCallbackTestArray[t];
+
+							callbackTest.SetPeriod(period);
+							callbackTest.SetPrecision(precision);
+							callbackTest.Start(NULL, false);
+						}
+
+						// Run the test for N seconds, during which our callback function will be called 
+						// repeatedly. After the callback function has been called for an amount 
+						// of kTestTicks or kTestTime, the callback function calls CallbackTimer::Stop.
+						bool                   bCallbacksAreStillRunning = true;
+						bool                   bShouldContinue = true;
+						EA::Thread::ThreadTime endTime = EA::Thread::GetThreadTime() + ci.mTestTimeMs;
+						uint64_t               loopCount; // This is just to help debugging.
+
+						for(loopCount = 0; bShouldContinue && bCallbacksAreStillRunning && (loopCount < UINT64_C(0x0fffffffffffffff)); ++loopCount)
+						{
+							bCallbacksAreStillRunning = false;
+
+							// Randomly trigger a user event.
+							if(EA::StdC::RandomBool(random))
+							{
+								for(int e = 0; e < CallbackTest::kUserModeEventPulseCount; e++) // Trigger multiple events at once.
+									callbackManager.OnUserEvent();
+							}
+
+							// If we are in synchronous (i.e. polled) mode, then we need to manually call Update.
+							// We don't need to call Update in synchronous (threaded) mode, but it's supported so 
+							// we test calling Update occasionally to make sure it works.
+							switch(mode)
+							{
+								case Callback::kModeSync:
+								{
+									callbackManager.Update();
+									break;
+								}
+								case Callback::kModeAsync:
+								default:
+								{
+									if(random.RandomUint32Uniform(100) == 0)
+									{
+										callbackManager.Update();
+									}
+									break;
+								}
+							}
+
+							// Sleep for a little bit.
+							EA::Thread::ThreadTime sleepTime = EA::Thread::kTimeoutImmediate;
+
+							#if EASTDC_THREADING_SUPPORTED
+								// Since we are in a seperate thread from the callback manager and sicne we are updating On UserEvent
+								// ourselves here, it might be useful to sleep here to handle the case that our OnUserEvent calls get
+								// ahead of what the callback manager can keep up with and "CallbackTest low accuracy rate" reports 
+								// can result, though these reports wouldn't be indicative of bugs or failures of EACallback.
+								if((mode == Callback::kModeAsync) && (ci.mType != EA::StdC::Callback::kTypeTime))
+									sleepTime = EA::Thread::ThreadTime(10);
+							#endif
+
+							EA::Thread::ThreadSleep(sleepTime);
+
+							// See if the alotted time has passed.
+							EA::Thread::ThreadTime currentTime = EA::Thread::GetThreadTime();
+							bShouldContinue = (currentTime < endTime);
+
+							// See if the callbacks have all been called the number of times we expected and are thus done now.
+							for(uint32_t t = 0; (t < kCallbackCount) && !bCallbacksAreStillRunning; ++t)
+							{
+								const CallbackTest& callbackTest = pCallbackTestArray[t];
+
+								if(callbackTest.IsRunning())
+								{
+									bCallbacksAreStillRunning = true;
+
+									if(!bShouldContinue) // If the time is up, yet this callback is still running...
+									{
+										// The callback function didn't get called the expected number of times before our loop time above expired.
+										//++nErrorCount; Hard to enable this because it's easy for this test to have some failures. Probably what we should do is count failures and expect some success rate.
+										EA::UnitTest::ReportVerbosity(1, "CallbackTest timeout. The callback didn't fire the expected number of\n"
+																		 "times during our test period. Expected: %u, Actual: %u\n", 
+																		 (callbackTest.GetType() == Callback::kTypeTime) ? (unsigned)CallbackTest::kTestTicks : (unsigned)CallbackTest::kTestTime, (unsigned)callbackTest.mTotalSampleCount);
+									}
+								}
+							}
+						}
+
+						uint32_t successSum = 0;
+
+						for(uint32_t t = 0; t < kCallbackCount; ++t)
+						{
+							CallbackTest& callbackTest = pCallbackTestArray[t];
+
+							successSum += (uint32_t)callbackTest.GetAccuracyRate(); // GetAccuracyRate returns a value in the range of [0, 100]
+							callbackTest.Reset();
+						}
+
+						const uint32_t successRate = (successSum / kCallbackCount); // successRate is a value in the range of [0, 100].
+
+						if(successRate < 90) // If less than 90% of the callbacks occurred within the expected time...
+						{
+							// ++nErrorCount; Hard to enable this because it's easy for this test to have some failures. Probably what we should do is count failures and expect some success rate.
+							EA::UnitTest::ReportVerbosity(1, "CallbackTest low accuracy rate of %i%%.\n", successRate);
+						}
+					}
+				}
+			}
+		}
+
+		delete[] pCallbackTestArray;
+
+	#endif // EA_PLATFORM_DESKTOP
+
+	// to avoid test timeout
+	EA::EAMain::ReportVerbosity(0, "%s", "\n");
+
+	return nErrorCount;
+}
+
+
+

+ 1837 - 0
test/source/TestDateTime.cpp

@@ -0,0 +1,1837 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EADateTime.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EAMemory.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <eathread/eathread.h>
+#include <EATest/EATest.h>
+#include <string.h>
+#include <EAAssert/eaassert.h>
+
+
+#if defined(EA_PLATFORM_MICROSOFT)
+	#ifdef _MSC_VER
+		#pragma warning(push, 0)
+		#if !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+			#include <winsock2.h> // for timeval
+		#endif
+		#include <Windows.h>
+		#pragma warning(pop)
+	#else
+		#include <Windows.h>
+	#endif
+
+	bool GetLocaleInfoHelper(LCTYPE lcType, char* lcData, int cchData)
+	{
+		#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+			return (GetLocaleInfoA(LOCALE_USER_DEFAULT, lcType, lcData, cchData) != 0);
+		#else
+			wchar_t* temp = static_cast<wchar_t*>(EAAlloca(cchData * sizeof(wchar_t)));
+			const bool res = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, lcType, temp, cchData) != 0;
+			EA::StdC::Strlcpy(lcData, temp, static_cast<size_t>(cchData));
+			return res;
+		#endif
+	}
+#endif
+
+#if defined(_MSC_VER)
+	#pragma warning(push)
+	#pragma warning(disable: 6011) // Dereferencing NULL pointer.
+#endif
+
+
+#if defined(EA_HAVE_localtime_DECL)
+	static int VerifyTmMatch(const tm& tmTest, const EA::StdC::DateTime& dtTest)
+	{
+		using namespace EA::StdC;
+
+		int nErrorCount = 0;
+
+		tm temp;
+
+		// What we do here is the same as DateTimeToTm(), but also serves to test it:
+		temp.tm_year = dtTest.GetParameter(kParameterYear) - 1900;      // tm_year: years since 1900
+		temp.tm_mon  = dtTest.GetParameter(kParameterMonth) - 1;        // tm_mon:  months since January - [0,11]
+		temp.tm_mday = dtTest.GetParameter(kParameterDayOfMonth);       // tm_mday: day of the month - [1,31]
+		temp.tm_wday = dtTest.GetParameter(kParameterDayOfWeek) - 1;    // tm_wday: days since Sunday - [0,6]
+		temp.tm_yday = dtTest.GetParameter(kParameterDayOfYear) - 1;    // tm_yday: days since January 1 - [0,365]
+		temp.tm_hour = dtTest.GetParameter(kParameterHour);             // tm_hour: hours since midnight - [0,23]
+		temp.tm_min  = dtTest.GetParameter(kParameterMinute);           // tm_min:  minutes after the hour - [0,59
+		temp.tm_sec  = dtTest.GetParameter(kParameterSecond);           // tm_sec:  seconds after the minute - [0,59]
+
+		EATEST_VERIFY(temp.tm_year == tmTest.tm_year);
+		EATEST_VERIFY(temp.tm_mon  == tmTest.tm_mon);
+		EATEST_VERIFY(temp.tm_mday == tmTest.tm_mday);
+		EATEST_VERIFY(temp.tm_wday == tmTest.tm_wday);
+		EATEST_VERIFY(temp.tm_yday == tmTest.tm_yday);
+		EATEST_VERIFY(temp.tm_hour == tmTest.tm_hour);
+		EATEST_VERIFY(temp.tm_min  == tmTest.tm_min);
+		EATEST_VERIFY(temp.tm_sec  == tmTest.tm_sec);
+
+		return nErrorCount;
+	}
+
+	static int VerifyTmMatch(const tm& tmA, const tm& tmB)
+	{
+		int nErrorCount = 0;
+
+		EATEST_VERIFY(tmA.tm_year == tmB.tm_year);
+		EATEST_VERIFY(tmA.tm_mon  == tmB.tm_mon);
+		EATEST_VERIFY(tmA.tm_mday == tmB.tm_mday);
+		EATEST_VERIFY(tmA.tm_wday == tmB.tm_wday);
+		EATEST_VERIFY(tmA.tm_yday == tmB.tm_yday);
+		EATEST_VERIFY(tmA.tm_hour == tmB.tm_hour);
+		EATEST_VERIFY(tmA.tm_min  == tmB.tm_min);
+		EATEST_VERIFY(tmA.tm_sec  == tmB.tm_sec);
+
+		return nErrorCount;
+	}
+#endif
+
+
+#if defined(EA_HAVE_localtime_DECL)
+	// TestGMTime
+	//
+	// Tests an individual univeral time value by comparing it to what the standard C gmtime and 
+	// localtime functions say on platforms that support these functions. The EAStdC DateTime class
+	// is somewhat like a C++ version of the tm struct, with member functions and finer precision (nanoseconds).
+	// The proper calculations for time can be tricky, and we use a conforming Standard C library 
+	// implementation as a benchmark for results. 
+	// We equate GM time with Universal time.
+	//
+	static int TestGMTime(time_t timeGM)
+	{
+		using namespace EA::StdC;
+
+		int nErrorCount = 0;
+
+		tm        tmLocal = *localtime(&timeGM);
+		tm        tmGM    = *gmtime(&timeGM);
+		tm        tmLocalEA;
+		DateTime  dt;
+
+		// void TmToDateTime(const tm& time, DateTime& dateTime);
+		TmToDateTime(tmLocal, dt);
+		nErrorCount += VerifyTmMatch(tmLocal, dt);
+
+		// void DateTimeToTm(const DateTime& dateTime, tm& time);
+		DateTimeToTm(dt, tmLocalEA);
+		nErrorCount += VerifyTmMatch(tmLocal, tmLocalEA);
+
+		// Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour = 0, uint32_t nMinute = 0, uint32_t nSecond = 0, uint32_t nNanosecond = 0);
+		dt.Set(1900 + tmGM.tm_year, tmGM.tm_mon + 1, tmGM.tm_mday, tmGM.tm_hour, tmGM.tm_min, tmGM.tm_sec, 0);
+		nErrorCount += VerifyTmMatch(tmGM, dt);
+
+		// DateTime(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour = 0, uint32_t nMinute = 0, uint32_t nSecond = 0, uint32_t nNanosecond = 0);
+		dt = DateTime(1900 + tmGM.tm_year, tmGM.tm_mon + 1, tmGM.tm_mday, tmGM.tm_hour, tmGM.tm_min, tmGM.tm_sec, 0);
+		nErrorCount += VerifyTmMatch(tmGM, dt);
+
+		// int64_t GetTimeZoneBias();
+		// bool    IsDSTDateTime(int64_t dateTimeSeconds);
+		// int64_t GetDaylightSavingsBias();
+		const int32_t timeZoneBias        = (int32_t)GetTimeZoneBias();
+		const bool    isDaylightSavings   = IsDSTDateTime(TimeTSecondsSecondsToDateTime(timeGM));
+		const int32_t daylightSavingsBias = (int32_t)GetDaylightSavingsBias();
+
+		dt.Set(1900 + tmGM.tm_year, tmGM.tm_mon + 1, tmGM.tm_mday, tmGM.tm_hour, tmGM.tm_min, tmGM.tm_sec, 0);
+		dt.AddTime(kParameterSecond, timeZoneBias); // Offset by the time zone bias.
+		EATEST_VERIFY(isDaylightSavings == (tmLocal.tm_isdst > 0));
+		if(isDaylightSavings)
+			dt.AddTime(kParameterSecond, daylightSavingsBias); // Offset by the daylight savings time bias.
+
+		nErrorCount += VerifyTmMatch(tmLocal, dt);
+
+		return nErrorCount;
+	}
+#endif
+
+
+#define LOCAL_MAX(x, y) ((x) > (y) ? (x) : (y))
+
+int TestDateTime()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestDateTime\n");
+
+	DateTime dateTimeTest(1970, 1, 1, 0, 0, 0);
+	EATEST_VERIFY(DateTimeSecondsToTimeTSeconds(dateTimeTest.GetSeconds()) == 0);
+
+	// C tm struct conversion and setting local time.
+	#if defined(EA_HAVE_localtime_DECL)
+		// Regression of end-of-month bug, where it is June 1 in Universal time but May 31 in local time (California, -8 hours from Prime Meridian).
+		// nTime = 1338519441: tm_isdst = 1, tm_yday = 151, tm_wday = 4, tm_year = 112, tm_mon = 4, tm_mday = 31, tm_hour = 19, tm_min = 57, tm_sec = 21.
+		nErrorCount += TestGMTime(1338519441);
+
+		// Test a big range of dates
+		// http://www.convert-unix-time.com/?t=946080000
+		time_t timeGMBegin  =    946684800; // UTC: Saturday 1st January 2000 12:00:00 AM
+		time_t timeGMEnd    =   1207008000; // UTC: Tuesday 1st April 2008 12:00:00 AM
+		time_t timeInterval = time_t(2220.0f / LOCAL_MAX(0.1f, EA::UnitTest::GetSystemSpeed(EA::UnitTest::kSpeedTypeCPU))); // 37 minutes divided by the CPU relative speed (making this test run at acceptable speed on slow platforms)
+		int    errorCount   = 0;
+
+		for(time_t t = timeGMBegin; (t < timeGMEnd) && !errorCount; t += timeInterval)
+		{
+			errorCount = TestGMTime(t);
+			nErrorCount += errorCount;
+		}
+	#endif
+
+
+	{   // Verify that DateTime matches time()/localtime()
+		#if defined(EA_HAVE_localtime_DECL)
+			DateTime      dateTime2(EA::StdC::kTimeFrameLocal);
+			const  time_t nTime = time(NULL);
+			struct tm*    pTime = localtime(&nTime);
+			uint32_t      value;
+			int           i;
+
+			// We have a small problem: it's possible that the system time turned over to a 
+			// new second right between the two time calls below. If that seems to be the case
+			// then we do an update of dateTime2 here, which should execute within the same second.
+			// Only in a pathological case would it not execute within the same second.
+			for(i = 0; (i < 5) && (dateTime2.GetParameter(kParameterSecond) != (uint32_t)pTime->tm_sec); i++)
+			{
+				dateTime2 = DateTime(EA::StdC::kTimeFrameLocal);
+				pTime     = localtime(&nTime);
+			}
+			EATEST_VERIFY(i < 5); // Sanity check. i should almost always be 0, and rarely be 1. Just about never anything higher.
+
+			value = dateTime2.GetParameter(kParameterYear);
+			EATEST_VERIFY_F(value == (uint32_t)(1900 + pTime->tm_year), "TestDateTime DateTime year failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d", 
+										value, (uint32_t)(1900 + pTime->tm_year), dateTime2.GetSeconds(), (int64_t)nTime);
+
+			value = dateTime2.GetParameter(kParameterMonth);
+			EATEST_VERIFY_F(value == (uint32_t)(kMonthJanuary + pTime->tm_mon), "TestDateTime DateTime month failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d", 
+										value, (uint32_t)(kMonthJanuary + pTime->tm_mon), dateTime2.GetSeconds(), (int64_t)nTime);
+
+			value = dateTime2.GetParameter(kParameterDayOfMonth);
+			EATEST_VERIFY_F(value == (uint32_t)pTime->tm_mday, "TestDateTime DateTime day of month failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d", 
+										value, (uint32_t)pTime->tm_mday, dateTime2.GetSeconds(), (int64_t)nTime);
+
+			value = dateTime2.GetParameter(kParameterHour);
+			EATEST_VERIFY_F(value == (uint32_t)pTime->tm_hour, "TestDateTime DateTime hour failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d",
+										value, (uint32_t)pTime->tm_hour, dateTime2.GetSeconds(), (int64_t)nTime);
+
+			value = dateTime2.GetParameter(kParameterMinute);
+			EATEST_VERIFY_F(value == (uint32_t)pTime->tm_min, "TestDateTime DateTime minute failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d", 
+										value, (uint32_t)pTime->tm_min, dateTime2.GetSeconds(), (int64_t)nTime);
+
+			value = dateTime2.GetParameter(kParameterSecond);
+			EATEST_VERIFY_F(value == (uint32_t)pTime->tm_sec, "TestDateTime DateTime second failure: value: %u, expected: %u. DateTime seconds: %I64u, time_t: %I64d", 
+										value, (uint32_t)pTime->tm_sec, dateTime2.GetSeconds(), (int64_t)nTime);
+		#endif
+	}
+
+	{
+		// Basic test of setting/getting parameters.
+		DateTime dateTime(2004, 11, 9, 0, 0, 0);
+		DateTime dateTime2(EA::StdC::kTimeFrameLocal);
+
+		dateTime.SetParameter(kParameterYear, 1888);
+		dateTime.SetParameter(kParameterMonth, 5);
+		dateTime.SetParameter(kParameterDayOfMonth, 16);
+		dateTime.SetParameter(kParameterHour, 16);
+		dateTime.SetParameter(kParameterMinute, 44);
+		dateTime.SetParameter(kParameterSecond, 36);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1888);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)5);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)16);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterHour) == (uint32_t)16);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMinute) == (uint32_t)44);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterSecond) == (uint32_t)36);
+
+		dateTime.SetParameter(kParameterDayOfYear, 244);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)244);
+
+		dateTime.SetParameter(kParameterDayOfWeek, kDayOfWeekThursday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekThursday);
+
+		dateTime.SetParameter(kParameterWeekOfYear, 23);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterWeekOfYear) == (uint32_t)23);
+
+		dateTime.SetParameter(kParameterWeekOfMonth, 2);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterWeekOfMonth) == (uint32_t)2);
+
+
+		// Basic day of week day determination
+		dateTime.Set(2004, 9, 25, 0, 0, 0); // 9/25/2004 - Saturday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSaturday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2004);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)9);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)25);
+
+		// Day of week determination on the very beginning of a year
+		dateTime.Set(1995, 1, 1, 0, 0, 0); // 1/1/1995 - Sunday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSunday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1995);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)1);
+
+		// Day of week determination on the very beginning of a leap year
+		dateTime.Set(1980, 1, 1, 0, 0, 0); // 1/1/1980 - Tuesday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekTuesday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1980);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)1);
+
+		// Day of week determination on the very end of a year - note: it should be the 365th day of the year
+		dateTime.Set(2007, 12, 31, 0, 0, 0); // 12/31/2007 - Monday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekMonday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2007);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)12);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)31);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)365);
+
+		// Day of week determination on the very end of the leap year - note: it should be the 366th day of the year
+		dateTime.Set(2004,12,31,0,0,0); // 12/31/2004 - Friday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekFriday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2004);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)12);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)31);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)366);
+
+		// Day of week determination on a leap year - note: this day exists only during leap years
+		dateTime.Set(2004, 2, 29, 0, 0, 0); // 2/29/2004 - Sunday
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSunday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2004);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)2);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)29);
+
+		// Change the year and we no longer have 2/29 - instead we should have 3/1/2005 - Tuesday
+		dateTime.Set(2005, 0xffffffff, 0xffffffff, 0, 0, 0);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekTuesday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2005);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)3);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)1);
+
+		// Go back to 1980 and we should have 2/29 back - Friday
+		dateTime.Set(1980, 2, 29, 0, 0, 0);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekFriday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1980);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)2);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)29);
+
+
+		// Wrapping test - handling of values intentionally out of range
+		dateTime.Set(2004, 14, 32, 25, 66, 126);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSaturday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2005);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)3);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)5);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterHour) == (uint32_t)2);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMinute) == (uint32_t)8);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterSecond) == (uint32_t)6);
+
+
+		// Changing the day of the week so that we go back to a different year 
+		// 01/02/2008 - Wednesday - go back 2 days to Monday and we should have
+		// 12/31/2007 - Monday
+		dateTime.Set(2008, 1, 2, 0, 0, 0);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekWednesday);
+
+		dateTime.SetParameter(kParameterDayOfWeek,kDayOfWeekMonday);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekMonday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2007);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)12);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)31);
+
+
+		// Set day of year for a non-leap year
+		dateTime.Set(1983, 1, 1, 0, 0, 0);
+		dateTime.SetParameter(kParameterDayOfYear, 365);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSaturday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1983);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)12);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)31);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)365);
+
+		dateTime.SetParameter(kParameterDayOfYear, 366); // intentional overflow
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekSunday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1984);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)1);
+
+
+		// Set day of year for a leap year
+		dateTime.Set(1984, 1, 1, 0, 0, 0);
+		dateTime.SetParameter(kParameterDayOfYear, 366);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekMonday);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)1984);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)12);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)31);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)366);
+
+
+		// Comparisons test
+		dateTime.Set(1866, 1, 2, 20, 10, 8);
+		dateTime2.Set(1866, 1, 2, 20, 10, 6);
+
+		EATEST_VERIFY(dateTime.Compare(dateTime2,true,true) == 1);   // >
+		EATEST_VERIFY(dateTime.Compare(dateTime2,true,false) == 0);  // ==
+		EATEST_VERIFY(dateTime.Compare(dateTime2,false,true) == 1);  // >
+
+		dateTime.Set(1866, 1, 1, 20, 10, 8);
+
+		EATEST_VERIFY(dateTime.Compare(dateTime2,true,true) == -1);   // <
+		EATEST_VERIFY(dateTime.Compare(dateTime2,true,false) == -1);  // <
+		EATEST_VERIFY(dateTime.Compare(dateTime2,false,true) == 1);   // >
+
+
+		// Arithmetic
+		dateTime.Set(2004, 12, 30, 0, 0, 0);
+		dateTime.AddTime(kParameterYear, -2);
+		dateTime.AddTime(kParameterMonth, 1);
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2003);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)30);
+
+		dateTime.AddTime(kParameterMonth, -11); // note that we don't have 30 days in Feb hence the date should spill into March
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2002);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)3);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)2);
+
+		dateTime.AddTime(kParameterMonth, 25); // add more than one year
+
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2004);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMonth) == (uint32_t)4);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)2);
+
+		dateTime.AddTime(kParameterDayOfMonth, 10);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfMonth) == (uint32_t)12);
+
+		dateTime.SetParameter(kParameterDayOfYear, 366);
+		dateTime.AddTime(kParameterDayOfYear, 40);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterYear) == (uint32_t)2005);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)40);
+
+		dateTime.SetParameter(kParameterDayOfWeek, kDayOfWeekMonday);
+		dateTime.AddTime(kParameterDayOfWeek, 3);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekThursday);
+
+		dateTime.SetParameter(kParameterHour, 10);
+		dateTime.AddTime(kParameterHour, 16);
+		dateTime.AddTime(kParameterHour, -1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterHour) == (uint32_t)1);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfWeek) == (uint32_t)kDayOfWeekFriday);
+
+		dateTime.AddTime(kParameterMinute, 125);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterHour) == (uint32_t)3);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMinute) == (uint32_t)5);
+
+		dateTime.AddTime(kParameterSecond, 65);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterMinute) == (uint32_t)6);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterSecond) == (uint32_t)5);
+
+		dateTime.SetParameter(kParameterDayOfYear, 100);
+		dateTime.AddTime(kParameterWeekOfYear, 2);
+		EATEST_VERIFY(dateTime.GetParameter(kParameterDayOfYear) == (uint32_t)114);
+	}
+
+	// Nanosecond precision
+	{
+		// DateTime(int64_t nSeconds, uint32_t nNanosecond = 0);
+		DateTime dtNS1(1000, 2000);
+		EATEST_VERIFY(dtNS1.GetSeconds()     == 1000);
+		EATEST_VERIFY(dtNS1.GetNanoseconds() == (1000 * EA::StdC::int128_t(1000000000)) + 2000);
+		dtNS1.SetNanoseconds(3000);
+		EATEST_VERIFY(dtNS1.GetSeconds()     == 0);
+		EATEST_VERIFY(dtNS1.GetNanoseconds() == 3000);
+
+		// DateTime(const int128_t& nanoseconds);
+		// EA::StdC::int128_t GetNanoseconds() const;
+		// void SetNanoseconds(const EA::StdC::int128_t& nanoseconds);
+		DateTime dtNS2;
+		EA::StdC::int128_t nsNow = dtNS2.GetNanoseconds();
+		DateTime dtNS3(nsNow);
+		EATEST_VERIFY(dtNS2 == dtNS3);
+
+		nsNow -= (INT64_C(1000000000) * 3600); // Subtrace one hour
+		dtNS2.SetNanoseconds(nsNow);
+		EATEST_VERIFY((dtNS2.GetSeconds() + 3600) == dtNS3.GetSeconds());
+
+		// void Set(uint32_t nYear, uint32_t nMonth,  uint32_t nDay, uint32_t nHour, 
+		//          uint32_t nMinute, uint32_t nSecond, uint32_t nNanosecond);
+		dtNS3.Set(dtNS2.GetParameter(kParameterYear), dtNS2.GetParameter(kParameterMonth),
+				  dtNS2.GetParameter(kParameterDayOfMonth), dtNS2.GetParameter(kParameterHour),
+				  dtNS2.GetParameter(kParameterMinute), dtNS2.GetParameter(kParameterSecond),
+				  dtNS2.GetParameter(kParameterNanosecond));
+
+		EATEST_VERIFY(dtNS2 == dtNS3);
+	}
+
+	// Millisecond precision
+	{
+		DateTime dtNS1(1);  // 1 second
+		EATEST_VERIFY(dtNS1.GetSeconds() == 1);
+		EATEST_VERIFY(dtNS1.GetMilliseconds() == 1000);
+
+		dtNS1.SetMilliseconds(8);
+		EATEST_VERIFY(dtNS1.GetNanoseconds() == 8000000);
+		EATEST_VERIFY(dtNS1.GetMilliseconds() == 8);
+		EATEST_VERIFY(dtNS1.GetSeconds() == 0);
+
+		dtNS1.SetNanoseconds(1);
+		EATEST_VERIFY(dtNS1.GetSeconds() == 0);
+		EATEST_VERIFY(dtNS1.GetMilliseconds() == 0);
+		EATEST_VERIFY(dtNS1.GetNanoseconds() == 1);
+
+		dtNS1.SetNanoseconds(8000006);
+		EATEST_VERIFY(dtNS1.GetSeconds() == 0);
+		EATEST_VERIFY(dtNS1.GetMilliseconds() == 8);
+		EATEST_VERIFY(dtNS1.GetNanoseconds() == 8000006);
+	}
+
+	// void DateTimeToFileTime(const DateTime& dateTime, _FILETIME& time);
+	// void FileTimeToDateTime(const _FILETIME& time, DateTime& dateTime);
+	// void DateTimeToSystemTime(const DateTime& dateTime, _SYSTEMTIME& time);
+	// void SystemTimeToDateTime(const _SYSTEMTIME& time, DateTime& dateTime);
+	#if defined(EA_PLATFORM_MICROSOFT)
+	{
+		DateTime    dateTime(1234, 5, 6, 7, 8, 9);
+		DateTime    dateTime2(6789, 6, 5, 4, 3, 2);
+		_FILETIME   fileTime;
+		_SYSTEMTIME systemTime;
+
+		// Our current test is feeble: it merely converts to FILETIME and 
+		// then back and verifies that the time is the same. 
+		dateTime.Set();
+
+		DateTimeToFileTime(dateTime, fileTime);
+		FileTimeToDateTime(fileTime, dateTime2);
+		EATEST_VERIFY(dateTime == dateTime2);
+
+		DateTimeToSystemTime(dateTime, systemTime);
+		SystemTimeToDateTime(systemTime, dateTime2);
+		EATEST_VERIFY(dateTime == dateTime2);
+	}
+	#endif
+
+	{
+		// http://pubs.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+		// http://msdn.microsoft.com/en-us/library/fe06s4ak%28v=VS.100%29.aspx
+
+		// Posix alternative formats:
+		// "Some conversion specifiers can be modified by the E or O modifier characters 
+		//  to indicate that an alternative format or specification should be used rather 
+		//  than the one normally used by the unmodified conversion specifier. If the 
+		//  alternative format or specification does not exist for the current locale, 
+		//  (see ERA in the XBD specification, Section 5.3.5) the behaviour will be as if 
+		//  the unmodified conversion specification were used."
+		//
+		// Microsoft alternative formats:
+		// Also, Microsoft (alternatively to Posix  E and O) supports using the # char 
+		// after % to indicate alternative behaviour as follows:
+		//     %#a, %#A, %#b, %#B, %#h, %#p, %#X, %#z, %#Z, %#%             # flag is ignored.
+		//     %#c                                                          Long date and time representation, appropriate for current locale. For example: "Tuesday, March 14, 1995, 12:41:29".
+		//     %#x                                                          Long date representation, appropriate to current locale. For example: "Tuesday, March 14, 1995".
+		//     %#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y   Remove leading zeros (if any).
+
+
+		// %a   Replaced by the locale's abbreviated weekday name. [ tm_wday]
+		// %A   Replaced by the locale's full weekday name. [ tm_wday]
+		// %b   Replaced by the locale's abbreviated month name. [ tm_mon]
+		// %B   Replaced by the locale's full month name. [ tm_mon]
+		// %c   Replaced by the locale's appropriate date and time representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+		// %C   Replaced by the year divided by 100 and truncated to an integer, as a decimal number [00,99]. [ tm_year]
+		// %d   Replaced by the day of the month as a decimal number [01,31]. [ tm_mday]
+		// %D   Equivalent to %m / %d / %y. [ tm_mon, tm_mday, tm_year]
+		// %e   Replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space. [ tm_mday]
+		// %F   Equivalent to %Y - %m - %d (the ISO 8601:2000 standard date format). [ tm_year, tm_mon, tm_mday]
+		// %g   Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]. [ tm_year, tm_wday, tm_yday]
+		// %G   Replaced by the week-based year (see below) as a decimal number (for example, 1977). [ tm_year, tm_wday, tm_yday]
+		// %h   Equivalent to %b. [ tm_mon]
+		// %H   Replaced by the hour (24-hour clock) as a decimal number [00,23]. [ tm_hour]
+		// %I   Replaced by the hour (12-hour clock) as a decimal number [01,12]. [ tm_hour]
+		// %j   Replaced by the day of the year as a decimal number [001,366]. [ tm_yday]
+		// %m   Replaced by the month as a decimal number [01,12]. [ tm_mon]
+		// %M   Replaced by the minute as a decimal number [00,59]. [ tm_min]
+		// %n   Replaced by a <newline>.
+		// %p   Replaced by the locale's equivalent of either a.m. or p.m. [ tm_hour]
+		// %r   Replaced by the time in a.m. and p.m. notation; [CX] [Option Start]  in the POSIX locale this shall be equivalent to %I : %M : %S %p. [Option End] [ tm_hour, tm_min, tm_sec]
+		// %R   Replaced by the time in 24-hour notation ( %H : %M ). [ tm_hour, tm_min]
+		// %S   Replaced by the second as a decimal number [00,60]. [ tm_sec]
+		// %t   Replaced by a <tab>.
+		// %T   Replaced by the time ( %H : %M : %S ). [ tm_hour, tm_min, tm_sec]
+		// %u   Replaced by the weekday as a decimal number [1,7], with 1 representing Monday. [ tm_wday]
+		// %U   Replaced by the week number of the year as a decimal number [00,53]. The first Sunday of January is the first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+		// %V   Replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. If the week containing 1 January has four or more days in the new year, then it is considered week 1. Otherwise, it is the last week of the previous year, and the next week is week 1. Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
+		// %w   Replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. [ tm_wday]
+		// %W   Replaced by the week number of the year as a decimal number [00,53]. The first Monday of January is the first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+		// %x   Replaced by the locale's appropriate date representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+		// %X   Replaced by the locale's appropriate time representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+		// %y   Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year]
+		// %Y   Replaced by the year as a decimal number (for example, 1997). [ tm_year]
+		// %z   Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no characters if no timezone is determinable. For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). [CX] [Option Start]  If tm_isdst is zero, the standard time offset is used. If tm_isdst is greater than zero, the daylight savings time offset is used. If tm_isdst is negative, no characters are returned. [Option End] [ tm_isdst]
+		// %Z   Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst]
+		// %%   Replaced by %. 
+
+		const size_t kBufferSize = 2048;
+		char*        pBuffer = new char[kBufferSize];
+		tm           tmValue1, tmValue2;
+		size_t       n;
+		char8_t*     p;
+
+		{
+			DateTime dt(1999, 12, 31, 23, 59, 58);
+
+			memset(&tmValue1, 0, sizeof(tmValue1));
+			memset(&tmValue2, 0, sizeof(tmValue2));
+			DateTimeToTm(dt, tmValue1);
+
+			// In our simplest test, we simply make a string with all formats and make sure it doesn't crash or hang.
+			Strftime(pBuffer, kBufferSize, "%a | %#a | %A | %#A | %b | %#b | %B | %#B | %c | %#c | %C | %d | %#d | %D | %e | %h | %H | %#H | %I | %#I | %j | %#j | %m | %#m | %M | %#M | %n | %p | %#p | %r | %R | %S | %#S | %t | %T | %u | %U | %#U | %V | %w | %#w | %W | %#W | %x | %#x | %X | %#X | %y | %#y | %Y | %#Y | %Z | %#Z | %% | %#%", &tmValue1, NULL);
+
+			// Generate a string buffer and parse it. The format string differs from the above as some specifiers
+			// like %u and %W aren't supported by Strptime.
+			Strftime(pBuffer, kBufferSize, "%a | %#a | %A | %#A | %b | %#b | %B | %#B | %c | %#c | %C | %d | %#d | %D | %e | %h | %H | %#H | %I | %#I | %j | %#j | %m | %#m | %M | %#M | %n | %p | %#p | %r | %R | %S | %#S | %t | %T | %w | %#w | %x | %#x | %X | %#X | %y | %#y | %Y | %#Y | %% | %#%", &tmValue1, NULL);
+			char *ptr = Strptime(pBuffer, "%a | %#a | %A | %#A | %b | %#b | %B | %#B | %c | %#c | %C | %d | %#d | %D | %e | %h | %H | %#H | %I | %#I | %j | %#j | %m | %#m | %M | %#M | %n | %p | %#p | %r | %R | %S | %#S | %t | %T | %w | %#w | %x | %#x | %X | %#X | %y | %#y | %Y | %#Y | %% | %#%", &tmValue2, NULL);
+
+			EATEST_VERIFY(ptr != NULL);
+			EATEST_VERIFY(tmValue1.tm_sec   == tmValue2.tm_sec);
+			EATEST_VERIFY(tmValue1.tm_min   == tmValue2.tm_min);
+			EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+			EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+			EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+			EATEST_VERIFY(tmValue1.tm_wday  == tmValue2.tm_wday);
+			EATEST_VERIFY(tmValue1.tm_yday  == tmValue2.tm_yday);
+			EATEST_VERIFY(tmValue1.tm_isdst == tmValue2.tm_isdst);
+		}
+
+		{
+			DateTime dt(2000, 1, 1, 0, 0, 0);
+
+			{
+				// %t   Replaced by a <tab>.
+				// %%   Replaced by %. 
+				// %n   Replaced by a <newline>.
+
+				memcpy(&tmValue2, &tmValue1, sizeof(tmValue2));
+
+				const char8_t* kFormat = "%t %t%%%n%n%%";
+				const char8_t* kResult = "\t \t%\n\n%";
+				n = Strftime(pBuffer, kBufferSize, kFormat, &tmValue1, NULL);
+				EATEST_VERIFY((n == Strlen(kResult)) && (Strcmp(pBuffer, kResult) == 0));
+				EATEST_VERIFY(memcmp(&tmValue1, &tmValue2, sizeof(tmValue2)) == 0);   // Verify that tmValue1 was not written to.
+				p = Strptime(pBuffer, kFormat, &tmValue2, NULL);
+				EATEST_VERIFY(p == (pBuffer + n));
+				EATEST_VERIFY(memcmp(&tmValue1, &tmValue2, sizeof(tmValue2)) == 0);   // Verify that tmValue2 was not written to.
+			}
+
+			{
+				// %a   Replaced by the locale's abbreviated weekday name. [ tm_wday]
+				// %A   Replaced by the locale's full weekday name. [ tm_wday]
+
+				const char8_t* kExpectedResults[7] = 
+				{
+					"Sun | Sun | Sunday | Sunday",
+					"Mon | Mon | Monday | Monday",
+					"Tue | Tue | Tuesday | Tuesday",
+					"Wed | Wed | Wednesday | Wednesday",
+					"Thu | Thu | Thursday | Thursday",
+					"Fri | Fri | Friday | Friday",
+					"Sat | Sat | Saturday | Saturday",
+				};
+
+				for(uint32_t d = kDayOfWeekSunday; d <= kDayOfWeekSaturday; d++)
+				{
+					dt.SetParameter(kParameterDayOfWeek, d);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%a | %#a | %A | %#A", &tmValue1, NULL);
+					EA_COMPILETIME_ASSERT(kDayOfWeekSunday == 1);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[d - 1]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[d - 1]) == 0);  // d-1 because kDayOfWeekSunday is 1 and not 0.
+
+					// This isn't the right way to test this; we need to test each format in turn and not all of them at once.
+					p = Strptime(pBuffer, "%a | %#a | %A | %#A", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_wday  == tmValue2.tm_wday);
+				}
+			}
+
+			{
+				// %b   Replaced by the locale's abbreviated month name. [ tm_mon]
+				// %B   Replaced by the locale's full month name. [ tm_mon]
+				// %h   Equivalent to %b. [ tm_mon]
+				// %m   Replaced by the month as a decimal number [01,12]. [ tm_mon]
+
+				const char8_t* kExpectedResults[12] = 
+				{
+					"Jan | Jan | January | January | Jan | Jan | 01 | 1",
+					"Feb | Feb | February | February | Feb | Feb | 02 | 2",
+					"Mar | Mar | March | March | Mar | Mar | 03 | 3",
+					"Apr | Apr | April | April | Apr | Apr | 04 | 4",
+					"May | May | May | May | May | May | 05 | 5",
+					"Jun | Jun | June | June | Jun | Jun | 06 | 6",
+					"Jul | Jul | July | July | Jul | Jul | 07 | 7",
+					"Aug | Aug | August | August | Aug | Aug | 08 | 8",
+					"Sep | Sep | September | September | Sep | Sep | 09 | 9",
+					"Oct | Oct | October | October | Oct | Oct | 10 | 10",
+					"Nov | Nov | November | November | Nov | Nov | 11 | 11",
+					"Dec | Dec | December | December | Dec | Dec | 12 | 12",
+				};
+
+				for(uint32_t m = kMonthJanuary; m <= kMonthDecember; m++)
+				{
+					dt.SetParameter(kParameterMonth, m);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%b | %#b | %B | %#B | %h | %#h | %m | %#m", &tmValue1, NULL);
+					EA_COMPILETIME_ASSERT(kMonthJanuary == 1);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[m - 1]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[m - 1]) == 0); // m-1 because kMonthJanuary is 1 and not 0.
+
+					// This isn't the right way to test this; we need to test each format in turn and not all of them at once.
+					p = Strptime(pBuffer, "%b | %#b | %B | %#B | %h | %#h | %m | %#m", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+				}
+			}
+
+			{
+				// %c   Replaced by the locale's appropriate date and time representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+				dt.SetParameter(kParameterYear, 2012);
+				dt.SetParameter(kParameterMonth, 1);
+				dt.SetParameter(kParameterDayOfMonth, 9);
+				dt.SetParameter(kParameterHour, 13);
+				dt.SetParameter(kParameterMinute, 24);
+				dt.SetParameter(kParameterSecond, 5);
+				DateTimeToTm(dt, tmValue1);
+
+				const int kResultSize = 512;
+				char kExpectedResult[kResultSize] = { '\0' };
+
+				#if defined (EA_PLATFORM_WINDOWS)
+					char kLongMonth[32];
+					char kLongDayOfWeek[32];
+					char kShortMonth[32];
+					char kShortDayOfWeek[32];
+					char shortDateFormat[80];
+					char longDateFormat[80];
+					char timeFormat[80];
+
+					char* dateFormat[2] = 
+					{
+						&shortDateFormat[0],
+						&longDateFormat[0],
+					};
+
+					char* ptr;
+					size_t i = 0;
+					kExpectedResult[0] = '\0';
+
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SSHORTDATE, shortDateFormat, sizeof(shortDateFormat)/sizeof(shortDateFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SLONGDATE, longDateFormat, sizeof(longDateFormat)/sizeof(longDateFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_STIMEFORMAT, timeFormat, sizeof(timeFormat)/sizeof(timeFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SABBREVDAYNAME1, kShortDayOfWeek, sizeof(kShortDayOfWeek)/sizeof(kShortDayOfWeek[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SABBREVMONTHNAME1, kShortMonth, sizeof(kShortMonth)/sizeof(kShortMonth[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SDAYNAME1, kLongDayOfWeek, sizeof(kLongDayOfWeek)/sizeof(kLongDayOfWeek[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SMONTHNAME1, kLongMonth, sizeof(kLongMonth)/sizeof(kLongMonth[0])) != 0);
+
+					char tmp[kResultSize];
+					for(int j = 0; j < 2; j++)
+					{
+						i = 0;
+						pBuffer[0] = '\0';
+
+						strncpy(tmp, dateFormat[j], kResultSize);
+
+						ptr = strtok(tmp, "/, -");
+						while(ptr != 0)
+						{
+							if(strncmp(ptr, "d", strlen(ptr)) == 0)
+								strncat(pBuffer, "9", kBufferSize);
+							else if(strncmp(ptr, "dd", strlen(ptr)) == 0)
+								strncat(pBuffer, "09", kBufferSize);
+							else if(strncmp(ptr, "ddd", strlen(ptr)) == 0)
+								strncat(pBuffer, kShortDayOfWeek, kBufferSize);
+							else if(strncmp(ptr, "dddd", strlen(ptr)) == 0)
+								strncat(pBuffer, kLongDayOfWeek, kBufferSize);
+							else if(strncmp(ptr, "M", strlen(ptr)) == 0)
+								strncat(pBuffer, "1", kBufferSize);
+							else if(strncmp(ptr, "MM", strlen(ptr)) == 0)
+								strncat(pBuffer, "01", kBufferSize);
+							else if(strncmp(ptr, "MMM", strlen(ptr)) == 0)
+								strncat(pBuffer, kShortMonth, kBufferSize);
+							else if(strncmp(ptr, "MMMM", strlen(ptr)) == 0)
+								strncat(pBuffer, kLongMonth, kBufferSize);
+							else if(strncmp(ptr, "y", strlen(ptr)) == 0)
+								strncat(pBuffer, "2", kBufferSize);
+							else if(strncmp(ptr, "yy", strlen(ptr)) == 0)
+								strncat(pBuffer, "12", kBufferSize);
+							else if(strncmp(ptr, "yyyy", strlen(ptr)) == 0)
+								strncat(pBuffer, "2012", kBufferSize);
+							else if(strncmp(ptr, "yyyyy", strlen(ptr)) == 0)
+								strncat(pBuffer, "2012", kBufferSize);
+							else
+								EA_FAIL_M("Unsupported date format");
+
+							i += strlen(ptr);
+							size_t separators = strcspn(&dateFormat[j][i], "dmyM");
+							strncat(pBuffer, &dateFormat[j][i], separators);
+							i += separators;
+
+							ptr = strtok(NULL, "/, -");
+						}
+
+						strncpy(kExpectedResult, pBuffer, kResultSize);
+
+						pBuffer[0] = '\0';
+						strncpy(tmp, timeFormat, kResultSize);
+						ptr = strtok(tmp, ": ");
+						i = 0;
+						while(ptr != 0)
+						{
+							if(strncmp(ptr, "h", strlen(ptr)) == 0)
+								strncat(pBuffer, "1", kBufferSize);
+							else if(strncmp(ptr, "hh", strlen(ptr)) == 0)
+								strncat(pBuffer, "01", kBufferSize);
+							else if(strncmp(ptr, "H", strlen(ptr)) == 0)
+								strncat(pBuffer, "13", kBufferSize);
+							else if(strncmp(ptr, "HH", strlen(ptr)) == 0)
+								strncat(pBuffer, "13", kBufferSize);
+							else if(strncmp(ptr, "m", strlen(ptr)) == 0)
+								strncat(pBuffer, "24", kBufferSize);
+							else if(strncmp(ptr, "mm", strlen(ptr)) == 0)
+								strncat(pBuffer, "24", kBufferSize);
+							else if(strncmp(ptr, "s", strlen(ptr)) == 0)
+								strncat(pBuffer, "5", kBufferSize);
+							else if(strncmp(ptr, "ss", strlen(ptr)) == 0)
+								strncat(pBuffer, "05", kBufferSize);
+							else if(strncmp(ptr, "t", strlen(ptr)) == 0)
+								strncat(pBuffer, "P", kBufferSize);
+							else if(strncmp(ptr, "tt", strlen(ptr)) == 0)
+								strncat(pBuffer, "PM", kBufferSize);
+							else
+								EA_FAIL_M("Unsupported date format");
+
+							i+=strlen(ptr);
+							size_t separators = strcspn(&timeFormat[i], "hHmst");
+							strncat(pBuffer, &timeFormat[i], separators);
+							i += separators;
+
+							ptr = strtok(NULL, ": ");
+						}
+						strncat(kExpectedResult, " ", kResultSize);
+						strncat(kExpectedResult, pBuffer, kResultSize);
+
+						if(j == 0)
+						{
+							n = Strftime(pBuffer, kBufferSize, "%#c", &tmValue1, NULL);
+							EATEST_VERIFY(n == Strlen(kExpectedResult));
+							EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+							ptr = Strptime(pBuffer, "%#c", &tmValue2, NULL);
+							EATEST_VERIFY(ptr == (pBuffer + n));
+							EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+							EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+							EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+							EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+							EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+							EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+						}
+						else
+						{
+							n = Strftime(pBuffer, kBufferSize, "%c", &tmValue1, NULL);
+							EATEST_VERIFY(n == Strlen(kExpectedResult));
+							EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+							ptr = Strptime(pBuffer, "%c", &tmValue2, NULL);
+							EATEST_VERIFY(ptr == (pBuffer + n));
+							EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+							EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+							EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+							EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+							EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+							EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+						}
+					}
+
+				#else
+					char* ptr;
+
+					//Takes on the form of %a %b %d %H:%M:%S %Y
+					strncpy(kExpectedResult, "Mon Jan 09 13:24:05 2012", kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%c", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%c", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+					EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+
+					strncpy(kExpectedResult, "Mon Jan 9 13:24:5 2012", kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%#c", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%#c", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+					EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+
+				#endif
+
+			}
+
+			{
+				//strftime %C   Replaced by the year divided by 100 and truncated to an integer, as a decimal number [00,99]. [ tm_year]
+				//strptime %C   The century number [00,99]; leading zeros are permitted but not required.
+
+				const uint32_t years[5] = 
+				{
+					1999,
+					2000,
+					999,
+					99,
+					2100,
+				};
+
+				const char8_t* kExpectedResults[5] = 
+				{
+					"19 | 19",
+					"20 | 20",
+					"09 | 9",
+					"00 | 0",
+					"21 | 21",
+				};
+
+				for(uint32_t y =0; y < sizeof(years)/sizeof(uint32_t); y++)
+				{
+					dt.SetParameter(kParameterYear, years[y]);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%C | %#C", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[y]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[y]) == 0);
+
+					p = Strptime(pBuffer, "%C | %#C", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					//Divide by 100 to find number of centuries past 1900, add 19 for 1900 itself, then multiply by 100 to give the proper year
+					EATEST_VERIFY(((tmValue1.tm_year+1900)/100)*100 == tmValue2.tm_year);
+				}
+			}
+
+			{
+				// %d   Replaced by the day of the month as a decimal number [01,31]. [ tm_mday]
+				// %e   Replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space. [ tm_mday]
+				char kExpectedResult[32];
+
+				for(uint32_t m = kDayOfMonthMin; m <= kDayOfMonthMax; m++)
+				{
+					sprintf(kExpectedResult, "%02u | %u | %2u", m, m, m);
+					dt.SetParameter(kParameterDayOfMonth, m);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%d | %#d | %e", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					p = Strptime(pBuffer, "%d | %#d | %e", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+				}
+			}
+
+			{
+				// %D   Equivalent to %m/%d/%y. [ tm_mon, tm_mday, tm_year]
+				const char8_t* kExpectedResults[12] = 
+				{
+					"01/01/66",
+					"02/02/67",
+					"03/03/68",
+					"04/04/69",
+					"05/05/70",
+					"06/06/71",
+					"07/07/72",
+					"08/08/73",
+					"09/09/74",
+					"10/10/75",
+					"11/11/76",
+					"12/12/77",
+				};
+
+				const uint32_t yearOffset = 2065;
+				for(uint32_t m = kMonthJanuary; m <= kMonthDecember; m++)
+				{
+					dt.SetParameter(kParameterDayOfMonth, m);
+					dt.SetParameter(kParameterMonth, m);
+
+					//strptime %y - The year within century. When a century is not otherwise specified, values in the range [69,99] shall refer to years 1969 to 1999 
+					//inclusive, and values in the range [00,68] shall refer to years 2000 to 2068 inclusive; leading zeros shall be permitted but shall not be required.
+
+					dt.SetParameter(kParameterYear, m + yearOffset); // Add offset ( > 2068 - 12) so the tmValue2.tm_year value changes ranges from [2000, 2068] to [1969, 1999] when using strptime
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%D", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[m - 1]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[m - 1]) == 0); // m-1 because kMonthJanuary is 1 and not 0.
+
+					p = Strptime(pBuffer, "%D", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+					EATEST_VERIFY(tmValue1.tm_mon   == tmValue2.tm_mon);
+
+					//Check which range the year will be in
+					if(m + yearOffset <= 2068)
+					{
+						//tm-year range from [2000, 2068], inclusively
+						EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+					}
+					else
+					{
+						//tm-year range from [1969, 1999], inclusively
+						EATEST_VERIFY((tmValue1.tm_year - 100) == tmValue2.tm_year); // We subtract the offset -> (2000-1900)
+					}
+				}
+			}
+
+			{
+				// %F   Equivalent to %Y - %m - %d (the ISO 8601:2000 standard date format). [ tm_year, tm_mon, tm_mday]
+				const char8_t* kExpectedResults[12] = 
+				{
+					"2001-01-01",
+					"2002-02-02",
+					"2003-03-03",
+					"2004-04-04",
+					"2005-05-05",
+					"2006-06-06",
+					"2007-07-07",
+					"2008-08-08",
+					"2009-09-09",
+					"2010-10-10",
+					"2011-11-11",
+					"2012-12-12",
+				};
+
+				const uint32_t yearOffset = 2000;
+				for(uint32_t m = kMonthJanuary; m <= kMonthDecember; m++)
+				{
+					dt.SetParameter(kParameterDayOfMonth, m);
+					dt.SetParameter(kParameterMonth, m);
+					dt.SetParameter(kParameterYear, m + yearOffset); //Add offset to make the years more legible/realistic
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%F", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[m - 1]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[m - 1]) == 0);
+				}
+			}
+
+			{
+				//Unsupported as of yet - we currently maintain no concept of "week number of year"
+				// %g   Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]. [ tm_year, tm_wday, tm_yday]
+				// %G   Replaced by the week-based year (see below) as a decimal number (for example, 1977). [ tm_year, tm_wday, tm_yday]
+			}
+
+			{
+				// %H   Replaced by the hour (24-hour clock) as a decimal number [00,23]. [ tm_hour]
+				// %I   Replaced by the hour (12-hour clock) as a decimal number [01,12]. [ tm_hour]
+				// %M   Replaced by the minute as a decimal number [00,59]. [ tm_min]
+				// %S   Replaced by the second as a decimal number [00,60]. [ tm_sec]
+
+				char kExpectedResult[32];
+				const int32_t amToPmDifference = 12;
+				for(int32_t t = 1; t < kHoursPerDay; t++)
+				{
+					sprintf(kExpectedResult, "%02d | %d | %02d | %d | %02d | %d", t, t, t, t, t, t);
+					dt.SetParameter(kParameterHour,   (uint32_t)t);
+					dt.SetParameter(kParameterMinute, (uint32_t)t);
+					dt.SetParameter(kParameterSecond, (uint32_t)t);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%H | %#H | %M | %#M | %S | %#S", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					p = Strptime(pBuffer, "%H | %#H | %M | %#M | %S | %#S", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min   == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec   == tmValue2.tm_sec);
+
+					if(t <= amToPmDifference)
+					{
+						sprintf(kExpectedResult, "%02d | %d | %02d | %d | %02d | %d", t, t, t, t, t, t);
+						n = Strftime(pBuffer, kBufferSize, "%I | %#I | %M | %#M | %S | %#S", &tmValue1, NULL);
+						EATEST_VERIFY(n == Strlen(kExpectedResult));
+						EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+						p = Strptime(pBuffer, "%I | %#I | %M | %#M | %S | %#S", &tmValue2, NULL);
+						EATEST_VERIFY(p == (pBuffer + n));
+						EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+						EATEST_VERIFY(tmValue1.tm_min   == tmValue2.tm_min);
+						EATEST_VERIFY(tmValue1.tm_sec   == tmValue2.tm_sec);
+					}
+					else
+					{
+						sprintf(kExpectedResult, "%02d | %d | %02d | %d | %02d | %d", t - amToPmDifference, t - amToPmDifference, t, t, t, t);//Adjust for AM to PM rollover
+						n = Strftime(pBuffer, kBufferSize, "%I | %#I | %M | %#M | %S | %#S", &tmValue1, NULL);
+						EATEST_VERIFY(n == Strlen(kExpectedResult));
+						EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+						p = Strptime(pBuffer, "%I | %#I | %M | %#M | %S | %#S", &tmValue2, NULL);
+						EATEST_VERIFY(p == (pBuffer + n));
+						EATEST_VERIFY((tmValue1.tm_hour - amToPmDifference)  == tmValue2.tm_hour);
+						EATEST_VERIFY(tmValue1.tm_min                        == tmValue2.tm_min);
+						EATEST_VERIFY(tmValue1.tm_sec                        == tmValue2.tm_sec);
+					}
+				}
+			}
+
+			{
+				// %j   Replaced by the day of the year as a decimal number [001,366]. [ tm_yday]
+				const char8_t* kExpectedResults[4] = 
+				{
+					"366 | 366",
+					"365 | 365",
+					"001 | 1",
+					"099 | 99",
+				};
+
+				for(uint32_t d = 0; d < 4; d++)
+				{
+					switch(d)
+					{
+					case 0://Leap Year End
+						dt.SetParameter(kParameterDayOfMonth, 31);
+						dt.SetParameter(kParameterMonth, 12);
+						dt.SetParameter(kParameterYear, 2012);
+						break;
+					case 1://Regular Year End
+						dt.SetParameter(kParameterDayOfMonth, 31);
+						dt.SetParameter(kParameterMonth, 12);
+						dt.SetParameter(kParameterYear, 2011);
+						break;
+					case 2://Year Beginning
+						dt.SetParameter(kParameterDayOfMonth, 1);
+						dt.SetParameter(kParameterMonth, 1);
+						dt.SetParameter(kParameterYear, 1970);
+						break;
+					case 3://Mid-Regular-Year, two significant digits
+						dt.SetParameter(kParameterDayOfMonth, 9);
+						dt.SetParameter(kParameterMonth, 4);
+						dt.SetParameter(kParameterYear, 2011);
+						break;
+					}
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%j | %#j", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[d]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[d]) == 0);
+
+					p = Strptime(pBuffer, "%j | %#j", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_yday  == tmValue2.tm_yday);
+				}
+			}
+
+			{
+				// %p   Replaced by the locale's equivalent of either a.m. or p.m. [ tm_hour]
+				// %r   Replaced by the time in a.m. and p.m. notation; [CX] [Option Start]  in the POSIX locale this shall be equivalent to %I : %M : %S %p. [Option End] [ tm_hour, tm_min, tm_sec]
+				// %R   Replaced by the time in 24-hour notation ( %H : %M ). [ tm_hour, tm_min]
+				// %T   Replaced by the time ( %H : %M : %S ). [ tm_hour, tm_min, tm_sec]
+				const char8_t* kExpectedResults[8] = 
+				{
+					"AM", "10:11:12 AM", "10:11", "10:11:12",
+					"PM", "01:02:03 PM", "13:02", "13:02:03",
+				};
+
+				for(int32_t d = 0; d < 2; d++)
+				{
+					switch(d)
+					{
+					case 0://AM time leading zeroes
+						dt.SetParameter(kParameterHour, 10);
+						dt.SetParameter(kParameterMinute, 11);
+						dt.SetParameter(kParameterSecond, 12);
+						break;
+					case 1://PM time
+						dt.SetParameter(kParameterHour, 13);
+						dt.SetParameter(kParameterMinute, 2);
+						dt.SetParameter(kParameterSecond, 3);
+						break;
+					}
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%p", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[4*d]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[4*d]) == 0);
+
+					int32_t oldHour = tmValue2.tm_hour;
+					p = Strptime(pBuffer, "%p", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY((oldHour + (12*d)) == tmValue2.tm_hour);
+
+					n = Strftime(pBuffer, kBufferSize, "%r", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[4*d+1]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[4*d+1]) == 0);
+
+					p = Strptime(pBuffer, "%r", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+
+					n = Strftime(pBuffer, kBufferSize, "%R", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[4*d+2]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[4*d+2]) == 0);
+
+					p = Strptime(pBuffer, "%R", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+
+					n = Strftime(pBuffer, kBufferSize, "%T", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[4*d+3]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[4*d+3]) == 0);
+
+					p = Strptime(pBuffer, "%T", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+				}
+			}
+
+			{
+				// %u   Replaced by the weekday as a decimal number [1,7], with 1 representing Monday. [ tm_wday]
+				// %w   Replaced by the weekday as a decimal number [0,6], with 0 representing Sunday. [ tm_wday]
+
+				for(uint32_t d = 1; d <= kDaysPerWeek; d++)
+				{
+					char kExpectedResult[8];
+					Snprintf(kExpectedResult, 8, "%d", d-1);
+					dt.SetParameter(kParameterDayOfWeek, d);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%w", &tmValue1, NULL);
+					EATEST_VERIFY(kDayOfWeekSunday == 1);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					p = Strptime(pBuffer, "%w", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_wday  == tmValue2.tm_wday);
+
+					Snprintf(kExpectedResult, 8, "%d", ((d+5)%7) + 1); //((d+5)%7) + 1) lets wrap around in the range [1,7]
+					n = Strftime(pBuffer, kBufferSize, "%u", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+				}
+			}
+
+			{
+				//Unsupported as of yet - we currently maintain no concept of "week number of year"
+				// %U   Replaced by the week number of the year as a decimal number [00,53]. The first Sunday of January is the first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+				// %V   Replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. If the week containing 1 January has four or more days in the new year, then it is considered week 1. Otherwise, it is the last week of the previous year, and the next week is week 1. Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
+				// %W   Replaced by the week number of the year as a decimal number [00,53]. The first Monday of January is the first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+			}
+
+			{
+				// %x   Replaced by the locale's appropriate date representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+				// %X   Replaced by the locale's appropriate time representation. (See the Base Definitions volume of IEEE Std 1003.1-2001, <time.h>.)
+
+				dt.SetParameter(kParameterYear, 2012);
+				dt.SetParameter(kParameterMonth, 1);
+				dt.SetParameter(kParameterDayOfMonth, 9);
+				dt.SetParameter(kParameterHour, 13);
+				dt.SetParameter(kParameterMinute, 24);
+				dt.SetParameter(kParameterSecond, 5);
+				DateTimeToTm(dt, tmValue1);
+
+				const int kResultSize = 512;
+				char kExpectedResult[kResultSize] = { '\0' };
+
+				#if defined (EA_PLATFORM_WINDOWS)
+					char kLongMonth[32];
+					char kLongDayOfWeek[32];
+					char kShortMonth[32];
+					char kShortDayOfWeek[32];
+					char shortDateFormat[80];
+					char longDateFormat[80];
+					char timeFormat[80];
+
+					char* dateFormat[2] = 
+					{
+						&shortDateFormat[0],
+						&longDateFormat[0],
+					};
+
+					char* ptr;
+					size_t i = 0;
+					kExpectedResult[0] = '\0';
+
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SSHORTDATE, shortDateFormat, sizeof(shortDateFormat)/sizeof(shortDateFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SLONGDATE, longDateFormat, sizeof(longDateFormat)/sizeof(longDateFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_STIMEFORMAT, timeFormat, sizeof(timeFormat)/sizeof(timeFormat[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SABBREVDAYNAME1, kShortDayOfWeek, sizeof(kShortDayOfWeek)/sizeof(kShortDayOfWeek[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SABBREVMONTHNAME1, kShortMonth, sizeof(kShortMonth)/sizeof(kShortMonth[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SDAYNAME1, kLongDayOfWeek, sizeof(kLongDayOfWeek)/sizeof(kLongDayOfWeek[0])) != 0);
+					EATEST_VERIFY(GetLocaleInfoHelper(LOCALE_SMONTHNAME1, kLongMonth, sizeof(kLongMonth)/sizeof(kLongMonth[0])) != 0);
+
+					char tmp[kResultSize];
+					for(int j = 0; j < 2; j++)
+					{
+						i = 0;
+						pBuffer[0] = '\0';
+
+						strncpy(tmp, dateFormat[j], kResultSize);
+
+						ptr = strtok(tmp, "/, -");
+						while(ptr != 0)
+						{
+							if(strncmp(ptr, "d", strlen(ptr)) == 0)
+								strncat(pBuffer, "9", kBufferSize);
+							else if(strncmp(ptr, "dd", strlen(ptr)) == 0)
+								strncat(pBuffer, "09", kBufferSize);
+							else if(strncmp(ptr, "ddd", strlen(ptr)) == 0)
+								strncat(pBuffer, kShortDayOfWeek, kBufferSize);
+							else if(strncmp(ptr, "dddd", strlen(ptr)) == 0)
+								strncat(pBuffer, kLongDayOfWeek, kBufferSize);
+							else if(strncmp(ptr, "M", strlen(ptr)) == 0)
+								strncat(pBuffer, "1", kBufferSize);
+							else if(strncmp(ptr, "MM", strlen(ptr)) == 0)
+								strncat(pBuffer, "01", kBufferSize);
+							else if(strncmp(ptr, "MMM", strlen(ptr)) == 0)
+								strncat(pBuffer, kShortMonth, kBufferSize);
+							else if(strncmp(ptr, "MMMM", strlen(ptr)) == 0)
+								strncat(pBuffer, kLongMonth, kBufferSize);
+							else if(strncmp(ptr, "y", strlen(ptr)) == 0)
+								strncat(pBuffer, "2", kBufferSize);
+							else if(strncmp(ptr, "yy", strlen(ptr)) == 0)
+								strncat(pBuffer, "12", kBufferSize);
+							else if(strncmp(ptr, "yyyy", strlen(ptr)) == 0)
+								strncat(pBuffer, "2012", kBufferSize);
+							else if(strncmp(ptr, "yyyyy", strlen(ptr)) == 0)
+								strncat(pBuffer, "2012", kBufferSize);
+							else
+								EA_FAIL_M("Unsupported date format");
+
+							i += strlen(ptr);
+							size_t separators = strcspn(&dateFormat[j][i], "dmyM");
+							strncat(pBuffer, &dateFormat[j][i], separators);
+							i += separators;
+
+							ptr = strtok(NULL, "/, -");
+						}
+						strncpy(kExpectedResult, pBuffer, kResultSize);
+
+						if(j == 0)
+						{
+							n = Strftime(pBuffer, kBufferSize, "%#x", &tmValue1, NULL);
+							EATEST_VERIFY(n == Strlen(kExpectedResult));
+							EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+							ptr = Strptime(pBuffer, "%#x", &tmValue2, NULL);
+							EATEST_VERIFY(ptr == (pBuffer + n));
+							EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+							EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+							EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+						}
+						else
+						{
+							n = Strftime(pBuffer, kBufferSize, "%x", &tmValue1, NULL);
+							EATEST_VERIFY(n == Strlen(kExpectedResult));
+							EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+						}
+					}
+
+					pBuffer[0] = '\0';
+					strncpy(tmp, timeFormat, kResultSize);
+					ptr = strtok(tmp, ": ");
+					i = 0;
+					while(ptr != 0)
+					{
+						if(strncmp(ptr, "h", strlen(ptr)) == 0)
+							strncat(pBuffer, "1", kBufferSize);
+						else if(strncmp(ptr, "hh", strlen(ptr)) == 0)
+							strncat(pBuffer, "01", kBufferSize);
+						else if(strncmp(ptr, "H", strlen(ptr)) == 0)
+							strncat(pBuffer, "13", kBufferSize);
+						else if(strncmp(ptr, "HH", strlen(ptr)) == 0)
+							strncat(pBuffer, "13", kBufferSize);
+						else if(strncmp(ptr, "m", strlen(ptr)) == 0)
+							strncat(pBuffer, "24", kBufferSize);
+						else if(strncmp(ptr, "mm", strlen(ptr)) == 0)
+							strncat(pBuffer, "24", kBufferSize);
+						else if(strncmp(ptr, "s", strlen(ptr)) == 0)
+							strncat(pBuffer, "5", kBufferSize);
+						else if(strncmp(ptr, "ss", strlen(ptr)) == 0)
+							strncat(pBuffer, "05", kBufferSize);
+						else if(strncmp(ptr, "t", strlen(ptr)) == 0)
+							strncat(pBuffer, "P", kBufferSize);
+						else if(strncmp(ptr, "tt", strlen(ptr)) == 0)
+							strncat(pBuffer, "PM", kBufferSize);
+						else
+							EA_FAIL_M("Unsupported date format");
+
+						i+=strlen(ptr);
+						size_t separators = strcspn(&timeFormat[i], "hHmst");
+						strncat(pBuffer, &timeFormat[i], separators);
+						i += separators;
+
+						ptr = strtok(NULL, ": ");
+					}
+
+					strncpy(kExpectedResult, pBuffer, kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%X", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%X", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);
+				#else
+					char* ptr;
+
+					strncpy(kExpectedResult, "01/09/12", kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%x", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%x", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+					EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+
+					strncpy(kExpectedResult, "1/9/12", kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%#x", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%#x", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+					EATEST_VERIFY(tmValue1.tm_mon  == tmValue2.tm_mon);
+					EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+
+					strncpy(kExpectedResult, "13:24:05", kResultSize);
+					n = Strftime(pBuffer, kBufferSize, "%X", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResult));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResult) == 0);
+
+					ptr = Strptime(pBuffer, "%X", &tmValue2, NULL);
+					EATEST_VERIFY(ptr == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+					EATEST_VERIFY(tmValue1.tm_min  == tmValue2.tm_min);
+					EATEST_VERIFY(tmValue1.tm_sec  == tmValue2.tm_sec);    
+
+				#endif
+
+			}
+
+			{
+				// %y   Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year]
+				// %Y   Replaced by the year as a decimal number (for example, 1997). [ tm_year]
+				const uint32_t years[5] = 
+				{
+					2000,
+					2001,
+					1999,
+					2068,
+					1969,
+				};
+
+				const char8_t* kExpectedResults[5] = 
+				{
+					"00 | 0 | 2000",
+					"01 | 1 | 2001",
+					"99 | 99 | 1999",
+					"68 | 68 | 2068",
+					"69 | 69 | 1969",
+				};
+
+				for(uint32_t y = 0; y < 5; y++)
+				{
+					dt.SetParameter(kParameterYear, years[y]);
+					DateTimeToTm(dt, tmValue1);
+
+					n = Strftime(pBuffer, kBufferSize, "%y | %#y | %Y", &tmValue1, NULL);
+					EATEST_VERIFY(n == Strlen(kExpectedResults[y]));
+					EATEST_VERIFY(Strcmp(pBuffer, kExpectedResults[y]) == 0);
+
+					p = Strptime(pBuffer, "%y | %#y | %Y", &tmValue2, NULL);
+					EATEST_VERIFY(p == (pBuffer + n));
+					EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+				}
+			}
+
+			{
+				// %z   Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no characters if no timezone is determinable. For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). [CX] [Option Start]  If tm_isdst is zero, the standard time offset is used. If tm_isdst is greater than zero, the daylight savings time offset is used. If tm_isdst is negative, no characters are returned. [Option End] [ tm_isdst]
+				// %Z   Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst]
+				int  tzBias = (int)GetTimeZoneBias();  // tzBias will be a negative number in the United States.
+				int  hour   = (tzBias / 3600);
+				int  min    = (tzBias - (hour * 3600)) / 60;
+				char kExpectedResult[32];
+				char timeZoneName[EA::StdC::kTimeZoneNameCapacity];
+
+				EA::StdC::GetTimeZoneName(timeZoneName, tmValue1.tm_isdst != 0);
+				Snprintf(kExpectedResult, 32, "%+03d%02d | %s", hour, min, timeZoneName);
+				DateTimeToTm(dt, tmValue1);
+
+				n = Strftime(pBuffer, kBufferSize, "%z | %Z", &tmValue1, NULL);
+				EATEST_VERIFY(n == Strlen(kExpectedResult));
+				EATEST_VERIFY_F(Strcmp(pBuffer, kExpectedResult) == 0, "TestDateTime Strftime(%z %Z) failure: expected: %s, actual: %s", kExpectedResult, pBuffer);
+			}
+		}
+
+		{
+			#if defined(EA_PLATFORM_DESKTOP) // Currently tested only on desktop platforms because not all platforms completely or properly support the time and gmtime functions.
+				time_t t    = time(NULL);
+				tm*    pTM  = gmtime(&t);
+
+				memcpy(&tmValue1, pTM, sizeof(tm));
+				memset(&tmValue2, 0, sizeof(tmValue2));
+
+				// As with the usage of Strftime/Strptime in the first test case above, these do not use all format specifiers
+				// as not all (ie: %u, %W) are currently supported by Strptime.
+				Strftime(pBuffer, kBufferSize, "%a | %#a | %A | %#A | %b | %#b | %B | %#B | %c | %#c | %C | %d | %#d | %D | %e | %h | %H | %#H | %I | %#I | %j | %#j | %m | %#m | %M | %#M | %n | %p | %#p | %r | %R | %S | %#S | %t | %T | %w | %#w | %x | %#x | %X | %#X | %y | %#y | %Y | %#Y | %% | %#%", &tmValue1, NULL);
+				char *ptr = Strptime(pBuffer, "%a | %#a | %A | %#A | %b | %#b | %B | %#B | %c | %#c | %C | %d | %#d | %D | %e | %h | %H | %#H | %I | %#I | %j | %#j | %m | %#m | %M | %#M | %n | %p | %#p | %r | %R | %S | %#S | %t | %T | %w | %#w | %x | %#x | %X | %#X | %y | %#y | %Y | %#Y | %% | %#%", &tmValue2, NULL);
+
+				EATEST_VERIFY(ptr != NULL);
+				EATEST_VERIFY(tmValue1.tm_sec   == tmValue2.tm_sec);
+				EATEST_VERIFY(tmValue1.tm_min   == tmValue2.tm_min);
+				EATEST_VERIFY(tmValue1.tm_hour  == tmValue2.tm_hour);
+				EATEST_VERIFY(tmValue1.tm_mday  == tmValue2.tm_mday);
+				EATEST_VERIFY(tmValue1.tm_year  == tmValue2.tm_year);
+				EATEST_VERIFY(tmValue1.tm_wday  == tmValue2.tm_wday);
+				EATEST_VERIFY(tmValue1.tm_yday  == tmValue2.tm_yday);
+				EATEST_VERIFY(tmValue1.tm_isdst == tmValue2.tm_isdst);
+			#endif
+		}
+
+		{   // Regression
+			// Verify basically that %Y supports 4 digit years.
+			p = Strptime("1999", "%Y", &tmValue1, NULL);
+			//value in tm_year is stored as years past 1900
+			EATEST_VERIFY(p && (p[-1] == '9') && (tmValue1.tm_year == (1999 - 1900)));
+
+			n = Strftime(pBuffer, kBufferSize, "%Y", &tmValue1, NULL);
+			int32_t nYear = EA::StdC::StrtoI32(pBuffer, NULL, 10);
+			EATEST_VERIFY((n == 4) && (nYear == 1999));
+		}
+
+		{   // Regression
+			p = Strptime("969-12-30T12:33:45Z", "%Y-%m-%dT%H:%M:%SZ", &tmValue1, NULL);
+			//value in tm_year is stored as years past 1900
+			EATEST_VERIFY(p && (p[-1] == 'Z') && (tmValue1.tm_year == (969 - 1900)));
+
+			p = Strptime("1969-12-30T12:33:45Z", "%Y-%m-%dT%H:%M:%SZ", &tmValue1, NULL);
+			//value in tm_year is stored as years past 1900
+			EATEST_VERIFY(p && (p[-1] == 'Z') && (tmValue1.tm_year == (1969 - 1900)));
+		}
+
+		delete[] pBuffer;
+	}
+
+
+	// Misc
+	EATEST_VERIFY(IsLeapYear(2004) == true);
+	EATEST_VERIFY(IsLeapYear(1800) == false);
+	EATEST_VERIFY(GetDaysInYear(2004) == (uint32_t)366);
+	EATEST_VERIFY(GetDaysInYear(1800) == (uint32_t)365);
+	EATEST_VERIFY(GetDaysInMonth(kMonthFebruary,2004) == (uint32_t)29);
+	EATEST_VERIFY(GetDaysInMonth(kMonthFebruary,1800) == (uint32_t)28);
+	EATEST_VERIFY(GetDayOfYear(kMonthFebruary,29,2004) == (uint32_t)60);
+	EATEST_VERIFY(GetDayOfYear(kMonthMarch,1,1800) == (uint32_t)60);
+
+
+	{ // Regression of user-reported bug
+		DateTime date1;        
+		uint32_t value;
+
+		date1.Set(1, 1, 1, 0, 0, 0);
+
+		value = date1.GetParameter(kParameterYear);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfYear);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterWeekOfYear);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfWeek);
+		EATEST_VERIFY(value == kDayOfWeekMonday);
+
+		value = date1.GetParameter(kParameterHour);
+		EATEST_VERIFY(value == 0);
+
+		value = date1.GetParameter(kParameterMinute);
+		EATEST_VERIFY(value == 0);
+
+		value = date1.GetParameter(kParameterSecond);
+		EATEST_VERIFY(value == 0);
+
+
+
+		date1.Set(2009, 1, 1, 0, 0, 0);
+
+		value = date1.GetParameter(kParameterYear);
+		EATEST_VERIFY(value == 2009);
+
+		value = date1.GetParameter(kParameterMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfYear);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterWeekOfYear);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfWeek);
+		EATEST_VERIFY(value == kDayOfWeekThursday);
+
+		value = date1.GetParameter(kParameterHour);
+		EATEST_VERIFY(value == 0);
+
+		value = date1.GetParameter(kParameterMinute);
+		EATEST_VERIFY(value == 0);
+
+		value = date1.GetParameter(kParameterSecond);
+		EATEST_VERIFY(value == 0);
+
+
+		date1 = 0;
+		date1.SetParameter(kParameterYear, 2009);
+		date1.SetParameter(kParameterMonth, 1);
+		date1.SetParameter(kParameterDayOfMonth, 1);
+
+		value = date1.GetParameter(kParameterYear);
+		EATEST_VERIFY(value == 2009);
+
+		value = date1.GetParameter(kParameterMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfMonth);
+		EATEST_VERIFY(value == 1);
+
+
+		date1 = 0;
+		date1.SetParameter(kParameterDayOfMonth, 1);
+		date1.SetParameter(kParameterMonth, 1);
+		date1.SetParameter(kParameterYear, 2009);
+
+		value = date1.GetParameter(kParameterYear);
+		EATEST_VERIFY(value == 2009);
+
+		value = date1.GetParameter(kParameterMonth);
+		EATEST_VERIFY(value == 1);
+
+		value = date1.GetParameter(kParameterDayOfMonth);
+		EATEST_VERIFY(value == 1);
+
+
+		//for(int64_t i = 0, secondsPerYear = (kSecondsPerDay * 365), secondsPerFourYears = secondsPerYear * 4; i < secondsPerFourYears; i++)
+		//{
+		//    date1 = i;
+		//    date1.SetParameter(kParameterYear, 2009);
+		//    value = date1.GetParameter(kParameterYear);
+		//    EATEST_VERIFY(value == 2009);
+		//}
+	}
+
+	{ // GetTime
+		const int kTestCount = 5;
+		const int kFailThreshold = 20;
+
+		// 0.2 seconds and 2.0 seconds. It's this high because many platform
+		// implementations of the tv_usec value are grainy and so this is the
+		// best we can validate against.
+		const uint64_t kMaxErrorNs = (EA::StdC::GetTimePrecision() < UINT64_C(100000000)) ? UINT64_C(200000000) : UINT64_C(2000000000);
+
+		// Converts GetTimeOfDay output to an uint64_t representation.
+		auto GetTimeOfDayAsUInt64 = []
+		{
+			timeval tv;
+			timezone_ tz;
+			EA::StdC::GetTimeOfDay(&tv, &tz, true);
+			return (uint64_t)((tv.tv_sec * UINT64_C(1000000000)) + (tv.tv_usec * UINT64_C(1000)));
+		};
+
+		// We need to capture the initial date which our test started running at
+		// so we can try to detect if the system clock has been changed while
+		// our tests are running.
+		uint64_t testStartTimeNs = GetTimeOfDayAsUInt64();
+		uint64_t dateChangedDiffNs = 0;
+		int failCount = 0;
+
+		for (int i = 0; i < kTestCount; i++)
+		{
+			uint64_t t1 = GetTime(); // nanoseconds
+			uint64_t t2 = GetTimeOfDayAsUInt64();
+			const uint64_t diffNs = (t1 > t2) ? (t1 - t2) : (t2 - t1);
+
+			// Adjust the clock if the date has been changed
+			t2 += dateChangedDiffNs;
+
+			// If a thread context switch happened right between the two calls above, resample t1. 
+			// We encounter this problem fairly regularly.
+			if (diffNs > kMaxErrorNs && failCount < kFailThreshold) 
+			{
+				// If the time discrepancy is greater than a minute the system clock has likely changed so we adjust all
+				// future runs of this tests based on the time difference between the old data and the new one
+				if (diffNs > kSecondsPerMinute)
+				{
+					dateChangedDiffNs = t2 > testStartTimeNs ? (~diffNs) + 1 : diffNs; // take diff or two's complement of diff
+				}
+
+				// It's very unlikely we could get another context switch so soon.
+				// Keep trying until we hit the failure limit.
+				failCount++;
+				continue;
+			}
+
+			// For platforms where the main process can be swapped out for extended periods of time,
+			// we disable the test on the buildfarm (via the manifest.xml)
+			#if !defined(EASTDC_SWAPPABLE_PROCESS_PLATFORM)
+				EATEST_VERIFY_F(diffNs <= kMaxErrorNs,
+								"GetTimeOfDay failure on run %d of %d: diffNs: %I64u ns, kMaxErrorNs: %I64u ns. GetTime: "
+								"%I64u ns (%I64u s)\nGetTimeOfDay: %I64u ns (%I64u s)\n",
+								i, kTestCount, diffNs, kMaxErrorNs, t1, t1 / 1000000000, t2,
+								t2 / 1000000000); // Verify that the difference is within N nanoseconds.
+			#endif
+
+			EA::Thread::ThreadSleep(EA::Thread::ThreadTime(500)); // Sleep for N milliseconds, and test again.
+		}
+	}
+
+
+	{ // GetTimeOfDay
+
+		timeval   tv;
+		timezone_ tz;
+
+		// Test various combinations of arguments.
+		int result = EA::StdC::GetTimeOfDay(&tv, &tz, false);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(&tv, &tz, true);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(&tv, NULL, true);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(&tv, NULL, false);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(NULL, &tz, true);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(NULL, &tz, false);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(NULL, NULL, true);
+		EATEST_VERIFY(result == 0);
+
+		result = EA::StdC::GetTimeOfDay(NULL, NULL, false);
+		EATEST_VERIFY(result == 0);
+	}
+
+	{
+		// int64_t ConvertEpochSeconds(Epoch src, int64_t srcSeconds, Epoch dest);
+
+		// Do some conversions from kEpochDateTime to other Epochs.
+		DateTime dtEpochJulian((uint32_t)-4712,  1,  1, 12,  0,  0);
+		int64_t epochJulian = ConvertEpochSeconds(kEpochDateTime, dtEpochJulian.GetSeconds(), kEpochJulian);
+		EATEST_VERIFY(epochJulian == 0);
+
+		DateTime dtEpochModifiedJulian(1858, 11, 17,  0,  0,  0);
+		int64_t epochModifiedJulian = ConvertEpochSeconds(kEpochDateTime, dtEpochModifiedJulian.GetSeconds(), kEpochModifiedJulian);
+		EATEST_VERIFY(epochModifiedJulian == 0);
+
+		DateTime dtEpochGregorian(1752,  9, 14,  0,  0,  0);
+		int64_t epochGregorian = ConvertEpochSeconds(kEpochDateTime, dtEpochGregorian.GetSeconds(), kEpochGregorian);
+		EATEST_VERIFY(epochGregorian == 0);
+
+		DateTime dtEpoch1900(1900,  1,  1,  0,  0,  0);
+		int64_t epoch1900 = ConvertEpochSeconds(kEpochDateTime, dtEpoch1900.GetSeconds(), kEpoch1900);
+		EATEST_VERIFY(epoch1900 == 0);
+
+		DateTime dtEpoch1950(1950,  1,  1,  0,  0,  0);
+		int64_t epoch1950 = ConvertEpochSeconds(kEpochDateTime, dtEpoch1950.GetSeconds(), kEpoch1950);
+		EATEST_VERIFY(epoch1950 == 0);
+
+		DateTime dtEpoch1970(1970,  1,  1,  0,  0,  0);
+		int64_t epoch1970 = ConvertEpochSeconds(kEpochDateTime, dtEpoch1970.GetSeconds(), kEpoch1970);
+		EATEST_VERIFY(epoch1970 == 0);
+
+		DateTime dtEpoch2000(2000,  1,  1,  0,  0,  0);
+		int64_t epoch2000 = ConvertEpochSeconds(kEpochDateTime, dtEpoch2000.GetSeconds(), kEpoch2000);
+		EATEST_VERIFY(epoch2000 == 0);
+
+		DateTime dtEpochJ2000(2000,  1,  1, 11, 58, 55);
+		int64_t epochJ2000 = ConvertEpochSeconds(kEpochDateTime, dtEpochJ2000.GetSeconds(), kEpochJ2000);
+		EATEST_VERIFY(epochJ2000 == 0);
+
+		DateTime dtEpochDateTime(0); // We want to use a full date here instead, but there's a non-trivial issue with the code and documentation currently that prevents this.
+		int64_t epochDateTime = ConvertEpochSeconds(kEpochDateTime, dtEpochDateTime.GetSeconds(), kEpochDateTime);
+		EATEST_VERIFY(epochDateTime == 0);
+
+		// Do a conversion between Epochs to make sure the math is right.
+		int64_t epoch1970RelativeTo1950         = ConvertEpochSeconds(kEpoch1970, epoch1970, kEpoch1950);
+		int64_t epoch1970RelativeTo1950Expected = (dtEpoch1970.GetSeconds() - dtEpoch1950.GetSeconds());
+		EATEST_VERIFY(epoch1970RelativeTo1950 == epoch1970RelativeTo1950Expected);
+	}
+
+	return nErrorCount;
+}
+
+#undef LOCAL_MAX
+
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+

+ 836 - 0
test/source/TestEndian.cpp

@@ -0,0 +1,836 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAEndian.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <float.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+int TestEndian()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestEndian\n");
+
+	/////////////////////////////////////////////////////////////////////
+	// Test Swizzle
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint16_t n16 = 0x0011;
+		n16 = Swizzle(n16);
+
+		EATEST_VERIFY(n16 == 0x1100);
+	}
+
+	{
+		uint32_t n32 = 0x00112233;
+		n32 = Swizzle(n32);
+
+		EATEST_VERIFY(n32 == 0x33221100);
+	}
+
+	{
+		uint64_t n64 = UINT64_C(0x0011223344556677);
+		n64 = Swizzle(n64);
+
+		EATEST_VERIFY(n64 == UINT64_C(0x7766554433221100));
+	}
+
+	{
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::uint128_t n128Swizzled("0xffeeddccbbaa99887766554433221100", 16);
+		EA::StdC::uint128_t n128 = Swizzle(n128Local);
+
+		EATEST_VERIFY(n128 == n128Swizzled);
+	}
+
+	{
+		int16_t n16 = 0x0011;
+		n16 = Swizzle(n16);
+
+		EATEST_VERIFY(n16 == 0x1100);
+	}
+
+	{
+		uint32_t n32 = 0x00112233;
+		n32 = Swizzle(n32);
+
+		EATEST_VERIFY(n32 == 0x33221100);
+	}
+
+	{
+		uint64_t n64 = UINT64_C(0x0011223344556677);
+		n64 = Swizzle(n64);
+
+		EATEST_VERIFY(n64 == UINT64_C(0x7766554433221100));
+	}
+
+	{
+		EA::StdC::int128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::int128_t n128Swizzled("0xffeeddccbbaa99887766554433221100", 16);
+		EA::StdC::int128_t n128 = Swizzle(n128Local);
+
+		EATEST_VERIFY(n128 == n128Swizzled);
+	}
+
+	{
+		// void Swizzle(float* pFloat)
+		// void Swizzle(double* pDouble)
+
+		float f = 1234.5678f;
+		Swizzle(&f);
+		// The representation of f at this point is not necessarily a valid floating point representation. That may present a problem
+		// for this unit test if the compiler loads the f into a floating point register and then moves it back to memory for the rest
+		// of this test, as that could result in the bits no longer being what they were after the above swizzle.
+		Swizzle(&f);
+		EATEST_VERIFY(f == 1234.5678f);
+
+		double d = 1234.5678;
+		Swizzle(&d);
+		// The representation of f at this point is not necessarily a valid floating point representation. That may present a problem
+		// for this unit test if the compiler loads the f into a floating point register and then moves it back to memory for the rest
+		// of this test, as that could result in the bits no longer being what they were after the above swizzle.
+		Swizzle(&d);
+		EATEST_VERIFY(d == 1234.5678);
+	}
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ToBigEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint16_t n16Local = 0x0011;
+		uint16_t n16 = ToBigEndian(n16Local);
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+	}
+
+	{
+		uint32_t n32Local = 0x00112233;
+		uint32_t n32 = ToBigEndian(n32Local);
+		uint8_t* p32 = (uint8_t*)&n32;
+
+		EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+	}
+
+	{
+		uint64_t n64Local = UINT64_C(0x0011223344556677);
+		uint64_t n64 = ToBigEndian(n64Local);
+		uint8_t* p64 = (uint8_t*)&n64;
+
+		EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+					  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+	}
+
+	{
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::uint128_t n128 = ToBigEndian(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+					  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+					  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+					  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+	}  
+
+	{
+		int16_t n16Local = 0x0011;
+		int16_t n16 = ToBigEndian(n16Local);
+		int8_t* p16 = (int8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+	}
+
+	{
+		int32_t n32Local = 0x00112233;
+		int32_t n32 = ToBigEndian(n32Local);
+		int8_t* p32 = (int8_t*)&n32;
+
+		EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+	}
+
+	{
+		int64_t n64Local = UINT64_C(0x0011223344556677);
+		int64_t n64 = ToBigEndian(n64Local);
+		int8_t* p64 = (int8_t*)&n64;
+
+		EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+					  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+	}
+
+	{
+		EA::StdC::int128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::int128_t n128 = ToBigEndian(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+					  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+					  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+					  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+	}  
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ToLittleEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint16_t n16Local = 0x0011;
+		uint16_t n16 = ToLittleEndian(n16Local);
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+	}
+
+	{
+		uint32_t n32Local = 0x00112233;
+		uint32_t n32 = ToLittleEndian(n32Local);
+		uint8_t* p32 = (uint8_t*)&n32;
+
+		EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+	}
+
+	{
+		uint64_t n64Local = UINT64_C(0x0011223344556677);
+		uint64_t n64 = ToLittleEndian(n64Local);
+		uint8_t* p64 = (uint8_t*)&n64;
+
+		EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+					  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+	}
+
+	{
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::uint128_t n128 = ToLittleEndian(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+					  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+					  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+					  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+	}
+
+	{
+		int16_t n16Local = 0x0011;
+		int16_t n16 = ToLittleEndian(n16Local);
+		int8_t* p16 = (int8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+	}
+
+	{
+		int32_t n32Local = 0x00112233;
+		int32_t n32 = ToLittleEndian(n32Local);
+		int8_t* p32 = (int8_t*)&n32;
+
+		EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+	}
+
+	{
+		int64_t n64Local = UINT64_C(0x0011223344556677);
+		int64_t n64 = ToLittleEndian(n64Local);
+		int8_t* p64 = (int8_t*)&n64;
+
+		EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+					  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+	}
+
+	{
+		EA::StdC::int128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::int128_t n128 = ToLittleEndian(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+					  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+					  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+					  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+	}
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ToBigEndianConst
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint16_t n16 = ToBigEndianConst((uint16_t)(0x0011));
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+	}
+
+	{
+		uint32_t n32 = ToBigEndianConst((uint32_t)(0x00112233));
+		uint8_t* p32 = (uint8_t*)&n32;
+
+		EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+	}
+
+	{
+		uint64_t n64 = ToBigEndianConst(UINT64_C(0x0011223344556677));
+		uint8_t* p64 = (uint8_t*)&n64;
+
+		EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+					  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+	}
+
+	{
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::uint128_t n128 = ToBigEndianConst(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+					  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+					  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+					  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+	}  
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ToLittleEndianConst
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint16_t n16 = ToLittleEndian((uint16_t)(0x0011));
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+	}
+
+	{
+		uint32_t n32 = ToLittleEndianConst((uint32_t)(0x00112233));
+		uint8_t  p32[4];
+		memcpy(p32, &n32, sizeof(n32));
+
+		EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+	}
+
+	{
+		uint64_t n64 = ToLittleEndianConst(UINT64_C(0x0011223344556677));
+		uint8_t  p64[8];
+		memcpy(p64, &n64, sizeof(n64));
+
+		EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+					  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));  
+	}
+
+	{
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		EA::StdC::uint128_t n128 = ToLittleEndianConst(n128Local);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+					  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+					  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+					  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+	}  
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ReadFromBigEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint8_t  pBuffer[sizeof(uint16_t)] = { 0x00, 0x11 };
+		uint16_t n16 = ReadFromBigEndianUint16(pBuffer);
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+		#else
+			EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+		#endif
+	}
+
+	{
+		uint8_t  pBuffer[sizeof(uint32_t)] = { 0x00, 0x11, 0x22, 0x33 };
+		uint32_t n32 = ReadFromBigEndianUint32(pBuffer);
+		uint8_t* p32 = (uint8_t*)&n32;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+		#else
+			EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+		#endif
+	}
+
+	{
+		uint8_t  pBuffer[sizeof(uint64_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+		uint64_t n64 = ReadFromBigEndianUint64(pBuffer);
+		uint8_t* p64 = (uint8_t*)&n64;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+						  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+		#else
+			EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+						  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int16_t)] = { 0x00, 0x11 };
+		int16_t n16 = ReadFromBigEndianInt16(pBuffer);
+		int8_t* p16 = (int8_t*)&n16;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+		#else
+			EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int32_t)] = { 0x00, 0x11, 0x22, 0x33 };
+		int32_t n32 = ReadFromBigEndianInt32(pBuffer);
+		int8_t* p32 = (int8_t*)&n32;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+		#else
+			EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int64_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+		int64_t n64 = ReadFromBigEndianInt64(pBuffer);
+		int8_t* p64 = (int8_t*)&n64;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+						  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+		#else
+			EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+						  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+		#endif
+	}
+
+	{
+		// This is a bit hard to test fully, as we need to hit a lot of floating point values 
+		// to make sure no invalid floating point values are put into FPU registers. We can't 
+		// just test random 32 bit memory patterns. With respect to NANs, a swizzled NAN is 
+		// converted by the FPU to some other NAN and so we can't do a bit comparison.
+
+		size_t       i;
+		char         buffer[32];
+		const float  fTestValues[] = { FLT_MIN, FLT_MAX, 0.f, -0.f, 1.f, -1.f };
+		const double dTestValues[] = { DBL_MIN, DBL_MAX, 0.f, -0.f, 1.f, -1.f };
+
+		for(i = 0; i < (sizeof(fTestValues) / sizeof(fTestValues[0])); i++)
+		{
+			WriteToBigEndian(buffer, fTestValues[i]);
+			float f = ReadFromBigEndianFloat(buffer);
+
+			EATEST_VERIFY(f == fTestValues[i]);
+		}
+
+		for(i = 0; i < (sizeof(dTestValues) / sizeof(dTestValues[0])); i++)
+		{
+			WriteToBigEndian(buffer, dTestValues[i]);
+			double d = ReadFromBigEndianDouble(buffer);
+
+			EATEST_VERIFY(d == dTestValues[i]);
+		}
+	}
+
+	{
+		uint8_t   pBuffer[sizeof(EA::StdC::uint128_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+		EA::StdC::uint128_t n128 = ReadFromBigEndianUint128(pBuffer);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+						  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+						  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+						  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+		#else
+			EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+						  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+						  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+						  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+		#endif
+	}
+
+	{
+		uint8_t   pBuffer[sizeof(EA::StdC::int128_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+		EA::StdC::int128_t  n128 = ReadFromBigEndianInt128(pBuffer);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+						  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+						  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+						  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+		#else
+			EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+						  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+						  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+						  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+		#endif
+	}
+
+	{
+		// uint32_t ReadFromBigEndian(const void* pSource, int32_t nSourceBytes)
+
+		// To do: Improve this test. Currently it's hardly more than a compile test.
+		uint32_t n32, nSource = 0x00112233;
+		n32 = ReadFromBigEndian(&nSource, 4);
+		EATEST_VERIFY(n32 != 0);
+	}
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test ReadFromLittleEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint8_t  pBuffer[sizeof(uint16_t)] = { 0x00, 0x11 };
+		uint16_t n16 = ReadFromLittleEndianUint16(pBuffer);
+		uint8_t* p16 = (uint8_t*)&n16;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+		#else
+			EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+		#endif
+	}
+
+	{
+		uint8_t  pBuffer[sizeof(uint32_t)] = { 0x00, 0x11, 0x22, 0x33 };
+		uint32_t n32 = ReadFromLittleEndianUint32(pBuffer);
+		uint8_t* p32 = (uint8_t*)&n32;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+		#else
+			EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+		#endif
+	}
+
+	{
+		uint8_t  pBuffer[sizeof(uint64_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+		uint64_t n64 = ReadFromLittleEndianUint64(pBuffer);
+		uint8_t* p64 = (uint8_t*)&n64;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+				   (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+		#else
+			EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+				   (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int16_t)] = { 0x00, 0x11 };
+		int16_t n16 = ReadFromLittleEndianInt16(pBuffer);
+		int8_t* p16 = (int8_t*)&n16;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11));
+		#else
+			EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int32_t)] = { 0x00, 0x11, 0x22, 0x33 };
+		int32_t n32 = ReadFromLittleEndianInt32(pBuffer);
+		int8_t* p32 = (int8_t*)&n32;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33));
+		#else
+			EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00));
+		#endif
+	}
+
+	{
+		int8_t  pBuffer[sizeof(int64_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+		int64_t n64 = ReadFromLittleEndianInt64(pBuffer);
+		int8_t* p64 = (int8_t*)&n64;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) &&
+				   (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77));
+		#else
+			EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+				   (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00));
+		#endif
+	}
+
+	{
+		// This is a bit hard to test fully, as we need to hit a lot of floating point values 
+		// to make sure no invalid floating point values are put into FPU registers. We can't 
+		// just test random 32 bit memory patterns. With respect to NANs, a swizzled NAN is 
+		// converted by the FPU to some other NAN and so we can't do a bit comparison.
+
+		size_t       i;
+		char         buffer[32];
+		const float  fTestValues[] = { FLT_MIN, FLT_MAX, 0.f, -0.f, 1.f, -1.f };
+		const double dTestValues[] = { DBL_MIN, DBL_MAX, 0.f, -0.f, 1.f, -1.f };
+
+		for(i = 0; i < (sizeof(fTestValues) / sizeof(fTestValues[0])); i++)
+		{
+			WriteToLittleEndian(buffer, fTestValues[i]);
+			float f = ReadFromLittleEndianFloat(buffer);
+
+			EATEST_VERIFY(f == fTestValues[i]);
+		}
+
+		for(i = 0; i < (sizeof(dTestValues) / sizeof(dTestValues[0])); i++)
+		{
+			WriteToLittleEndian(buffer, dTestValues[i]);
+			double d = ReadFromLittleEndianDouble(buffer);
+
+			EATEST_VERIFY(d == dTestValues[i]);
+		}
+	}
+
+	{
+		uint8_t   pBuffer[sizeof(EA::StdC::uint128_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+		EA::StdC::uint128_t n128 = ReadFromLittleEndianUint128(pBuffer);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+				   (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+				   (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+				   (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+		#else
+			EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+				   (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+				   (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+				   (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+		#endif
+	}
+
+	{
+		uint8_t   pBuffer[sizeof(EA::StdC::int128_t)] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+		EA::StdC::int128_t n128 = ReadFromLittleEndianInt128(pBuffer);
+		uint8_t*  p128 = (uint8_t*)&n128;
+
+		#ifdef EA_SYSTEM_LITTLE_ENDIAN
+			EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+				   (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+				   (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+				   (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff));
+		#else
+			EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+				   (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+				   (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+				   (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00));
+		#endif
+	}
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test WriteToBigEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint8_t p16[sizeof(uint16_t) + 1];
+		p16[sizeof(uint16_t)] = 0xfe;
+		WriteToBigEndian(p16, (uint16_t)0x0011);
+
+		EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11) &&  p16[sizeof(uint16_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p32[sizeof(uint32_t) + 1];
+		p32[sizeof(uint32_t)] = 0xfe;
+		WriteToBigEndian(p32, (uint32_t)0x00112233);
+
+		EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33) && p32[sizeof(uint32_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p64[sizeof(uint64_t) + 1];
+		p64[sizeof(uint64_t)] = 0xfe;
+		WriteToBigEndian(p64, UINT64_C(0x0011223344556677));
+
+		EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) && 
+					  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77) && 
+					   p64[sizeof(uint64_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p16[sizeof(int16_t) + 1];
+		p16[sizeof(int16_t)] = 0xfe;
+		WriteToBigEndian(p16, (int16_t)0x0011);
+
+		EATEST_VERIFY((p16[0] == 0x00) && (p16[1] == 0x11) &&  p16[sizeof(int16_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p32[sizeof(int32_t) + 1];
+		p32[sizeof(int32_t)] = 0xfe;
+		WriteToBigEndian(p32, (int32_t)0x00112233);
+
+		EATEST_VERIFY((p32[0] == 0x00) && (p32[1] == 0x11) && (p32[2] == 0x22) && (p32[3] == 0x33) && p32[sizeof(int32_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p64[sizeof(int64_t) + 1];
+		p64[sizeof(int64_t)] = 0xfe;
+		WriteToBigEndian(p64, INT64_C(0x0011223344556677));
+
+		EATEST_VERIFY((p64[0] == 0x00) && (p64[1] == 0x11) && (p64[2] == 0x22) && (p64[3] == 0x33) && 
+					  (p64[4] == 0x44) && (p64[5] == 0x55) && (p64[6] == 0x66) && (p64[7] == 0x77) && 
+					   p64[sizeof(int64_t)] == 0xfe);
+	}
+
+	{
+		// WriteToBigEndian Float
+		// WriteToBigEndian Double
+		// These are covered in the ReadFromBigEndian tests.
+	}
+
+	{
+		uint8_t p128[sizeof(EA::StdC::uint128_t) + 1];
+		p128[sizeof(EA::StdC::uint128_t)] = 0xfe;
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		WriteToBigEndian(p128, n128Local);
+
+		EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+					  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+					  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+					  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff) &&
+					   p128[sizeof(EA::StdC::uint128_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p128[sizeof(EA::StdC::int128_t) + 1];
+		p128[sizeof(EA::StdC::int128_t)] = 0xfe;
+		EA::StdC::int128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		WriteToBigEndian(p128, n128Local);
+
+		EATEST_VERIFY((p128[0]  == 0x00) && (p128[1]  == 0x11) && (p128[2]   == 0x22) && (p128[3]  == 0x33) &&
+					  (p128[4]  == 0x44) && (p128[5]  == 0x55) && (p128[6]   == 0x66) && (p128[7]  == 0x77) &&
+					  (p128[8]  == 0x88) && (p128[9]  == 0x99) && (p128[10]  == 0xaa) && (p128[11] == 0xbb) &&
+					  (p128[12] == 0xcc) && (p128[13] == 0xdd) && (p128[14]  == 0xee) && (p128[15] == 0xff) &&
+					   p128[sizeof(EA::StdC::int128_t)] == 0xfe);
+	}
+
+	{
+		// void WriteToBigEndian(const void* pDest, uint32_t data, int32_t nSourceBytes)
+
+		// To do: Improve this test. Currently it's hardly more than a compile test.
+		uint32_t n32 = 0, nSource = 0x00112233;
+		WriteToBigEndian(&n32, nSource, 4);
+		EATEST_VERIFY(n32 != 0);
+	}
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Test WriteToLittleEndian
+	/////////////////////////////////////////////////////////////////////
+	{
+		uint8_t p16[sizeof(uint16_t) + 1];
+		p16[sizeof(uint16_t)] = 0xfe;
+		WriteToLittleEndian(p16, (uint16_t)0x0011);
+
+		EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00) &&  p16[sizeof(uint16_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p32[sizeof(uint32_t) + 1];
+		p32[sizeof(uint32_t)] = 0xfe;
+		WriteToLittleEndian(p32, (uint32_t)0x00112233);
+
+		EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00) && p32[sizeof(uint32_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p64[sizeof(uint64_t) + 1];
+		p64[sizeof(uint64_t)] = 0xfe;
+		WriteToLittleEndian(p64, UINT64_C(0x0011223344556677));
+
+		EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+					  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00) &&
+					   p64[sizeof(uint64_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p16[sizeof(int16_t) + 1];
+		p16[sizeof(int16_t)] = 0xfe;
+		WriteToLittleEndian(p16, (int16_t)0x0011);
+
+		EATEST_VERIFY((p16[0] == 0x11) && (p16[1] == 0x00) &&  p16[sizeof(int16_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p32[sizeof(int32_t) + 1];
+		p32[sizeof(int32_t)] = 0xfe;
+		WriteToLittleEndian(p32, (int32_t)0x00112233);
+
+		EATEST_VERIFY((p32[0] == 0x33) && (p32[1] == 0x22) && (p32[2] == 0x11) && (p32[3] == 0x00) && p32[sizeof(int32_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p64[sizeof(int64_t) + 1];
+		p64[sizeof(int64_t)] = 0xfe;
+		WriteToLittleEndian(p64, UINT64_C(0x0011223344556677));
+
+		EATEST_VERIFY((p64[0] == 0x77) && (p64[1] == 0x66) && (p64[2] == 0x55) && (p64[3] == 0x44) &&
+					  (p64[4] == 0x33) && (p64[5] == 0x22) && (p64[6] == 0x11) && (p64[7] == 0x00) &&
+					   p64[sizeof(int64_t)] == 0xfe);
+	}
+
+	{
+		// To do
+		// WriteToLittleEndian Float
+		// WriteToLittleEndian Double
+		// These are covered in the ReadFromLittleEndian tests.
+	}
+
+	{
+		uint8_t p128[sizeof(EA::StdC::uint128_t) + 1];
+		p128[sizeof(EA::StdC::uint128_t)] = 0xfe;
+		EA::StdC::uint128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		WriteToLittleEndian(p128, n128Local);
+
+		EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+					  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+					  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+					  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00) &&
+					   p128[sizeof(EA::StdC::uint128_t)] == 0xfe);
+	}
+
+	{
+		uint8_t p128[sizeof(EA::StdC::int128_t) + 1];
+		p128[sizeof(EA::StdC::int128_t)] = 0xfe;
+		EA::StdC::int128_t n128Local("0x00112233445566778899aabbccddeeff", 16);
+		WriteToLittleEndian(p128, n128Local);
+
+		EATEST_VERIFY((p128[0]  == 0xff) && (p128[1]  == 0xee) && (p128[2]   == 0xdd) && (p128[3]  == 0xcc) &&
+					  (p128[4]  == 0xbb) && (p128[5]  == 0xaa) && (p128[6]   == 0x99) && (p128[7]  == 0x88) &&
+					  (p128[8]  == 0x77) && (p128[9]  == 0x66) && (p128[10]  == 0x55) && (p128[11] == 0x44) &&
+					  (p128[12] == 0x33) && (p128[13] == 0x22) && (p128[14]  == 0x11) && (p128[15] == 0x00) &&
+					   p128[sizeof(EA::StdC::int128_t)] == 0xfe);
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+

+ 220 - 0
test/source/TestFixedPoint.cpp

@@ -0,0 +1,220 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/EAFixedPoint.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+
+
+static bool CompareValues(double a, double b)
+{
+	return (fabs(a - b) < 0.01);
+}
+
+static bool CompareValues(EA::StdC::SFixed16 a, double b)
+{
+	#define Fixed16ToDouble(a) (((double)a) / 65536.0)
+
+	const double c = Fixed16ToDouble(a.AsFixed());
+	return (fabs(c - b) < 0.01);
+}
+
+
+// In a DLL build, VC++ doesn't like it when you provide your own 
+// versions of functions. So we just skip testing this, for simplicity.
+// If we need this to work, we can just add these functions to EAFixedPoint.cpp.
+#ifndef EA_DLL
+
+	namespace EA
+	{
+		namespace StdC
+		{
+			static bool CompareValues(SFixed24 a, double b){
+				#define Fixed24ToDouble(a) (((double)a) / 256.0)
+				const double c = Fixed24ToDouble(a.AsFixed());
+				return (fabs(c - b) < 0.01);
+			}
+
+			template<>
+			int32_t SFixed24::FixedMul(const int32_t a, const int32_t b){
+				 const int64_t c = (int64_t)a * b;
+				 return (int32_t)(c >> 8);
+			}
+
+			template<>
+			int32_t SFixed24::FixedDiv(const int32_t a, const int32_t b){
+				 const int64_t c = ((uint64_t)a) << 8;
+				 return (int32_t)(c / b);
+			}
+
+			template<>
+			int32_t SFixed24::FixedMod(const int32_t a, const int32_t b){
+				 const volatile uint64_t c = ((uint64_t)a) << 8;
+				 return (int32_t)(uint32_t)(c % b);
+			}
+		}
+	}
+#endif
+
+
+
+int TestFixedPoint()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestFixedPoint\n");
+
+	// Test SFixed16
+	{
+		SFixed16  a(1), b(2), c(3.f), d(1.0);
+		double    e = 3.2;
+		float     f = 4.5;
+		int       g =   6;
+
+		if(a.AsInt() != 1)
+			nErrorCount++;
+		if(c.AsUnsignedInt() != 3)
+			nErrorCount++;
+		if(a.AsLong() != 1)
+			nErrorCount++;
+		if(c.AsUnsignedLong() != 3)
+			nErrorCount++;
+		if(!CompareValues((double)a.AsFloat(), 1.0))
+			nErrorCount++;
+		if(!CompareValues(c.AsDouble(), 3.0))
+			nErrorCount++;
+
+		a = b * f;
+		if(!CompareValues(a, 9.0))
+			nErrorCount++;
+
+		a = b / d;
+		if(!CompareValues(a, 2.0))
+			nErrorCount++;
+
+		a = b + d;
+		if(!CompareValues(a, 3.0))
+			nErrorCount++;
+
+		a = (c / e) + b + f;
+		if(!CompareValues(a, 7.4375))
+			nErrorCount++;
+
+		a = c / e * (b % g) + f / c;
+		if(!CompareValues(a, 3.375))
+			nErrorCount++;
+
+		a = g * -c / (b++);
+		if(!CompareValues(a, -9.0))
+			nErrorCount++;
+		if(!CompareValues(b, 3.0))
+			nErrorCount++;
+		--b; //Restore it to its original value.
+		if(!CompareValues(b, 2.0))
+			nErrorCount++;
+
+		a = sin(d) + pow(b, e) * sqrt(d);
+		if(!CompareValues(a, 10.031))
+			nErrorCount++;
+
+		a = log(e) / log(f);
+		if(!CompareValues(a, 0.77333))
+			nErrorCount++;
+	}
+
+
+	#ifndef EA_DLL
+		// Test SFixed24
+		{
+			SFixed24  a(1), b(2), c(3.f), d(1.0);
+			double    e = 3.2;
+			float     f = 4.5;
+			int       g =   6;
+
+			if(a.AsInt() != 1)
+				nErrorCount++;
+			if(c.AsUnsignedInt() != 3)
+				nErrorCount++;
+			if(a.AsLong() != 1)
+				nErrorCount++;
+			if(c.AsUnsignedLong() != 3)
+				nErrorCount++;
+			if(!CompareValues((double)a.AsFloat(), 1.0))
+				nErrorCount++;
+			if(!CompareValues(c.AsDouble(), 3.0))
+				nErrorCount++;
+
+			a = b * f;
+			if(!CompareValues(a, 9.0))
+				nErrorCount++;
+
+			a = b / d;
+			if(!CompareValues(a, 2.0))
+				nErrorCount++;
+
+			a = b + d;
+			if(!CompareValues(a, 3.0))
+				nErrorCount++;
+
+			a = (c / e) + b + f;
+			if(!CompareValues(a, 7.4375))
+				nErrorCount++;
+
+			a = c / e * (b % g) + f / c;
+			if(!CompareValues(a, 3.375))
+				nErrorCount++;
+
+			a = g * -c / (b++);
+			if(!CompareValues(a, -9.0))
+				nErrorCount++;
+			if(!CompareValues(b, 3.0))
+				nErrorCount++;
+			--b; //Restore it to its original value.
+			if(!CompareValues(b, 2.0))
+				nErrorCount++;
+
+			a = sin(d) + pow(b, e) * sqrt(d);
+			if(!CompareValues(a, 10.031))
+				nErrorCount++;
+
+			a = log(e) / log(f);
+			if(!CompareValues(a, 0.77333))
+				nErrorCount++;
+		}
+	#endif
+
+	// Test core multiplication/division functions
+	{
+		//SFixed16 SFixed16::FixedMul       (const T t1, const T t2);
+		//SFixed16 SFixed16::FixedDiv       (const T t1, const T t2);
+		//SFixed16 SFixed16::FixedDivSafe   (const T t1, const T t2);
+		//SFixed16 SFixed16::FixedMulDiv    (const T t1, const T t2, const T t3);
+		//SFixed16 SFixed16::FixedMulDivSafe(const T t1, const T t2, const T t3); 
+		//SFixed16 SFixed16::FixedMod       (const T t1, const T t2);
+		//SFixed16 SFixed16::FixedModSafe   (const T t1, const T t2);
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 148 - 0
test/source/TestGlobal.cpp

@@ -0,0 +1,148 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAGlobal.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+struct GlobalTestObject
+{
+	int sequence;
+	static int sSeqCounter;
+
+	GlobalTestObject() 
+	  : sequence(++sSeqCounter) {}
+};
+
+int GlobalTestObject::sSeqCounter;
+
+// This structure will require 8 byte alignment on most platforms
+struct AlignedTestObject
+{
+	uint64_t data;
+};
+
+int TestGlobal()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestGlobal\n");
+
+	const uint32_t id = 0x8e9c5fd7;
+
+	GlobalTestObject::sSeqCounter = 0;
+
+	// create an OS global object and verify that the same one is returned on a second query
+	{
+		AutoOSGlobalPtr<GlobalTestObject, id> pObj;
+
+		EATEST_VERIFY(pObj->sequence == 1);
+
+		{
+			AutoOSGlobalPtr<GlobalTestObject, id> pObj2;
+
+			EATEST_VERIFY(pObj.get() == pObj2.get());
+			EATEST_VERIFY(pObj2->sequence == 1);
+		}
+	}
+
+	// create another OS global object; the last one should have been destroyed so this
+	// one should be a different count
+	{
+		AutoStaticOSGlobalPtr<GlobalTestObject, id> pObj;
+
+		EATEST_VERIFY(pObj->sequence == 2);
+
+		{
+			AutoStaticOSGlobalPtr<GlobalTestObject, id> pObj2;
+
+			EATEST_VERIFY(pObj.get() == pObj2.get());
+			EATEST_VERIFY(pObj2->sequence == 2);
+		}
+	}
+
+	{
+		AutoStaticOSGlobalPtr<AlignedTestObject, 0x52534562> pObj1;
+		AutoStaticOSGlobalPtr<AlignedTestObject, 0x93715355> pObj2;
+	
+		size_t requiredAlignment = EA_ALIGN_OF(AlignedTestObject);
+		// Ensure the two objects are unique.
+		EATEST_VERIFY(pObj1.get() != pObj2.get() );
+		EATEST_VERIFY( (reinterpret_cast<uintptr_t>(pObj1.get()) % requiredAlignment) == 0 );
+		EATEST_VERIFY( (reinterpret_cast<uintptr_t>(pObj2.get()) % requiredAlignment) == 0 );
+		pObj1->data = 1;
+		pObj2->data = 2;
+		// Verify the data is correct in the objects.  This is done to catch errors where the two global objects could potentially overlap.
+		EATEST_VERIFY( pObj1->data == 1 );
+		EATEST_VERIFY( pObj2->data == 2 );
+	}
+
+	// create a global pointer object
+	{
+		const uint32_t kGUID = 0xef020e29;
+
+		GlobalPtr<int, kGUID> pMemory;
+
+		EATEST_VERIFY(!pMemory);  // Global pointer should be nulled on creation.
+
+		if(pMemory)  // Global pointer should be nulled on creation.
+			EATEST_VERIFY(pMemory != NULL);
+
+		int* const p = new int[2];
+		pMemory = p;
+
+		EATEST_VERIFY((int*)pMemory == p);
+
+		pMemory[0] = 10;
+		pMemory[1] = 20;
+
+		{
+			GlobalPtr<int, kGUID> pMemory2A;
+			GlobalPtr<int, kGUID> pMemory2B(pMemory2A);
+
+			EATEST_VERIFY(pMemory2A == pMemory);
+			EATEST_VERIFY(pMemory2B == pMemory);
+			EATEST_VERIFY(*pMemory2A == 10);
+			EATEST_VERIFY(pMemory2A[0] == 10);
+			EATEST_VERIFY(pMemory2A[1] == 20);
+		}
+
+		pMemory = NULL;
+		EATEST_VERIFY(!pMemory);
+		delete[] p;
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 314 - 0
test/source/TestHash.cpp

@@ -0,0 +1,314 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/EAHashString.h>
+#include <EAStdC/EAHashCRC.h>
+#include <EAStdC/EABitTricks.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+
+#if defined(_MSC_VER)
+	#pragma warning(disable: 6211) // Leaking memory due to an exception. Consider using a local catch block to clean up memory.
+#endif
+
+
+static int TestHashString()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+ 
+	// GCC 2.x fails due to compiler bugs.
+	#if (!defined(__GNUC__) || (__GNUC__ >= 3)) 
+
+	{ // Test CTStringHash
+
+		char     stringArray[] = "01234567890123456789012345678901234567890";
+		uint32_t nCTHashArray[33];
+
+		nCTHashArray[0]  = CTStringHash<0>::value;
+		nCTHashArray[1]  = CTStringHash<'0'>::value;
+		nCTHashArray[2]  = CTStringHash<'0', '1'>::value;
+		nCTHashArray[3]  = CTStringHash<'0', '1', '2'>::value;
+		nCTHashArray[4]  = CTStringHash<'0', '1', '2', '3'>::value;
+		nCTHashArray[5]  = CTStringHash<'0', '1', '2', '3', '4'>::value;
+		nCTHashArray[6]  = CTStringHash<'0', '1', '2', '3', '4', '5'>::value;
+		nCTHashArray[7]  = CTStringHash<'0', '1', '2', '3', '4', '5', '6'>::value;
+		nCTHashArray[8]  = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7'>::value;
+		nCTHashArray[9]  = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8'>::value;
+		nCTHashArray[10] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'>::value;
+		nCTHashArray[11] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'>::value;
+		nCTHashArray[12] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1'>::value;
+		nCTHashArray[13] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2'>::value;
+		nCTHashArray[14] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3'>::value;
+		nCTHashArray[15] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4'>::value;
+		nCTHashArray[16] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value;
+		nCTHashArray[17] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value;
+		nCTHashArray[18] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7'>::value;
+		nCTHashArray[19] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'>::value;
+		nCTHashArray[20] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'>::value;
+		nCTHashArray[21] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'>::value;
+		nCTHashArray[22] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1'>::value;
+		nCTHashArray[23] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2'>::value;
+		nCTHashArray[24] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3'>::value;
+		nCTHashArray[25] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4'>::value;
+		nCTHashArray[26] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value;
+		nCTHashArray[27] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value;
+		nCTHashArray[28] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7'>::value;
+		nCTHashArray[29] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'>::value;
+		nCTHashArray[30] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'>::value;
+		nCTHashArray[31] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'>::value;
+		nCTHashArray[32] = CTStringHash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1'>::value;
+
+		for(int i = 31; i >= 0; i--)
+		{
+			stringArray[i] = 0;
+			const uint32_t nHashFNV1 = FNV1_String8(stringArray);
+			EATEST_VERIFY(nHashFNV1 == nCTHashArray[i]);
+		}
+	}
+
+	#endif
+
+	return nErrorCount;
+}
+
+
+
+int TestHash()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestHash\n");
+
+	const int kDataLength(16384); // We intentionally choose a power of 2.
+	EATEST_VERIFY((kDataLength % 8) == 0); // Code below depends on this.
+
+	uint8_t*  pDataA   = new uint8_t[kDataLength];
+	uint8_t*  pDataB   = new uint8_t[kDataLength];
+	char8_t*  pData8A  = new char8_t[kDataLength];
+	char8_t*  pData8B  = new char8_t[kDataLength];
+	char16_t* pData16A = new char16_t[kDataLength];
+	char16_t* pData16B = new char16_t[kDataLength];
+	char32_t* pData32A = new char32_t[kDataLength];
+	char32_t* pData32B = new char32_t[kDataLength];
+
+	// Initialize data
+	for(int i(0); i < kDataLength; i++)
+	{
+		const int c = ((i == 0) ? 1 : i);
+
+		pDataA[i]    = (uint8_t) c;
+		pDataB[i]    = (uint8_t) c;
+		pData8A[i]   = (char8_t) c;
+		pData8B[i]   = (char8_t) c;
+		pData16A[i]  = (char16_t)c;
+		pData16B[i]  = (char16_t)c;
+		pData32A[i]  = (char32_t)c;
+		pData32B[i]  = (char32_t)c;
+	}
+
+	pDataA[kDataLength - 1]   = 0;
+	pDataB[kDataLength - 1]   = 0;
+	pData8A[kDataLength - 1]  = 0;
+	pData8B[kDataLength - 1]  = 0;
+	pData16A[kDataLength - 1] = 0;
+	pData16B[kDataLength - 1] = 0;
+	pData32A[kDataLength - 1] = 0;
+	pData32B[kDataLength - 1] = 0;
+
+
+	{ // Test DJB2 string hash
+		uint32_t nInitialValue(0x12345678);
+		uint32_t nHashValue(0);
+
+		nHashValue = DJB2(pDataA, kDataLength, nInitialValue);
+		EATEST_VERIFY(nHashValue != 0);
+		nHashValue = DJB2_String8(pData8A, nInitialValue);
+		EATEST_VERIFY(nHashValue != 0);
+		nHashValue = DJB2_String16(pData16A, nInitialValue);
+		EATEST_VERIFY(nHashValue != 0);
+	}
+
+
+	{ // Test FNV1 string hash
+		uint32_t nInitialValue(0x12345678);
+		uint32_t nHashValue(0);
+
+		// To do: Come up with better test validation.
+		nHashValue = FNV1(pDataA, kDataLength, nInitialValue);
+		EATEST_VERIFY(nHashValue == 0x67f6dbec);
+		nHashValue = FNV1_String8(pData8A, nInitialValue);
+		EATEST_VERIFY(nHashValue == 0x70533413);
+		nHashValue = FNV1_String16(pData16A, nInitialValue);
+		EATEST_VERIFY(nHashValue == 0xa1014ae4);
+		nHashValue = FNV1_String32(pData32A, nInitialValue);
+		EATEST_VERIFY(nHashValue == 0xa1014ae4);
+	}
+
+
+	{ // Test FNV64 string hash
+		uint64_t nInitialValue(0x12345678);
+		uint64_t nHashValue(0);
+
+		// To do: Come up with better test validation.
+		nHashValue = FNV64(pDataA, kDataLength, nInitialValue);
+		EATEST_VERIFY(nHashValue == UINT64_C(0xbe387e6512cbab0c));
+		nHashValue = FNV64_String8(pData8A, nInitialValue);
+		EATEST_VERIFY(nHashValue == UINT64_C(0x78b14197ac736ef3));
+		nHashValue = FNV64_String16(pData16A, nInitialValue);
+		EATEST_VERIFY(nHashValue == UINT64_C(0xf07159d175cf1dc4));
+		nHashValue = FNV64_String32(pData32A, nInitialValue);
+		EATEST_VERIFY(nHashValue == UINT64_C(0xf07159d175cf1dc4));
+	}
+
+	nErrorCount += TestHashString();
+
+
+	{ // Test CRC16 binary hash
+		uint16_t nHashValue1;
+		uint16_t nHashValue2(kCRC16InitialValue);
+
+		// Test one-shot CRC.
+		nHashValue1 = CRC16(pDataA, kDataLength);
+
+		// Test iterative CRC.
+		for(int i = 0; i < 8; i++)
+			nHashValue2 = CRC16(pDataA + (i * (kDataLength / 8)), kDataLength / 8, nHashValue2, i == 7);
+
+		EATEST_VERIFY(nHashValue1 == nHashValue2);
+	}
+
+
+	{ // Test CRC24 binary hash
+		uint32_t nHashValue1;
+		uint32_t nHashValue2(kCRC24InitialValue);
+
+		// Test one-shot CRC.
+		nHashValue1 = CRC24(pDataA, kDataLength);
+
+		// Test iterative CRC.
+		for(int i = 0; i < 8; i++)
+			nHashValue2 = CRC24(pDataA + (i * (kDataLength / 8)), kDataLength / 8, nHashValue2, i == 7);
+
+		EATEST_VERIFY(EA::StdC::GetHighestBitPowerOf2(nHashValue1) <= 24);
+		EATEST_VERIFY(EA::StdC::GetHighestBitPowerOf2(nHashValue2) <= 24);
+		EATEST_VERIFY(nHashValue1 == nHashValue2);
+	}
+
+
+	{ // Test CRC32 binary hash
+		uint32_t nHashValue1;
+		uint32_t nHashValue2(kCRC32InitialValue);
+
+		// Test one-shot CRC.
+		nHashValue1 = CRC32(pDataA, kDataLength);
+
+		// Test iterative CRC.
+		for(int i = 0; i < 8; i++)
+			nHashValue2 = CRC32(pDataA + (i * (kDataLength / 8)), kDataLength / 8, nHashValue2, i == 7);
+
+		EATEST_VERIFY(nHashValue1 == nHashValue2);
+	}
+
+
+	{ // Test CRC32 big-endian binary hash
+		uint32_t nHashValue1;
+		uint32_t nHashValue2(kCRC32InitialValue);
+
+		// Test one-shot CRC.
+		nHashValue1 = CRC32Reverse(pDataA, kDataLength);
+
+		// Test iterative CRC.
+		for(int i = 0; i < 8; i++)
+			nHashValue2 = CRC32Reverse(pDataA + (i * (kDataLength / 8)), kDataLength / 8, nHashValue2, i == 7);
+
+		EATEST_VERIFY(nHashValue1 == nHashValue2);
+	}
+
+
+	{ // Test CRC64 binary hash
+		uint64_t nHashValue1;
+		uint64_t nHashValue2(kCRC64InitialValue);
+
+		// Test one-shot CRC.
+		nHashValue1 = CRC64(pDataA, kDataLength);
+
+		// Test iterative CRC.
+		for(int i = 0; i < 8; i++)
+			nHashValue2 = CRC64(pDataA + (i * (kDataLength / 8)), kDataLength / 8, nHashValue2, i == 7);
+
+		EATEST_VERIFY(nHashValue1 == nHashValue2);
+	}
+
+
+	{
+		const char a[ 1] = { 'a' };
+		const char b[ 2] = { 'b', 'c' };
+		const char c[ 3] = { 'c', 'd', 'e' };
+		const char d[ 4] = { 'd', 'e', 'f', 'g' };
+		const char e[ 5] = { 'd', 'e', 'f', 'g', 'h' };
+		const char h[ 8] = { 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' };
+		const char j[10] = { 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm' };
+
+		// To do: Establish correct test result values for these.
+
+		EATEST_VERIFY(CRC16(a, sizeof(a), 0xfbea, false) == 0x67bb);
+		EATEST_VERIFY(CRC16(b, sizeof(b), 0xfbea, false) == 0xaa67);
+		EATEST_VERIFY(CRC16(c, sizeof(c), 0xfbea, false) == 0x3178);
+		EATEST_VERIFY(CRC16(d, sizeof(d), 0xfbea, false) == 0x8c20);
+
+		EATEST_VERIFY(CRC24(a, sizeof(a)) != 0x00000000);
+		EATEST_VERIFY(CRC24(b, sizeof(b)) != 0x00000000);
+		EATEST_VERIFY(CRC24(c, sizeof(c)) != 0x00000000);
+		EATEST_VERIFY(CRC24(d, sizeof(d)) != 0x00000000);
+
+		EATEST_VERIFY(CRC32(a, sizeof(a)) != 0x00000000);
+		EATEST_VERIFY(CRC32(b, sizeof(b)) != 0x00000000);
+		EATEST_VERIFY(CRC32(c, sizeof(c)) != 0x00000000);
+		EATEST_VERIFY(CRC32(d, sizeof(d)) != 0x00000000);
+
+		EATEST_VERIFY(CRC32Reverse(a, sizeof(a)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(b, sizeof(b)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(c, sizeof(c)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(d, sizeof(d)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(e, sizeof(e)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(h, sizeof(h)) != 0x00000000);
+		EATEST_VERIFY(CRC32Reverse(j, sizeof(j)) != 0x00000000);
+
+		EATEST_VERIFY(CRC64(a, sizeof(a)) != 0x00000000);
+		EATEST_VERIFY(CRC64(b, sizeof(b)) != 0x00000000);
+		EATEST_VERIFY(CRC64(c, sizeof(c)) != 0x00000000);
+		EATEST_VERIFY(CRC64(d, sizeof(d)) != 0x00000000);
+	}
+
+
+	delete[] pDataA;
+	delete[] pDataB;
+	delete[] pData8A;
+	delete[] pData8B;
+	delete[] pData16A;
+	delete[] pData16B;
+	delete[] pData32A;
+	delete[] pData32B; 
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+
+

+ 1122 - 0
test/source/TestInt128.cpp

@@ -0,0 +1,1122 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/Int128_t.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+static inline double FloatAbsoluteDifference(float x1, float x2)
+{
+	return (x1 < x2) ? (x2 - x1) : (x1 - x2);
+}
+
+static inline bool FloatEqual(float x1, float x2)
+{
+	if(x1 < 1e-7f)
+		return (x2 < 1e-7f);
+	else if(x2 < 1e-7f)
+		return (x1 < 1e-7f);
+	else
+		return FloatAbsoluteDifference((x1 - x2) / x1, 1e-7f) < 1e-5f;
+}
+
+static bool CompareSelfTestResult(const char* p1, const char* p2)
+{
+	return strcmp(p1, p2) == 0;
+}
+
+int TestInt128()
+{
+	using namespace EA::StdC;
+
+	EA::UnitTest::Report("TestInt128\n");
+
+	int  nErrorCount(0);
+	char array[256];
+	bool bResult;
+
+	{  // Test of small string assignment
+		EA::StdC::int128_t x("10345");
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "10345"));
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::uint128_t x("10345");
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "10345"));
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::int128_t x("-10345");
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-10345"));
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::int128_t x("0xabcd1234fefe", 16);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::int128_t::kLZEnable, EA::StdC::int128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::int128_t::kLZDisable, EA::StdC::int128_t::kPrefixDisable);
+		EATEST_VERIFY_F(CompareSelfTestResult(array, "abcd1234fefe"), "%s %s", array, "abcd1234fefe");
+
+		x.Int128ToStr(array, NULL, 16,EA::StdC::int128_t:: kLZEnable, EA::StdC::int128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::int128_t::kLZDisable, EA::StdC::int128_t::kPrefixEnable);
+		EATEST_VERIFY_F(CompareSelfTestResult(array, "0xabcd1234fefe"), "%s %s", array, "abcd1234fefe");
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::uint128_t x("0xabcd1234fefe", 16);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::uint128_t::kLZEnable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::uint128_t::kLZDisable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY_F(CompareSelfTestResult(array, "abcd1234fefe"), "%s %s", array, "abcd1234fefe");
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::uint128_t::kLZEnable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "00000000000000000000abcd1234fefe"));
+
+		x.Int128ToStr(array, NULL, 16, EA::StdC::uint128_t::kLZDisable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY_F(CompareSelfTestResult(array, "0xabcd1234fefe"), "%s %s", array, "abcd1234fefe");
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::int128_t x("1010101010", 2);
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::int128_t::kLZEnable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::int128_t::kLZDisable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::int128_t::kLZEnable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::int128_t::kLZDisable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0b1010101010"));
+	}
+
+	{  // Test of small string assignment
+		EA::StdC::uint128_t x("1010101010", 2);
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::uint128_t::kLZEnable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::uint128_t::kLZDisable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::uint128_t::kLZEnable, EA::StdC::uint128_t::kPrefixDisable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010101010"));
+
+		x.Int128ToStr(array, NULL, 2, EA::StdC::uint128_t::kLZDisable, EA::StdC::uint128_t::kPrefixEnable);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0b1010101010"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::int128_t x("141183460469231731687303715884105728");
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "141183460469231731687303715884105728"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::uint128_t x("141183460469231731687303715884105728");
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "141183460469231731687303715884105728"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::int128_t x("0xabcd1234fefeabcd123abcd1234fef", 16);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00abcd1234fefeabcd123abcd1234fef"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::uint128_t x("0xabcd1234fefeabcd123abcd1234fef", 16);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00abcd1234fefeabcd123abcd1234fef"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::int128_t x("1010101010111010111111111000000000001111111110101010101000100111101001010101", 2);
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010111010111111111000000000001111111110101010101000100111101001010101"));
+	}
+
+	{  // Test of large string assignment
+		EA::StdC::uint128_t x("1010101010111010111111111000000000001111111110101010101000100111101001010101", 2);
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1010101010111010111111111000000000001111111110101010101000100111101001010101"));
+	}
+
+	{  //Test of floating point assignment
+		EA::StdC::int128_t x(4294967296.f);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "4294967296"));
+	}
+
+	{  //Test of floating point assignment
+		EA::StdC::int128_t x(-12345672704.f);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-12345672704"));
+	}
+
+	{  //Test of floating point assignment
+		EA::StdC::uint128_t x(0.0);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0"));
+	}
+
+	{  //Test of floating point assignment
+		EA::StdC::uint128_t x(3456345634563456.0);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "3456345634563456"));
+	}
+
+	{  //Test of floating point assignment
+		EA::StdC::int128_t x(-123456345634563456.0);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-123456345634563456"));
+	}
+
+	{  // Test of EASTDC_INT128_MIN
+		EA::StdC::int128_t x(EASTDC_INT128_MIN);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-170141183460469231731687303715884105728"));
+	}
+
+	{  // Test of EASTDC_INT128_MIN
+		EA::StdC::int128_t x(EASTDC_INT128_MIN);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x80000000000000000000000000000000"));
+	}
+
+	{  // Test of EASTDC_UINT128_MIN
+		EA::StdC::uint128_t x(EASTDC_UINT128_MIN);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0"));
+	}
+
+	{  // Test of EASTDC_INT128_MAX
+		EA::StdC::int128_t x(EASTDC_INT128_MAX);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "170141183460469231731687303715884105727"));
+	}
+
+	{  // Test of EASTDC_INT128_MAX
+		EA::StdC::int128_t x(EASTDC_INT128_MAX);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x7fffffffffffffffffffffffffffffff"));
+	}
+
+	{  // Test of EASTDC_UINT128_MAX
+		EA::StdC::uint128_t x(EASTDC_UINT128_MAX);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0xffffffffffffffffffffffffffffffff"));
+	}
+
+	{  // Test of unary operator-
+		EA::StdC::int128_t x("123456781234567812345678");
+		x = -x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-123456781234567812345678"));
+	}
+
+	{  // Test of unary operator-
+		EA::StdC::int128_t x("-123456781234567812345678");
+		x = -x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "123456781234567812345678"));
+	}
+
+	{  // Test of unary operator-
+		EA::StdC::int128_t x("1234");
+		x = -x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-1234"));
+	}
+
+	{  // Test of unary operator-
+		EA::StdC::uint128_t x("-1234");
+		x = -x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1234"));
+	}
+
+	{  // Test of unary operator+
+		EA::StdC::int128_t x("123456781234567812345678");
+		x = +x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "123456781234567812345678"));
+	}
+
+	{  // Test of unary operator+
+		EA::StdC::uint128_t x("123456781234567812345678");
+		x = +x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "123456781234567812345678"));
+	}
+
+	{  // Test of unary operator+
+		EA::StdC::int128_t x("-123456781234567812345678");
+		x = +x;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-123456781234567812345678"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::int128_t x("0x77777777000000008888888800000000", 16);
+		x >>= 32;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000777777770000000088888888"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::uint128_t x("0x77777777000000008888888800000000", 16);
+		x >>= 32;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000777777770000000088888888"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::int128_t x("0x77777777000000008888888800000000", 16);
+		x >>= -16;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x77770000000088888888000000000000"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::uint128_t x("0x77777777000000008888888800000000", 16);
+		x >>= -16;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x77770000000088888888000000000000"));
+	}
+
+	{  // Test of unary operator<<
+		EA::StdC::int128_t x("0x77777777000000008888888800000000", 16);
+		x <<= 32;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000888888880000000000000000"));
+	}
+
+	{  // Test of unary operator<<
+		EA::StdC::uint128_t x("0x77777777000000008888888800000000", 16);
+		x <<= 32;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00000000888888880000000000000000"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::int128_t x("0x77777777000000008888888800000000", 16);
+		x <<= -16;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00007777777700000000888888880000"));
+	}
+
+	{  // Test of unary operator>>
+		EA::StdC::uint128_t x("0x77777777000000008888888800000000", 16);
+		x <<= -16;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x00007777777700000000888888880000"));
+	}
+
+	{  // Test of unary operator!
+		EA::StdC::int128_t x("0");
+		bResult = !x;
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of unary operator!
+		EA::StdC::uint128_t x("0");
+		bResult = !x;
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of unary operator!
+		EA::StdC::int128_t x("-1");
+		bResult = !x;
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of unary operator!
+		EA::StdC::uint128_t x("-1");
+		bResult = !x;
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of unary operator~
+		EA::StdC::int128_t x("0x77777777000000008888888800000000", 16);
+		x = ~x;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x88888888ffffffff77777777ffffffff"));
+	}
+
+	{  // Test of unary operator~
+		EA::StdC::uint128_t x("0x77777777000000008888888800000000", 16);
+		x = ~x;
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x88888888ffffffff77777777ffffffff"));
+	}
+
+	{  // Test of operator^
+		EA::StdC::int128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::int128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x ^ y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1111111111111111111111111111111111010101010101010101010101010101"));
+	}
+
+	{  // Test of operator^
+		EA::StdC::uint128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::uint128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x ^ y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1111111111111111111111111111111111010101010101010101010101010101"));
+	}
+
+	{  // Test of operator|
+		EA::StdC::int128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::int128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x | y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1111111111111111111111111111111111111111111111111111111111111111"));
+	}
+
+	{  // Test of operator|
+		EA::StdC::uint128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::uint128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x | y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1111111111111111111111111111111111111111111111111111111111111111"));
+	}
+
+	{  // Test of operator&
+		EA::StdC::int128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::int128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x & y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "101010101010101010101010101010"));
+	}
+
+	{  // Test of operator&
+		EA::StdC::uint128_t x("1010101010101010101010101010101010101010101010101010101010101010", 2);
+		EA::StdC::uint128_t y("0101010101010101010101010101010101111111111111111111111111111111", 2);
+		x = x & y;
+		x.Int128ToStr(array, NULL, 2);
+		EATEST_VERIFY(CompareSelfTestResult(array, "101010101010101010101010101010"));
+	}
+
+	{  // Test of operator==
+		EA::StdC::int128_t x("123456781234567812345678");
+		EA::StdC::int128_t y("123456781234567812345678");
+		bResult = (x == y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator==
+		EA::StdC::uint128_t x("123456781234567812345678");
+		EA::StdC::uint128_t y("123456781234567812345678");
+		bResult = (x == y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator==
+		EA::StdC::int128_t x("123456781234567812345678");
+		bResult = (x == 100);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator==
+		EA::StdC::uint128_t x("123456781234567812345678");
+		bResult = (x == 100L);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator!=
+		EA::StdC::int128_t x("123123123123123123");
+		EA::StdC::int128_t y("123123123123123123");
+		bResult = (x != y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	 {  // Test of operator!=
+		 EA::StdC::uint128_t x("123123123123123123");
+		 EA::StdC::uint128_t y("123123123123123123");
+		bResult = (x != y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator!=
+		EA::StdC::int128_t x("123456781234567812345678");
+		bResult = (x != 0x12341234L);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator!=
+		EA::StdC::uint128_t x("123456781234567812345678");
+		bResult = (x != 0x12341234L);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("1000");
+		EA::StdC::int128_t y("2000");
+		bResult = (x > y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::uint128_t x("1000");
+		EA::StdC::uint128_t y("2000");
+		bResult = (x > y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("9999999999999999999999999999999");
+		EA::StdC::int128_t y("8888888888888888888888888888888");
+		bResult = (x > y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::uint128_t x("9999999999999999999999999999999");
+		EA::StdC::uint128_t y("8888888888888888888888888888888");
+		bResult = (x > y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("-9999999999999999999999999999999");
+		EA::StdC::int128_t y("-8888888888888888888888888888888");
+		bResult = (x > y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("-1000");
+		EA::StdC::int128_t y("-2000");
+		bResult = (x > y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("1000");
+		EA::StdC::int128_t y("-2000");
+		bResult = (x > y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>
+		EA::StdC::int128_t x("-1000");
+		EA::StdC::int128_t y("2000");
+		bResult = (x > y);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of operator>=
+		EA::StdC::int128_t x("123456781234567812345678");
+		EA::StdC::int128_t y("123456781234567812345678");
+		bResult = (x >= y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>=
+		EA::StdC::uint128_t x("123456781234567812345678");
+		EA::StdC::uint128_t y("123456781234567812345678");
+		bResult = (x >= y);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>=
+		EA::StdC::int128_t x("-12345678");
+		bResult = ((int32_t)-12345678 >= x);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>=
+		EA::StdC::uint128_t x("12345678");
+		bResult = ((int32_t)12345679 >= x);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of operator>=
+		EA::StdC::uint128_t x("12345679");
+		bResult = ((int32_t)1234567 >= x);
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of AsBool
+		EA::StdC::int128_t x("0");
+		bResult = (x.AsBool());
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of AsBool
+		EA::StdC::uint128_t x("0");
+		bResult = (x.AsBool());
+		EATEST_VERIFY(!bResult);
+	}
+
+	{  // Test of AsBool
+		EA::StdC::int128_t x("-500");
+		bResult = (x.AsBool());
+		EATEST_VERIFY(bResult);
+	}
+
+
+	{  // Test of AsInt8
+		EA::StdC::int128_t x("20");
+		bResult = (x.AsInt8() == int8_t(20));
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt8
+		EA::StdC::uint128_t x("20");
+		bResult = (x.AsInt8() == int8_t(20));
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt8
+		uint64_t x64 = UINT64_C(0x3333333322222222);
+		bResult = ((int8_t)x64 == int8_t(0x22));
+		EATEST_VERIFY(bResult);
+	}
+	{
+		uint64_t x64 = UINT64_C(0x9999999922222222);
+		bResult = ((int8_t)x64 == int8_t(0x22));
+		EATEST_VERIFY(bResult);
+	}
+	{
+		EA::StdC::uint128_t x("0x55555555444444443333333322222222", 16);
+		bResult = (x.AsInt8() == int8_t(0x22));
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt8
+		EA::StdC::int128_t x("-20");
+		bResult = (x.AsInt8() == int8_t(-20));
+		EATEST_VERIFY(bResult);
+	}
+
+
+	{  // Test of AsInt64
+		EA::StdC::int128_t x("18374403898749255808");
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			bResult = (x.AsUint64() == UINT64_C(18374403898749255808));
+		#else
+			bResult = (x.AsUint64() == 0xFEFEFEFE80808080ULL);            
+		#endif
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt64
+		EA::StdC::uint128_t x("18374403898749255808");
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			bResult = (x.AsUint64() == UINT64_C(18374403898749255808));
+		#else
+			bResult = (x.AsUint64() == 0xFEFEFEFE80808080ULL);
+		#endif
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt64
+		EA::StdC::int128_t x("-9223372036854775808");
+		bResult = (x.AsInt64() == INT64_MIN);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsInt64
+		EA::StdC::uint128_t x("8374403898749255808");
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			bResult = (x.AsInt64() == INT64_C(8374403898749255808));
+		#else
+			bResult = (x.AsInt64() == (int64_t)0x7437DBF9F6988080LL);
+		#endif
+		
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsFloat
+		EA::StdC::int128_t x("18374403898749255808");
+		volatile float actual = x.AsFloat();                // This prevents the FPU from cheating by using higher internal precision.
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			volatile float desired = float(UINT64_C(18374403898749255808));
+		#else
+			volatile float desired = float(0xFEFEFEFE80808080ULL);
+		#endif
+		bResult = FloatEqual(actual, desired);
+
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsFloat
+		EA::StdC::uint128_t x("18374403898749255808");
+		volatile float actual = x.AsFloat();                // This prevents the FPU from cheating by using higher internal precision.
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			volatile float desired = float(UINT64_C(18374403898749255808));
+		#else
+			volatile float desired = float(0xFEFEFEFE80808080ULL);
+		#endif
+		bResult = FloatEqual(actual, desired);
+		EATEST_VERIFY(bResult);
+	}
+
+	{  // Test of AsFloat
+		EA::StdC::int128_t x("-18374403898749255808");
+		volatile float actual = x.AsFloat();                 // This prevents the FPU from cheating by using higher internal precision.
+		#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ >= 4)
+			volatile float desired = -float(UINT64_C(18374403898749255808));
+		#else
+			volatile float desired = -float(0xFEFEFEFE80808080ULL);
+		#endif
+		bResult = FloatEqual(actual, desired);
+		EATEST_VERIFY(bResult);
+	}
+
+	{
+		// Test utility functions
+		// int      GetBit(int nIndex) const;
+		// void     SetBit(int nIndex, int value);
+		// uint8_t  GetPartUint8 (int nIndex) const;
+		// uint16_t GetPartUint16(int nIndex) const;
+		// uint32_t GetPartUint32(int nIndex) const;
+		// uint64_t GetPartUint64(int nIndex) const;
+		// void     SetPartUint8 (int nIndex, uint8_t  value);
+		// void     SetPartUint16(int nIndex, uint16_t value);
+		// void     SetPartUint32(int nIndex, uint32_t value);
+		// void     SetPartUint64(int nIndex, uint64_t value);
+		// bool     IsZero() const;
+		// void     SetZero();
+		// void     TwosComplement();
+		// void     InverseTwosComplement();
+
+		EA::StdC::int128_t x(0);
+
+		// uint8_t  GetPartUint8 (int nIndex) const;
+		// void     SetPartUint8 (int nIndex, uint8_t  value);
+		for(uint8_t i8 = 0; (uint8_t)(i8 < 16/sizeof(uint8_t)); i8++)
+			x.SetPartUint8((int)i8, i8);
+		for(uint8_t i8 = 0; (uint8_t)(i8 < 16/sizeof(uint8_t)); i8++)
+			EATEST_VERIFY(x.GetPartUint8((int)i8) == i8);
+
+		// uint16_t GetPartUint16(int nIndex) const;
+		// void     SetPartUint16(int nIndex, uint16_t value);
+		for(uint16_t i16 = 0; i16 < (uint16_t)(16/sizeof(uint16_t)); i16++)
+			x.SetPartUint16((int)i16, i16);
+		for(uint16_t i16 = 0; i16 < (uint16_t)(16/sizeof(uint16_t)); i16++)
+			EATEST_VERIFY(x.GetPartUint16((int)i16) == i16);
+
+		// uint32_t GetPartUint32(int nIndex) const;
+		// void     SetPartUint32(int nIndex, uint32_t value);
+		for(uint32_t i32 = 0; (uint32_t)(i32 < 16/sizeof(uint32_t)); i32++)
+			x.SetPartUint32((int)i32, i32);
+		for(uint32_t i32 = 0; (uint32_t)(i32 < 16/sizeof(uint32_t)); i32++)
+			EATEST_VERIFY(x.GetPartUint32((int)i32) == i32);
+
+		// uint64_t GetPartUint64(int nIndex) const;
+		// void     SetPartUint64(int nIndex, uint64_t value);
+		for(uint64_t i64 = 0; i64 < (uint64_t)(16/sizeof(uint64_t)); i64++)
+			x.SetPartUint64((int)i64, i64);
+		for(uint64_t i64 = 0; i64 < (uint64_t)(16/sizeof(uint64_t)); i64++)
+			EATEST_VERIFY(x.GetPartUint64((int)i64) == i64);
+
+
+		x = EA::StdC::int128_t("0x11223344556677880123456789ABCDEF", 0);
+
+		// uint8_t  GetPartUint8 (int nIndex) const;
+		bResult = (x.GetPartUint8(0) == 0xEF);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(1) == 0xCD);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(2) == 0xaB);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(3) == 0x89);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(4) == 0x67);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(5) == 0x45);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(6) == 0x23);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(7) == 0x01);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(8) == 0x88);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(9) == 0x77);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(10) == 0x66);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(11) == 0x55);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(12) == 0x44);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(13) == 0x33);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(14) == 0x22);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint8(15) == 0x11);
+		EATEST_VERIFY(bResult);
+
+		// uint16_t GetPartUint16(int nIndex) const;
+		bResult = (x.GetPartUint16(0) == 0xCDEF);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(1) == 0x89AB);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(2) == 0x4567);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(3) == 0x0123);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(4) == 0x7788);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(5) == 0x5566);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(6) == 0x3344);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint16(7) == 0x1122);
+		EATEST_VERIFY(bResult);
+
+		// uint32_t GetPartUint32(int nIndex) const;
+		bResult = (x.GetPartUint32(0) == 0x89ABCDEF);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint32(1) == 0x01234567);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint32(2) == 0x55667788);
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint32(3) == 0x11223344);
+		EATEST_VERIFY(bResult);
+
+		// uint64_t GetPartUint64(int nIndex) const;
+		bResult = (x.GetPartUint64(0) == UINT64_C(0x0123456789ABCDEF));
+		EATEST_VERIFY(bResult);
+
+		bResult = (x.GetPartUint64(1) == UINT64_C(0x1122334455667788));
+		EATEST_VERIFY(bResult);
+
+		// bool     IsZero() const;
+		// void     SetZero();
+		bResult = x.IsZero();
+		EATEST_VERIFY(!bResult);
+
+		x.SetZero();
+		bResult = x.IsZero();
+		EATEST_VERIFY(bResult);
+		bResult = (x.GetPartUint64(0) == 0 && x.GetPartUint64(1) == 0);
+		EATEST_VERIFY(bResult);
+
+
+		// int      GetBit(int nIndex) const;
+		// void     SetBit(int nIndex, int value);
+		x = EA::StdC::int128_t("0b10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010", 0);
+
+		for(int i = 0; i < 128; i++)
+			EATEST_VERIFY(x.GetBit(i) == (i % 2));
+		for(int i = 0; i < 128; i++)
+			x.SetBit(i, x.GetBit(i) ? 0 : 1);
+		for(int i = 0; i < 128; i++)
+			EATEST_VERIFY(x.GetBit(i) == ((i + 1) % 2));
+
+		// void     TwosComplement();
+		// void     InverseTwosComplement();
+		x = EA::StdC::int128_t(1);
+		x.TwosComplement();
+		EATEST_VERIFY(x == -1);
+
+		x.InverseTwosComplement();
+		EATEST_VERIFY(x == 1);
+	}
+
+	{  // Test of operator += / -=
+		EA::StdC::int128_t y("-10000000000000000");
+		y += 3000L;
+		y -= 3000.0;
+		y += UINT64_C(3000);
+		y -= 3000.f;
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-10000000000000000"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t y;
+		y = 120L * EA::StdC::int128_t("-10000000000000000");
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-1200000000000000000"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t  x("-10000000000000000");
+		EA::StdC::uint128_t y(120L);
+		x = x * y;
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-1200000000000000000"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t x((int64_t)INT64_C(-10000000000000000));
+		EA::StdC::int128_t y((int64_t)INT64_C(-20000000000000002));
+		y *= x;
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "200000000000000020000000000000000"));
+	} 
+
+	{  // Test of math
+		EA::StdC::uint128_t x((int64_t)INT64_C(-10000000000000000));
+		EA::StdC::uint128_t y((int64_t)INT64_C(-20000000000000002));
+		y *= x;
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "200000000000000020000000000000000"));
+	} 
+
+	{  // Test of math
+		EA::StdC::int128_t y;
+		y = 0L / EA::StdC::int128_t("-10000000000000000");
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0"));
+	}
+
+	{  // Test of math
+		EA::StdC::uint128_t y;
+		y = 0L / EA::StdC::uint128_t("10000000000000000");
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t y;
+		y = EA::StdC::int128_t("-10000000000000000") / 10L;
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "-1000000000000000"));
+	}
+
+	{  // Test of math
+		EA::StdC::uint128_t y;
+		y = EA::StdC::int128_t("10000000000000000") / 10L;
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "1000000000000000"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t x;
+		EA::StdC::int128_t y(1);
+		EA::StdC::int128_t z(2);
+		x = (y ^ z) + (y & z) + (y | z);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "6"));
+	}
+
+	{  // Test of math
+		EA::StdC::uint128_t x;
+		EA::StdC::uint128_t y(1L);
+		EA::StdC::uint128_t z(2L);
+		x = (y ^ z) + (y & z) + (y | z);
+		x.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "6"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t x;
+		EA::StdC::int128_t y("0x11111111000100001111111100000001", 16);
+		EA::StdC::int128_t z("0x22222222000100002222222200000001", 16);
+		x = (y ^ z) + (y & z) + (y | z);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x66666666000200006666666600000002"));
+	}
+
+	{  // Test of math
+		EA::StdC::uint128_t x;
+		EA::StdC::uint128_t y("0x11111111000100001111111100000001", 16);
+		EA::StdC::uint128_t z("0x22222222000100002222222200000001", 16);
+		x = (y ^ z) + (y & z) + (y | z);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x66666666000200006666666600000002"));
+	}
+
+	{  // Test of math
+		int32_t        a(17);
+		int16_t        b(26);
+		int32_t        c(45);
+		int64_t        d(77);
+		uint16_t       e(25);
+		EA::StdC::int128_t       x(13L);
+		EA::StdC::int128_t       y;
+
+		y = (((x + (a + b) * 37) / c) * x) % (d + e);
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "47"));
+	}
+
+	{  // Test of math
+		int            a(17);
+		short          b(26);
+		EA::StdC::int128_t       c(45L);
+		int64_t        d(77);
+		unsigned short e(25);
+		EA::StdC::uint128_t      x(13L);
+		EA::StdC::uint128_t      y;
+
+		y = (((x + (a + b) * 37L) / c) * x) % (d + e);
+		y.Int128ToStr(array, NULL, 10);
+		EATEST_VERIFY(CompareSelfTestResult(array, "47"));
+	}
+
+	{  // Test of math
+		EA::StdC::int128_t x;
+		EA::StdC::int128_t y("0x11111111000100001111111100000001", 16);
+		EA::StdC::uint128_t z("0x22222222000100002222222200000001", 16);
+		x = (y ^ z) + (y & z) + (y | z);
+		x.Int128ToStr(array, NULL, 16);
+		EATEST_VERIFY(CompareSelfTestResult(array, "0x66666666000200006666666600000002"));
+	}
+
+
+	{
+		//  int128_t  int128_t::StrToInt128(const char*    pValue, char**    ppEnd, int base) const;
+		//  int128_t  int128_t::StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base) const;
+		// EA::StdC::uint128_t EA::StdC::uint128_t::StrToInt128(const char*    pValue, char**    ppEnd, int base) const;
+		// EA::StdC::uint128_t EA::StdC::uint128_t::StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base) const;
+
+		char*     pEnd;
+		EA::StdC::int128_t  i128;
+		EA::StdC::uint128_t u128;
+
+		{   // Base 2
+			char strBase2[]  = "0b101_";  // Decimal 5
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase2, &pEnd, 0);
+			EATEST_VERIFY((i128.AsInt32() == 5) && (*pEnd == '_'));
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase2, &pEnd, 2);
+			EATEST_VERIFY((i128.AsInt32() == 5) && (*pEnd == '_'));
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase2 + 2, &pEnd, 2);
+			EATEST_VERIFY((i128.AsInt32() == 5) && (*pEnd == '_'));
+
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase2, &pEnd, 0);
+			EATEST_VERIFY((u128.AsInt32() == 5) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase2, &pEnd, 2);
+			EATEST_VERIFY((u128.AsInt32() == 5) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase2 + 2, &pEnd, 2);
+			EATEST_VERIFY((u128.AsInt32() == 5) && (*pEnd == '_'));
+		}
+
+		/* Base 8 is not yet supported by StrToInt128. 
+		{   // Base 8
+			char strBase8[] = "032_";    // Decimal 26
+
+			i128 = int128_t::StrToInt128(strBase8, &pEnd, 0);
+			EATEST_VERIFY((i128.AsInt32() == 26) && (*pEnd == '_'));
+
+			i128 = int128_t::StrToInt128(strBase8, &pEnd, 8);
+			EATEST_VERIFY((i128.AsInt32() == 26) && (*pEnd == '_'));
+
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase8, &pEnd, 0);
+			EATEST_VERIFY((u128.AsInt32() == 26) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase8, &pEnd, 8);
+			EATEST_VERIFY((u128.AsInt32() == 26) && (*pEnd == '_'));
+		}
+		*/
+
+		{   // Base 10
+			char strBase10[] = "32_";    // Decimal 32
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase10, &pEnd, 0);
+			EATEST_VERIFY((i128.AsInt32() == 32) && (*pEnd == '_'));
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase10, &pEnd, 10);
+			EATEST_VERIFY((i128.AsInt32() == 32) && (*pEnd == '_'));
+
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase10, &pEnd, 0);
+			EATEST_VERIFY((u128.AsInt32() == 32) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase10, &pEnd, 10);
+			EATEST_VERIFY((u128.AsInt32() == 32) && (*pEnd == '_'));
+		}
+
+		{   // Base 16
+			char strBase16[] = "0x32_";    // Decimal 50
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase16, &pEnd, 0);
+			EATEST_VERIFY((i128.AsInt32() == 50) && (*pEnd == '_'));
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase16, &pEnd, 16);
+			EATEST_VERIFY((i128.AsInt32() == 50) && (*pEnd == '_'));
+
+			i128 = EA::StdC::int128_t::StrToInt128(strBase16 + 2, &pEnd, 16);
+			EATEST_VERIFY((i128.AsInt32() == 50) && (*pEnd == '_'));
+
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase16, &pEnd, 0);
+			EATEST_VERIFY((u128.AsInt32() == 50) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase16, &pEnd, 16);
+			EATEST_VERIFY((u128.AsInt32() == 50) && (*pEnd == '_'));
+
+			u128 = EA::StdC::uint128_t::StrToInt128(strBase16 + 2, &pEnd, 16);
+			EATEST_VERIFY((u128.AsInt32() == 50) && (*pEnd == '_'));
+		}
+	}
+
+	return nErrorCount;
+}
+
+

+ 484 - 0
test/source/TestMathHelp.cpp

@@ -0,0 +1,484 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdC/EAMathHelp.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EABitTricks.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EASTL/string.h>
+#include <EAAssert/eaassert.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <float.h>
+#include <stdio.h>
+#include <math.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+using namespace EA::StdC;
+
+
+
+template<class T, class U, size_t count>
+bool TestArray(const char *testname, T (&values)[count], U (*testFunc)(T), U (*referenceFunc)(T), bool round_adjust = false, bool unit_only = false)
+{
+	typedef U tResult;
+
+	//const bool isUnsigned = ((T)-1 >= 0);
+
+	for(size_t i = 0; i < count; ++i)
+	{
+		const T& v = values[i];
+
+		//if (isUnsigned && (v < 0)) // This can never be true.
+		//    continue;
+
+		if (unit_only && (v < 0 || v > 1))
+			continue;
+
+		const tResult testResult = testFunc(v);
+		const tResult refResult  = referenceFunc(v);
+
+		if (testResult != refResult)
+		{
+			// If we are testing a rounding function, account for differences between
+			// always-up and round-to-nearest-even
+			if (round_adjust)
+			{
+				if (v == ((int32_t)v + ((v < 0) ? -0.5f : +0.5f)))
+				{
+					if (refResult == testFunc(v - 0.01f) || 
+						refResult == testFunc(v + 0.01f))
+					{
+						continue;
+					}
+				}
+			}
+
+			EA::UnitTest::Report("    Function \"%s\" FAILED at index %d\n", testname, (int)i);
+			EA::UnitTest::Report("        input[i]:       %s\n", eastl::to_string(v).c_str());
+			EA::UnitTest::Report("        test function:  %s\n", eastl::to_string(testResult).c_str()); 
+			EA::UnitTest::Report("        reference:      %s\n", eastl::to_string(refResult).c_str()); 
+
+			return false;
+		}
+	}
+
+	// EA::UnitTest::Report("    Function \"%s\" passed.\n", testname);
+	return true;
+}
+
+
+
+///////////////////////////////////////////////////////////////////
+// reference implementations
+//
+EA_NO_UBSAN static uint32_t ref_RoundToUint32(float32_t v)
+{
+	return (uint32_t)floorf(v + 0.5f);
+}
+
+
+static int32_t ref_FloorToInt32(float32_t v)
+{
+	return (int32_t)floorf(v);
+}
+
+
+static int32_t ref_CeilToInt32(float32_t v)
+{
+	return (int32_t)ceilf(v);
+}
+
+
+static int32_t ref_RoundToInt32(float32_t v)
+{
+	return (int32_t)floorf(v + 0.5f);
+}
+
+
+static int32_t ref_TruncateToInt32(float32_t v)
+{
+	return (int32_t)v;
+}
+
+
+EA_NO_UBSAN static uint8_t ref_UnitFloatToUint8(float fValue)
+{
+	return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
+}
+
+static uint8_t ref_ClampUnitFloatToUint8(float fValue)
+{
+	if (fValue < 0.0f)
+		fValue = 0.0f;
+	if (fValue > 1.0f)
+		fValue = 1.0f;
+
+	return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
+}
+
+
+///////////////////////////////////////////////////////////////////
+// tests
+//
+#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_WINDOWS)
+	static bool IsFPUModePP()
+	{
+		return (_controlfp(0, 0) & _MCW_PC) == _PC_24;
+	}
+#else
+	static bool IsFPUModePP()
+	{
+		return false;
+	}
+#endif
+
+static int32_t BitReprOfFloat(float f)
+{
+	union { float f; int32_t i; } typepun;
+	typepun.f = f;
+	return typepun.i;
+}
+
+static int TestMathHelpConversions(const char* /*testmode*/)
+{
+	int nErrorCount(0);
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Zero tests
+	//
+	// Zero must convert to zero, exactly.
+	//
+
+	static float zerosource[2] = { 0, -sqrtf(0) };
+
+	// EA::UnitTest::Report("\nZero tests (%s):\n", testmode);
+
+	nErrorCount += !TestArray("RoundToUint32",          zerosource, RoundToUint32,          ref_RoundToUint32);
+	nErrorCount += !TestArray("RoundToInt32",           zerosource, RoundToInt32,           ref_RoundToInt32);
+	nErrorCount += !TestArray("FloorToInt32",           zerosource, FloorToInt32,           ref_FloorToInt32);
+	nErrorCount += !TestArray("CeilToInt32",            zerosource, CeilToInt32,            ref_CeilToInt32);
+	nErrorCount += !TestArray("TruncateToInt32",        zerosource, TruncateToInt32,        ref_TruncateToInt32);
+	nErrorCount += !TestArray("FastRoundToInt23",       zerosource, FastRoundToInt23,       ref_RoundToInt32);
+	nErrorCount += !TestArray("UnitFloatToUint8",       zerosource, UnitFloatToUint8,       ref_UnitFloatToUint8);
+	nErrorCount += !TestArray("ClampUnitFloatToUint8",  zerosource, ClampUnitFloatToUint8,  ref_ClampUnitFloatToUint8);
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Square root ramp tests
+	//
+	// These are designed to catch basic errors. One nasty test case is
+	// negative zero -- this is known to trip EAMath's IntRound().
+	//
+
+	static float source[3072];
+
+	for(int i = 0; i < 256; ++i)
+	{ 
+		source[i*12+ 8] = source[i*12+4] = source[i*12+0] =  sqrtf((float)i);
+		source[i*12+ 9] = source[i*12+5] = source[i*12+1] =  sqrtf((float)i + 0.5f);
+		source[i*12+10] = source[i*12+6] = source[i*12+2] = -sqrtf((float)i);
+		source[i*12+11] = source[i*12+7] = source[i*12+3] = -sqrtf((float)i + 0.5f);
+
+		for(int j=0; j<4; ++j)
+		{
+			int32_t v1 = BitReprOfFloat(source[(i * 12) + 4 + j]);
+			int32_t v2 = BitReprOfFloat(source[(i * 12) + 8 + j]);
+
+			if (v1 & 0x7fffffff)
+				--v1;
+			if (v2 & 0x7fffffff)
+				++v2;
+		}
+	}
+
+	// EA::UnitTest::Report("\nSquare root tests (%s):\n", testmode);
+
+	nErrorCount += !TestArray("RoundToUint32",          source, RoundToUint32,          ref_RoundToUint32, true);
+	nErrorCount += !TestArray("RoundToInt32",           source, RoundToInt32,           ref_RoundToInt32, true);
+	nErrorCount += !TestArray("FloorToInt32",           source, FloorToInt32,           ref_FloorToInt32);
+	nErrorCount += !TestArray("CeilToInt32",            source, CeilToInt32,            ref_CeilToInt32);
+	nErrorCount += !TestArray("TruncateToInt32",        source, TruncateToInt32,        ref_TruncateToInt32);
+	nErrorCount += !TestArray("FastRoundToInt23",       source, FastRoundToInt23,       ref_RoundToInt32, true);
+	nErrorCount += !TestArray("UnitFloatToUint8",       source, UnitFloatToUint8,       ref_UnitFloatToUint8, true, true);
+	nErrorCount += !TestArray("ClampUnitFloatToUint8",  source, ClampUnitFloatToUint8,  ref_ClampUnitFloatToUint8, true);
+
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Epsilon tests
+	//
+	// These are designed to catch errors caused by accidentally rounding
+	// off significant bits through adjustment arithmetic. RZMathHelp's
+	// FastRoundToSint32() fails this test.
+	//
+
+	static float epsource[261*6];
+
+	// EA::UnitTest::Report("\nEpsilon tests (%s):\n", testmode);
+
+	for(int i=0; i< 261; ++i)
+	{
+		int j = i - 256;
+		epsource[i*6+0] =  ldexpf(1.0f, j);
+		epsource[i*6+1] = -ldexpf(1.0f, j);
+		epsource[i*6+2] =  ldexpf(1.0f, j) + 1.0f;
+		epsource[i*6+3] = -ldexpf(1.0f, j) + 1.0f;
+		epsource[i*6+4] =  ldexpf(1.0f, j) - 1.0f;
+		epsource[i*6+5] = -ldexpf(1.0f, j) - 1.0f;
+	}
+
+	nErrorCount += !TestArray("RoundToUint32",          epsource, RoundToUint32,            ref_RoundToUint32, true);
+	nErrorCount += !TestArray("RoundToInt32",           epsource, RoundToInt32,             ref_RoundToInt32, true);
+	nErrorCount += !TestArray("FloorToInt32",           epsource, FloorToInt32,             ref_FloorToInt32);
+	nErrorCount += !TestArray("CeilToInt32",            epsource, CeilToInt32,              ref_CeilToInt32);
+	nErrorCount += !TestArray("TruncateToInt32",        epsource, TruncateToInt32,          ref_TruncateToInt32);
+	nErrorCount += !TestArray("FastRoundToInt23",       epsource, FastRoundToInt23,         ref_RoundToInt32, true);
+	nErrorCount += !TestArray("UnitFloatToUint8",       epsource, UnitFloatToUint8,         ref_UnitFloatToUint8, true, true);
+	nErrorCount += !TestArray("ClampUnitFloatToUint8",  epsource, ClampUnitFloatToUint8,    ref_ClampUnitFloatToUint8, true);
+
+	static float epsource2[261*4];
+
+	// EA::UnitTest::Report("\nUnsigned epsilon tests (%s):\n", testmode);
+
+	for(int i=0; i<261; ++i)
+	{
+		int j = i-256;
+		epsource[i*4+0] =  ldexpf(1.0f, j) + 2147483647.0f;
+		epsource[i*4+1] = -ldexpf(1.0f, j) + 2147483647.0f;
+		epsource[i*4+2] =  ldexpf(1.0f, j) + 2147483648.0f;
+		epsource[i*4+3] = -ldexpf(1.0f, j) + 2147483648.0f;
+	}
+
+	nErrorCount += !TestArray("RoundToUint32", epsource2, RoundToUint32, ref_RoundToUint32, true);
+
+
+
+	/////////////////////////////////////////////////////////////////////
+	// Range tests
+	//
+	// These check for internal overflows.
+	//
+
+	static float uint32rangesource[2] = { 0x80000000, 0xfffffe00 };     // We cannot use the correct bounds because single precision (24-bit) significands will round them out of range.
+
+	// EA::UnitTest::Report("\nUint32 range tests (%s):\n", testmode);
+	nErrorCount += !TestArray("RoundToUint32", uint32rangesource, RoundToUint32, ref_RoundToUint32, true);
+
+	static float int32rangesource[2] = { -0x7fffff00, 0x7fffff00 };     // We cannot use the correct bounds because single precision (24-bit) significands will round them out of range.
+
+	// EA::UnitTest::Report("\nInt32 range tests (%s):\n", testmode);
+	nErrorCount += !TestArray("RoundToInt32",       int32rangesource, RoundToInt32,     ref_RoundToInt32, true);
+	nErrorCount += !TestArray("FloorToInt32",       int32rangesource, FloorToInt32,     ref_FloorToInt32);
+	nErrorCount += !TestArray("CeilToInt32",        int32rangesource, CeilToInt32,      ref_CeilToInt32);
+	nErrorCount += !TestArray("TruncateToInt32",    int32rangesource, TruncateToInt32,  ref_TruncateToInt32);
+
+	static float Int23rangesource[2] = { -0x003fffff, 0x003fffff };
+
+	// EA::UnitTest::Report("\nInt23 range tests (%s):\n", testmode);
+	nErrorCount += !TestArray("FastRoundToInt23", Int23rangesource, FastRoundToInt23, ref_RoundToInt32, true);
+
+	static float Uint8rangesource[3] = { 0, 128, 255 };
+
+	nErrorCount += !TestArray("UnitFloatToUint8",       Uint8rangesource, UnitFloatToUint8,         ref_UnitFloatToUint8, true);
+	nErrorCount += !TestArray("ClampUnitFloatToUint8",  Uint8rangesource, ClampUnitFloatToUint8,    ref_ClampUnitFloatToUint8, true);
+
+	return nErrorCount;
+}
+
+
+static int TestMathHelpDiagnosisFunctions(const char* /*testmode*/)
+{
+	static const union
+	{
+		uint32_t   i;
+		float32_t f;
+	} kFloat32Tests[]={
+		{ 0x00000000 } ,         // zero
+		{ 0x80000000 } ,         // negative zero
+		{ 0x3F800000 } ,         // +1
+		{ 0xBF800000 } ,         // -1
+		{ 0x00000001 } ,         // denormal
+		{ 0x80000001 } ,         // negative denormal
+		{ 0x7F800000 } ,         // +Inf
+		{ 0xFF800000 } ,         // -Inf
+		{ 0x7F800001 } ,         // -SNaN
+		{ 0xFF800001 } ,         // +SNaN
+		{ 0x7FFFFFFF } ,         // -QNaN
+		{ 0xFFFFFFFF } ,         // +QNaN
+		{ 0xFFC00000 }           // QNaN indefinite
+	};
+
+	static const union 
+	{
+		uint64_t   i;
+		float64_t f;
+	} kFloat64Tests[]={
+		{ UINT64_C(0x0000000000000000) } ,         // zero
+		{ UINT64_C(0x8000000000000000) } ,         // negative zero
+		{ UINT64_C(0x3FF0000000000000) } ,         // +1
+		{ UINT64_C(0xBFF0000000000000) } ,         // -1
+		{ UINT64_C(0x0000000000000001) } ,         // denormal
+		{ UINT64_C(0x8000000000000001) } ,         // negative denormal
+		{ UINT64_C(0x7FF0000000000000) } ,         // +Inf
+		{ UINT64_C(0xFFF0000000000000) } ,         // -Inf
+		{ UINT64_C(0x7FF0000000000001) } ,         // -SNaN
+		{ UINT64_C(0xFFF0000000000001) } ,         // +SNaN
+		{ UINT64_C(0x7FFFFFFFFFFFFFFF) } ,         // -QNaN
+		{ UINT64_C(0xFFFFFFFFFFFFFFFF) } ,         // +QNaN
+		{ UINT64_C(0xFFF8000000000000) }           // QNaN indefinite
+	};
+
+	static const struct {
+		bool mDenormal:1;
+		bool mInfinite:1;
+		bool mNAN:1;
+		bool mIndefinite:1;
+	} kTestReference[]={
+		{ false, false, false, false },    // zero
+		{ false, false, false, false },    // negative zero
+		{ false, false, false, false },    // +1
+		{ false, false, false, false },    // -1
+		{ true,  false, false, false },    // denormal
+		{ true,  false, false, false },    // negative denormal
+		{ false, true,  false, false },    // +Inf
+		{ false, true,  false, false },    // -Inf
+		{ false, false, true,  false },    // -QNaN
+		{ false, false, true,  false },    // +QNaN
+		{ false, false, true,  false },    // -SNaN
+		{ false, false, true,  false },    // +SNaN
+		{ false, false, true,  true  },    // -QNaN (indefinite)
+	};
+
+	int nTestsFailed = 0;
+
+	// EA::UnitTest::Report("\nClassification tests:\n");
+	for(size_t i=0; i<sizeof kTestReference / sizeof kTestReference[0]; ++i)
+	{
+		const float32_t f32 = kFloat32Tests[i].f;
+		const float64_t f64 = kFloat64Tests[i].f;
+		const uint32_t i32 = kFloat32Tests[i].i;
+		const uint64_t i64 = kFloat64Tests[i].i;
+		const bool isDenormal = kTestReference[i].mDenormal;
+		const bool isInfinite = kTestReference[i].mInfinite;
+		const bool isNAN = kTestReference[i].mNAN;
+		const bool isIndefinite = kTestReference[i].mIndefinite;
+		const bool isNormal = !isDenormal && !isInfinite && !isNAN;
+
+		if (IsNormal(f32) != isNormal) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsNormal(float32_t) FAIL: %g (%" PRIx32 ")\n", f32, i32);
+		}
+		if (IsNormal(f64) != isNormal) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsNormal(float64_t) FAIL: %g (%" PRIx64 ")\n", f64, i64);
+		}
+
+		if (IsDenormalized(f32) != isDenormal) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsDenormalized(float32_t) FAIL: %g (%" PRIx32 ")\n", f32, i32);
+		}
+		if (IsDenormalized(f64) != isDenormal) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsDenormalized(float64_t) FAIL: %g (%" PRIx64 ")\n", f64, i64);
+		}
+
+		if (IsIndefinite(f32) != isIndefinite) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsIndefinite(float32_t) FAIL: %g (%" PRIx32 ")\n", f32, i32);
+		}
+		if (IsIndefinite(f64) != isIndefinite) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsIndefinite(float64_t) FAIL: %g (%" PRIx64")\n", f64, i64);
+		}
+
+		if (IsInfinite(f32) != isInfinite) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsInfinite(float32_t) FAIL: %g (%" PRIx32 ")\n", f32, i32);
+		}
+		if (IsInfinite(f64) != isInfinite) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsInfinite(float64_t) FAIL: %g (%" PRIx64 ")\n", f64, i64);
+		}
+
+		if (IsNAN(f32) != isNAN) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsNAN(float32_t) FAIL: %g (%" PRIx32 ")\n", f32, i32);
+		}
+		if (IsNAN(f64) != isNAN) {
+			++nTestsFailed;
+			EA::UnitTest::Report("    IsNAN(float64_t) FAIL: %g (%" PRIx64 ")\n", f64, i64);
+		}
+	}
+
+	return nTestsFailed;
+}
+
+
+static int TestMath()
+{
+	const char* testmode;
+
+	#if defined(EAMATHHELP_MODE_SSE) && EAMATHHELP_MODE_SSE
+		testmode = "SSE";
+		IsFPUModePP(); // Call this only to prevent compiler warnings related to non-use.
+	#elif defined(EAMATHHELP_MODE_X86ASM) && EAMATHHELP_MODE_X86ASM
+		testmode = IsFPUModePP() ? "X86ASM-24" : "X86ASM-53";
+	#else
+		testmode = IsFPUModePP() ? "scalar-24" : "scalar-53";
+	#endif
+
+	return    TestMathHelpConversions(testmode)
+			+ TestMathHelpDiagnosisFunctions(testmode);
+}
+
+
+int TestMathHelp()
+{
+	int nErrorCount = 0;
+
+	EA::UnitTest::Report("TestMathHelp\n");
+
+	nErrorCount += TestMath();
+
+	#if defined(EA_PLATFORM_WINDOWS) // To consider: Enable this for other platforms.
+		// Verify that (HUGE_VAL == EA::StdC::kFloat64Infinity). We do this not because
+		// it's required by the C standard, but because we have code in EAStdC that 
+		// assumes they are equal under VC++. This assumption, if broken, isn't a very 
+		// big deal and no user would likely notice it, but it's a fine detail.
+		double hugeVal = HUGE_VAL;
+		double infVal  = EA::StdC::kFloat64Infinity;
+
+		EATEST_VERIFY(memcmp(&hugeVal, &infVal, sizeof(double)) == 0);
+	#endif
+
+	// To do: Make this work under x64 / Win64. 
+	#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_WIN32) && !EAMATHHELP_MODE_SSE
+		_controlfp(_PC_24, _MCW_PC);
+		nErrorCount += TestMath();
+		_controlfp(_PC_53, _MCW_PC);
+	#endif
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 1309 - 0
test/source/TestMemory.cpp

@@ -0,0 +1,1309 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAMemory.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EARandomDistribution.h>
+#include <EAStdC/EABitTricks.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <string.h>
+#include <EAStdC/EAAlignment.h>
+#include <EASTL/vector.h>
+
+#if defined(_MSC_VER)
+	#pragma warning(push)
+	#pragma warning(disable: 4996) // Function is deprecated.
+	#pragma warning(disable: 6255) // _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
+	#pragma warning(disable: 6211) // Leaking memory due to an exception. Consider using a local catch block to clean up memory.
+	#pragma warning(disable: 6200) // Index '15' is out of valid index range '0' to '9' for non-stack buffer 'kPredefinedMemSizes'
+#endif
+
+
+// The memory we will use for testing.
+static uint8_t* gMem1 = NULL;
+static uint8_t* gMem2 = NULL;
+
+// Define expected fill values for gMem1 / gMem2.
+const uint8_t kByte1 = 0xaa;
+const uint8_t kByte2 = 0xbb;
+
+// For memcpy tests we allocate two large blocks of memory that are 
+// of this alignment. 
+static const size_t kBaseMemAlignment = 65536;
+
+// For memcpy tests we allocate two large blocks of memory that are 
+// of this alignment. We will copy memory around to and from memory 
+// segments within this block.
+static const size_t kBaseMemSize = 16777216; // 16 MiB
+
+// These are some predefined sizes that we test.
+// We also have random size testing.
+static const size_t kPredefinedMemSizes[] = 
+{
+	0,
+	1,
+	24,
+	96,
+	200,
+	1024,
+	4096,       
+	65536,      // 64 KiB
+	1048576,    // 1 MiB
+	8388608     // 8 MiB
+};
+
+static void TestEAAllocaHelper()
+{
+	void* p = EAAlloca(32768);
+	EA_ANALYSIS_ASSUME(p != NULL);
+	// Try to force a reference to the memory
+	memset(p, 0, 1);
+}
+
+
+static int TestEAAlloca()
+{
+	int nErrorCount = 0;
+
+	{
+		void* p = EAAlloca(37); // It's actually possible that this could throw an exception (Microsoft) or a signal (Unix).
+		EA_ANALYSIS_ASSUME(p != NULL);
+		memset(p, 0, 37);
+	}
+
+	{
+		// Call a function using alloca repeatedly to ensure the memory is returned.  If memory is not released when
+		// returning from TestEAAllocaHelper, then the test will run out of memory (or use huge amounts of virtual memory
+		// on PC and likely crash or timeout).
+		for (int i=0; i < 1000000; i++)
+		{
+			TestEAAllocaHelper();
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestEAMalloca()
+{
+	int nErrorCount = 0;
+
+	{
+		void* p = EAMalloca(37);
+		if(p)
+		{
+			memset(p, 0, 37);
+			EAFreea(p);
+		}
+
+		p = EAMalloca(EAMALLOCA_THRESHOLD * 2); // Allocate something that's too large for alloca.
+		if(p)
+		{
+			memset(p, 0, EAMALLOCA_THRESHOLD * 2);
+			EAFreea(p);
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemset()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// uint8_t* Memset8C(void* pDestination, uint8_t c, size_t count);
+	{
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		for(size_t i = 0; i < 16; ++i)
+		{
+			// Randomly choose a copy size, but make sure the predifined ones are always tested.
+			const size_t copyCount = (i < EAArrayCount(kPredefinedMemSizes) && (kPredefinedMemSizes[i] < 4096)) ? kPredefinedMemSizes[i] : r.RandomUint32Uniform(4096);
+			const size_t copySize  = copyCount * sizeof(uint8_t);
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment);
+				uint8_t* pMem1 = gMem1 + offset1;
+
+				EA::StdC::Memset8C(pMem1, kByte2, copyCount); // Copy pMem2's kByte2 values over pMem1's kByte1 values.
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck8(pMem1, kByte2, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+
+	// uint8_t* Memset8_128C(void* pDestination, uint8_t c, size_t uint8Count);
+	{
+		for(size_t i = 0; i < 50; i++)
+		{
+			const size_t copySize  = i * 128;
+			const size_t copyCount = copySize * sizeof(uint8_t);
+
+			uint8_t* pMem1 = gMem1;
+			const void* pCheck;
+
+			EA::StdC::Memset8_128C(pMem1, kByte2, copyCount);
+
+			// Verify memory prior to pMem1 is unmodified.
+			pCheck = Memcheck8(pMem1 - 256, kByte1, 256);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Verify copied memory.
+			pCheck = Memcheck8(pMem1, kByte2, copySize);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Verify memory after pMem1+copySize.
+			pCheck = Memcheck8(pMem1 + copySize, kByte1, 256);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Set the memory back to its original value.
+			memset(pMem1, kByte1, copySize);
+		}
+	}
+
+
+	// uint16_t* Memset16(void* pDestination, uint16_t c, size_t count);
+	{
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		const uint16_t kByte2_16 = ((kByte2 << 8) | (kByte2 + 1));
+
+		for(size_t i = 0; i < 16; ++i)
+		{
+			// Randomly choose a copy count, but make sure the predifined ones are always tested.
+			const size_t copyCount = (i < EAArrayCount(kPredefinedMemSizes) && (kPredefinedMemSizes[i] < 2048)) ? kPredefinedMemSizes[i] : r.RandomUint32Uniform(2048);
+			const size_t copySize  = copyCount * sizeof(uint16_t);
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment) / sizeof(uint16_t) * sizeof(uint16_t); // Divide, then multiply.
+				uint8_t* pMem1 = gMem1 + offset1;
+
+				EA::StdC::Memset16(pMem1, kByte2_16, copyCount);
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck16(pMem1, kByte2_16, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+
+	// uint32_t* Memset32(void* pDestination, uint32_t c, size_t count);
+	{
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		const uint32_t kByte2_32 = ((kByte2 << 8) | (kByte2 + 1));
+
+		for(size_t i = 0; i < 16; ++i)
+		{
+			// Randomly choose a copy count, but make sure the predifined ones are always tested.
+			const size_t copyCount = (i < EAArrayCount(kPredefinedMemSizes) && (kPredefinedMemSizes[i] < 1024)) ? kPredefinedMemSizes[i] : r.RandomUint32Uniform(1024);
+			const size_t copySize  = copyCount * sizeof(uint32_t);
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment) / sizeof(uint32_t) * sizeof(uint32_t); // Divide, then multiply.
+				uint8_t* pMem1 = gMem1 + offset1;
+
+				EA::StdC::Memset32(pMem1, kByte2_32, copyCount);
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck32(pMem1, kByte2_32, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+
+	// uint64_t* Memset64(void* pDestination, uint64_t c, size_t count);
+	{
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		const uint64_t kByte2_64 = ((kByte2 << 8) | (kByte2 + 1));
+
+		for(size_t i = 0; i < 16; ++i)
+		{
+			// Randomly choose a copy count, but make sure the predifined ones are always tested.
+			const size_t copyCount = (i < EAArrayCount(kPredefinedMemSizes) && (kPredefinedMemSizes[i] < 512)) ? kPredefinedMemSizes[i] : r.RandomUint32Uniform(512);
+			const size_t copySize  = copyCount * sizeof(uint64_t);
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint64_t)kBaseMemAlignment) / sizeof(uint64_t) * sizeof(uint64_t); // Divide, then multiply.
+				uint8_t* pMem1 = gMem1 + offset1;
+
+				EA::StdC::Memset64(pMem1, kByte2_64, copyCount);
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck64(pMem1, kByte2_64, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+
+
+	// void* MemsetPointer(void* pDestination, const void* const pValue, size_t count)
+	{
+		const size_t kBufferSize = 2000;
+		void** const pBuffer = new void*[kBufferSize];
+
+		for(size_t i = 1; i < 2000; i *= 3)
+		{
+			memset(pBuffer, 0, kBufferSize * sizeof(void*));
+
+			void* p = MemsetPointer(pBuffer, (void*)(uintptr_t)i, i);
+			EATEST_VERIFY(p == pBuffer);
+
+			for(size_t k = 0; k < 2000; ++k)
+			{
+				if(k < i)
+					EATEST_VERIFY(pBuffer[k] == (void*)(uintptr_t)i);
+				else
+					EATEST_VERIFY(pBuffer[k] == (void*)(uintptr_t)0);
+			}
+		}
+
+		delete[] pBuffer;
+	}
+
+
+	// void* MemsetN(void* pDestination, const void* pSource, size_t sourceBytes, size_t count);
+	{
+		// To do: We need a more extensive test.
+
+		char8_t        buffer[2000];
+		const char8_t* pattern = "012345678";
+		size_t         sl = Strlen(pattern);
+
+		EATEST_VERIFY(buffer == MemsetN(buffer, pattern, sl, 2000));
+		EATEST_VERIFY(buffer[sl] == '0' && buffer[77*sl+1] == '1');
+		EATEST_VERIFY(buffer[sl*33+4] == '4' && buffer[123*sl+8] == '8');
+		EATEST_VERIFY(buffer[sl*98+3] == '3' && buffer[181*sl+6] == '6');
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemfill()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// void Memfill8(void* pDestination, uint8_t c,  size_t byteCount);
+	{
+		const void* result = nullptr;
+		const int ARR_SIZE = 4096;
+
+		uint8_t buf[ARR_SIZE];
+		void* pMem = static_cast<void*>(&buf);
+
+		Memfill8(pMem, kByte1, ARR_SIZE);
+		result = Memcheck8(pMem, kByte1, ARR_SIZE);
+		EATEST_VERIFY(result == NULL);
+
+		Memfill8(pMem, kByte2, ARR_SIZE);
+		result = Memcheck8(pMem, kByte2, ARR_SIZE);
+		EATEST_VERIFY(result == NULL);
+	}
+
+	// void Memfill16(void* pDestination, uint16_t c, size_t byteCount);
+	{
+		// Test different alignments, sizes 0 to 257, 1023 to 1026
+		uint16_t  val16      = 0x1234;
+		uint16_t* val16Array = new uint16_t[2048 + 32];    // To consider: Would it make the code better if we created constants for these 
+		uint8_t*  buf8Array  = new  uint8_t[4096 + 64];    //              sizes, or would it instead just make this harder to follow?
+		uint8_t*  buf8Array2 = new  uint8_t[4096 + 64];
+		size_t    j;
+
+		EATEST_VERIFY(val16Array && buf8Array && buf8Array2);
+
+		Memset16(val16Array, val16, 2048 + 32);
+
+		for(int32_t i = 0; i < 4; i++)
+		{
+			for(j = 0; j <= 257; j++)
+			{
+				memset(buf8Array, 0, 4096);
+				memset(buf8Array2, 0, 4096);
+				Memfill16(buf8Array + i, val16, j);
+
+				for(size_t k = 0; k < (j / sizeof(uint16_t) + 1); ++k)
+					memcpy(buf8Array2 + i + (k * sizeof(uint16_t)), val16Array, j - (k * sizeof(uint16_t)));
+
+				EATEST_VERIFY(memcmp(buf8Array, buf8Array2, 4096) == 0);
+			}
+
+			for(j = 1023; j <= 1026; j++)
+			{
+				memset(buf8Array, 0, 4096);
+				memset(buf8Array2, 0, 4096);
+				Memfill16(buf8Array + i, val16, j);
+
+				for(size_t k = 0; k < (j / sizeof(uint16_t) + 1); ++k)
+					memcpy(buf8Array2 + i + (k * sizeof(uint16_t)), val16Array, j - (k * sizeof(uint16_t)));
+
+				EATEST_VERIFY(memcmp(buf8Array, buf8Array2, 4096) == 0);
+			}
+		}
+
+		delete[] val16Array; 
+		delete[] buf8Array;
+		delete[] buf8Array2;
+	}
+
+	// void Memfill24(void* pDestination, uint32_t c, size_t byteCount);
+	{
+		// To do.
+	}
+
+	// void Memfill32(void* pDestination, uint32_t c, size_t byteCount);
+	{
+		// To do.
+	}
+
+	// void Memfill64(void* pDestination, uint64_t c, size_t byteCount);
+	{
+		// To do.
+	}
+
+	// void MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount);
+	{
+		// To do.
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemclear()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// void MemclearC(void* pDestination, size_t n);
+	{
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		for(size_t i = 0; i < 16; ++i)
+		{
+			// Randomly choose a copy size, but make sure the predefined ones are always tested.
+			const size_t copyCount = (i < EAArrayCount(kPredefinedMemSizes) && (kPredefinedMemSizes[i] < 4096)) ? kPredefinedMemSizes[i] : r.RandomUint32Uniform(4096);
+			const size_t copySize  = copyCount * sizeof(uint8_t);
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment);
+				uint8_t* pMem1 = gMem1 + offset1;
+
+				EA::StdC::MemclearC(pMem1, copyCount); // Set zero values over pMem1's kByte1 values.
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck8(pMem1, 0, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemcheck()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+	const void* pCheck;
+
+	// const void* Memcheck8(const void* p, uint8_t c, size_t byteCount);
+	{
+		const uint8_t bytes[5] = { 0x00, 0x01, 0x01, 0x01, 0x00 };
+
+		pCheck = Memcheck8(bytes + 0, 0x00, 1);
+		EATEST_VERIFY(pCheck == NULL);
+
+		pCheck = Memcheck8(bytes + 0, 0x01, 1);
+		EATEST_VERIFY(pCheck == bytes);
+
+		pCheck = Memcheck8(bytes + 0, 0x00, 2);
+		EATEST_VERIFY(pCheck == bytes + 1);
+
+		pCheck = Memcheck8(bytes + 1, 0x01, 3);
+		EATEST_VERIFY(pCheck == NULL);
+	}
+
+	// const void* Memcheck16(const void* p, uint16_t c, size_t byteCount);
+	{
+		union U16 {
+			uint16_t c16;
+			uint8_t  c8[2];
+		};
+		const U16 bytes[5] = { { 0x0000 }, { 0x0001 }, { 0x0001 }, { 0x0001 }, { 0x0101 } };
+
+		pCheck = Memcheck16(bytes + 0, 0x0000, 2);
+		EATEST_VERIFY(pCheck == NULL);
+
+		pCheck = Memcheck16(bytes + 0, 0x0001, 2);
+		#ifdef EA_SYSTEM_BIG_ENDIAN
+			EATEST_VERIFY(pCheck == bytes[0].c8 + 1);
+		#else
+			EATEST_VERIFY(pCheck == bytes[0].c8 + 0);
+		#endif
+
+		pCheck = Memcheck16(bytes + 1, 0x0001, 6);
+		EATEST_VERIFY(pCheck == NULL);
+
+		pCheck = Memcheck16(bytes[0].c8 + 1, 0x0001, 2);
+		#ifdef EA_SYSTEM_BIG_ENDIAN
+			EATEST_VERIFY(pCheck == bytes[0].c8 + 1);
+		#else
+			EATEST_VERIFY(pCheck == NULL);  // Due to byte ordering, little-endian sees this as matching.
+		#endif
+
+		pCheck = Memcheck16(bytes[0].c8 + 1, 0x0000, 2);
+		#ifdef EA_SYSTEM_BIG_ENDIAN
+			EATEST_VERIFY(pCheck == NULL);  // Due to byte ordering, big-endian sees this as matching.
+		#else
+			EATEST_VERIFY(pCheck == bytes[1].c8 + 0);
+		#endif
+	}
+
+	// const void* Memcheck32(const void* p, uint32_t c, size_t byteCount);
+	{
+		union U32 {
+			uint32_t c32;
+			uint8_t  c8[4];
+		};
+		const U32 bytes[5] = { { 0x00010203 }, { 0x00010203 }, { 0x00010203 }, { 0x00010203 }, { 0x00010203 } };
+
+		for(int i = 0; i <= 4; ++i)
+		{
+			pCheck = Memcheck32(bytes[0].c8 + i, 0x00010203, 9);
+			EATEST_VERIFY(pCheck == NULL);
+		}
+
+		for(int i = 0; i <= 4; ++i)
+		{
+			pCheck = Memcheck32(bytes[0].c8 + i, 0x01020300, 9);
+			EATEST_VERIFY(pCheck != NULL);
+		}
+	}
+
+	// const void* Memcheck64(const void* p, uint64_t c, size_t byteCount);
+	{
+		union U64 {
+			uint64_t c64;
+			uint8_t  c8[8];
+		};
+
+		// Some platforms' (e.g. x86) compilers don't align 64 bit values on 64 bit boundaries. So we guarantee it here, as Memcheck64 expects it.
+		// Additionally, some of the platforms we test for require 16 bit alignment of types, so we use that instead of 8.
+		static EA_ALIGNED(const U64, bytes[5], 16) = { { UINT64_C(0x0001020304050607) }, { UINT64_C(0x0001020304050607) }, { UINT64_C(0x0001020304050607) }, { UINT64_C(0x0001020304050607) }, { UINT64_C(0x0001020304050607) } };
+
+		for(int i = 0; i <= 8; ++i)
+		{
+			pCheck = Memcheck64(bytes[0].c8 + i, UINT64_C(0x0001020304050607), 18);
+			EATEST_VERIFY(pCheck == NULL);
+		}
+
+		for(int i = 0; i <= 8; ++i)
+		{
+			pCheck = Memcheck64(bytes[0].c8 + i, UINT64_C(0x0102030405060700), 18);
+			EATEST_VERIFY(pCheck != NULL);
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemchr()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{   // Memchr8
+		const char8_t* const s = "qwertyuiopASDFGHJKL:!@#$%^&*,=/";
+
+		EATEST_VERIFY((char8_t*)Memchr(s, (char8_t)'q', Strlen(s)) - s ==  0);
+		EATEST_VERIFY((char8_t*)Memchr(s, (char8_t)'F', Strlen(s)) - s == 13);
+		EATEST_VERIFY((char8_t*)Memchr(s, (char8_t)':', Strlen(s)) - s == 19);
+		EATEST_VERIFY((char8_t*)Memchr(s, (char8_t)'&', Strlen(s)) - s == 26);
+	}
+
+	#if EASTDC_MEMCHR16_ENABLED && defined(EA_CHAR16)
+		{   // Memchr16
+			const char16_t* const s = EA_CHAR16("qwertyuiopASDFGHJKL:!@#$%^&*,=/");
+
+			EATEST_VERIFY((char16_t*)Memchr(s, (char16_t)'q', Strlen(s)) - s ==  0);
+			EATEST_VERIFY((char16_t*)Memchr(s, (char16_t)'F', Strlen(s)) - s == 13);
+			EATEST_VERIFY((char16_t*)Memchr(s, (char16_t)':', Strlen(s)) - s == 19);
+			EATEST_VERIFY((char16_t*)Memchr(s, (char16_t)'&', Strlen(s)) - s == 26);
+		}
+	#endif
+
+	return nErrorCount;
+}
+
+
+static int TestMemcmp()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{   // Memcmp8
+		char8_t buffer1[] = "01234567a";
+		char8_t buffer2[] = "01234567b";
+		char8_t buffer3[] = "01234567c";
+
+		EATEST_VERIFY(Memcmp(buffer1, buffer1, 9) == 0);
+		EATEST_VERIFY(Memcmp(buffer2, buffer1, 9) >  0);
+		EATEST_VERIFY(Memcmp(buffer3, buffer2, 9) >  0);
+		EATEST_VERIFY(Memcmp(buffer2, buffer3, 9) <  0);
+		EATEST_VERIFY(Memcmp(buffer1, buffer2, 9) <  0);
+	}
+
+	#if EASTDC_MEMCPY16_ENABLED
+		{   // Memcmp16
+			char16_t buffer1[] = EA_CHAR16("01234567a");
+			char16_t buffer2[] = EA_CHAR16("01234567b");
+			char16_t buffer3[] = EA_CHAR16("01234567c");
+
+			EATEST_VERIFY(Memcmp(buffer1, buffer1, 9) == 0);
+			EATEST_VERIFY(Memcmp(buffer2, buffer1, 9) >  0);
+			EATEST_VERIFY(Memcmp(buffer3, buffer2, 9) >  0);
+			EATEST_VERIFY(Memcmp(buffer2, buffer3, 9) <  0);
+			EATEST_VERIFY(Memcmp(buffer1, buffer2, 9) <  0);
+		}
+	#endif
+
+	return nErrorCount;
+}
+
+
+static int TestMemmem()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	const size_t  kSize = 37;
+	const char8_t buffer1[kSize] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+	EATEST_VERIFY(Memmem(buffer1,     0, "",            0) == NULL);            // An empty haystack always results in NULL, regardless of the needle.
+	EATEST_VERIFY(Memmem(buffer1, kSize, "",            0) == buffer1);         // Otherwise, an empty needle results in success.
+	EATEST_VERIFY(Memmem(buffer1,     0, "_",           1) == NULL);            // 
+	EATEST_VERIFY(Memmem("_",         1, buffer1,   kSize) == NULL);            // Search of a needle that is bigger than the haystack. Always failure.
+	EATEST_VERIFY(Memmem(buffer1, kSize, "_",           1) == NULL);
+	EATEST_VERIFY(Memmem(buffer1, kSize, buffer1,   kSize) == buffer1);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "a",           1) == buffer1);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "abc",         3) == buffer1);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "bcd",         3) == buffer1 + 1);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "tuv",         3) == buffer1 + 19);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "9",           1) == buffer1 + 35);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "789",         3) == buffer1 + 33);
+	EATEST_VERIFY(Memmem(buffer1, kSize, "9__",         3) == NULL);
+	EATEST_VERIFY(Memmem("\1\0",      2, "\1\0",        2) != NULL);
+	EATEST_VERIFY(Memmem("\1\1",      2, "\1\0",        2) == NULL);
+
+	return nErrorCount;
+}
+
+
+static int TestMemcpy()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{   // MemcpyC
+		char8_t buffer1[] = "         ";
+		char8_t buffer2[] = "01234567b";
+
+		EATEST_VERIFY(buffer1 == MemcpyC(buffer1, buffer2, 9));
+		EATEST_VERIFY(Memcmp(buffer2, buffer1, 9) ==  0);
+	}
+
+	#if EASTDC_MEMCPY16_ENABLED
+		{   // Memcpy16
+			char16_t buffer1[] = EA_CHAR16("         ");
+			char16_t buffer2[] = EA_CHAR16("01234567b");
+
+			EATEST_VERIFY(buffer1 == (char16_t*)Memcpy((void*)buffer1, (void*)buffer2, 9 * sizeof(char16_t)));
+			EATEST_VERIFY(memcmp(buffer2, buffer1, 9 * sizeof(char16_t)) ==  0);
+		}
+	#endif
+
+
+	{ // char8_t* MemcpyC(void* pDestination, const void* pSource, size_t nByteCount);
+
+		EA::StdC::Random r;
+		const void* pCheck;
+
+		for(size_t i = 0; i < EAArrayCount(kPredefinedMemSizes); ++i)
+		{
+			const size_t copySize = kPredefinedMemSizes[i];
+
+			for(size_t j = 0; j < 7; ++j)
+			{
+				const size_t offset1 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment);
+				const size_t offset2 = r.RandomUint32Uniform((uint32_t)kBaseMemAlignment);
+
+				uint8_t* pMem1 = gMem1 + offset1;
+				uint8_t* pMem2 = gMem2 + offset2;
+
+				EA::StdC::MemcpyC(pMem1, pMem2, copySize); // Copy pMem2's kByte2 values over pMem1's kByte1 values.
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - kBaseMemAlignment, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck8(pMem1, kByte2, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, kBaseMemAlignment);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+			}
+		}
+	}
+
+
+	{   // char8_t* Memcpy128(void* pDestination, const void* pSource, size_t nByteCount);
+
+		EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
+		EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
+		EA::StdC::Stopwatch stopwatch3(EA::StdC::Stopwatch::kUnitsCPUCycles);
+
+		for(int t = 0; t < 2; t++)
+		{
+			stopwatch1.Reset();
+			stopwatch2.Reset();
+			stopwatch3.Reset();
+
+			for(size_t i = 0; i < 50; i++)
+			{
+				const size_t copySize = i * 128;
+				const void* pCheck;
+
+				uint8_t* pMem1 = gMem1;
+				uint8_t* pMem2 = gMem2;
+
+				stopwatch1.Start();
+				EA::StdC::Memcpy128(pMem1, pMem2, copySize); // Copy pMem2's kByte2 values over pMem1's kByte1 values.
+				stopwatch1.Stop();
+
+				// Verify memory prior to pMem1 is unmodified.
+				pCheck = Memcheck8(pMem1 - 256, kByte1, 256);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify copied memory.
+				pCheck = Memcheck8(pMem1, kByte2, copySize);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Verify memory after pMem1+copySize.
+				pCheck = Memcheck8(pMem1 + copySize, kByte1, 256);
+				EATEST_VERIFY(pCheck == NULL);
+
+				// Set the memory back to its original value.
+				memset(pMem1, kByte1, copySize);
+
+				// Compare to regular memcpy.
+				stopwatch2.Start();
+				memcpy(pMem1, pMem2, copySize);
+				stopwatch2.Stop();
+				memset(pMem1, kByte1, copySize);
+
+				// Compare to regular __builtin_memcpy.
+				stopwatch3.Start();
+				#if defined(__GNUC__)
+					__builtin_memcpy(pMem1, pMem2, copySize);
+				#else
+					memcpy(pMem1, pMem2, copySize);
+				#endif
+				stopwatch3.Stop();
+				memset(pMem1, kByte1, copySize);
+			}
+
+			if(t == 1)
+				EA::UnitTest::ReportVerbosity(1, "Memcpy128: %I64u cycles; memcpy: %I64u cycles, __builtin_memcpy: %I64u\n", 
+												stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(), stopwatch3.GetElapsedTime());
+		}
+	}
+
+	{   // char8_t* Memcpy128C(void* pDestination, const void* pSource, size_t nByteCount);
+		for(size_t i = 0; i < 50; i++)
+		{
+			const size_t copySize = i * 128;
+			const void* pCheck;
+
+			uint8_t* pMem1 = gMem1;
+			uint8_t* pMem2 = gMem2;
+
+			EA::StdC::Memcpy128C(pMem1, pMem2, copySize); // Copy pMem2's kByte2 values over pMem1's kByte1 values.
+
+			// Verify memory prior to pMem1 is unmodified.
+			pCheck = Memcheck8(pMem1 - 256, kByte1, 256);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Verify copied memory.
+			pCheck = Memcheck8(pMem1, kByte2, copySize);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Verify memory after pMem1+copySize.
+			pCheck = Memcheck8(pMem1 + copySize, kByte1, 256);
+			EATEST_VERIFY(pCheck == NULL);
+
+			// Set the memory back to its original value.
+			memset(pMem1, kByte1, copySize);
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestMemmove()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{    // Memmove8
+		char8_t buffer1[] = "..........."; 
+		char8_t buffer2[] = ".......0123"; 
+		char8_t buffer3[] = "0123......."; 
+
+		EATEST_VERIFY(buffer1 == MemmoveC(buffer1, buffer2, Strlen(buffer2)));
+		EATEST_VERIFY(memcmp(buffer1, buffer2, Strlen(buffer2)) ==  0);
+		EATEST_VERIFY(memset(buffer1, (char8_t )0, Strlen(buffer1)) != NULL);
+
+		EATEST_VERIFY(buffer1 == MemmoveC(buffer1, buffer2+7, Strlen(buffer2) - 7));
+		EATEST_VERIFY(memcmp(buffer1, buffer2+7, Strlen(buffer2) - 7) ==  0);
+
+		EATEST_VERIFY(buffer2+5 == MemmoveC(buffer2+5, buffer2+7, Strlen(buffer2) - 7));
+		EATEST_VERIFY(memcmp(buffer2+5, buffer1, Strlen(buffer2) - 7) ==  0);
+
+		EATEST_VERIFY(buffer1 == MemmoveC(buffer1, buffer3, Strlen(buffer3)));
+		EATEST_VERIFY(buffer3+2 == MemmoveC(buffer3+2, buffer3, Strlen(buffer3) - 2));
+		EATEST_VERIFY(memcmp(buffer3+2, buffer1, Strlen(buffer3) - 2) ==  0);
+
+		// To do: We need a much better test than this.
+	}
+
+	#if EASTDC_MEMCPY16_ENABLED
+		{   // Memmove16
+			char16_t buffer1[] = EA_CHAR16("..........."); 
+			char16_t buffer2[] = EA_CHAR16(".......0123"); 
+			char16_t buffer3[] = EA_CHAR16("0123......."); 
+
+			EATEST_VERIFY(buffer1 == (char16_t*)Memmove((void*)buffer1, (void*)buffer2, Strlen(buffer2) * sizeof(char16_t)));
+			EATEST_VERIFY(memcmp(buffer1, buffer2, Strlen(buffer2) * sizeof(char16_t)) ==  0);
+			EATEST_VERIFY(memset(buffer1, (char16_t )0, Strlen(buffer1) * sizeof(char16_t)) != NULL);
+
+			EATEST_VERIFY(buffer1 == (char16_t*)Memmove((void*)buffer1, (void*)(buffer2+7), (Strlen(buffer2) - 7) * sizeof(char16_t)));
+			EATEST_VERIFY(memcmp(buffer1, buffer2+7, (Strlen(buffer2) - 7) * sizeof(char16_t)) ==  0);
+
+			EATEST_VERIFY(buffer2+5 == (char16_t*)Memmove((void*)(buffer2+5), (void*)(buffer2+7), (Strlen(buffer2) - 7) * sizeof(char16_t)));
+			EATEST_VERIFY(memcmp(buffer2+5, buffer1, (Strlen(buffer2) - 7) * sizeof(char16_t)) ==  0);
+
+			EATEST_VERIFY(buffer1   == (char16_t*)Memmove((void*)buffer1,     (void*)buffer3, Strlen(buffer3) * sizeof(char16_t)));
+			EATEST_VERIFY(buffer3+2 == (char16_t*)Memmove((void*)(buffer3+2), (void*)buffer3, (Strlen(buffer3) - 2) * sizeof(char16_t)));
+			EATEST_VERIFY(memcmp(buffer3+2, buffer1, (Strlen(buffer3) - 2) * sizeof(char16_t)) ==  0);
+		}
+	#endif
+
+	return nErrorCount;
+}
+
+
+static int TestTimingSafe()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		// bool TimingSafeMemEqual(const void* p1, const void* p2, size_t n);
+		// int  TimingSafeMemcmp(const void* p1, const void* p2, size_t n);
+		// bool TimingSafeMemIsClear(const void* p, size_t n);
+
+		{   // Basic accuracy tests.
+			char8_t buffer1[] = "01234567a";
+			char8_t buffer2[] = "01234567b";
+			char8_t buffer3[] = "01234567c";
+			char8_t buffer4[] = "\0\0\0\0\0\0\0\0\0";
+
+			EATEST_VERIFY(TimingSafeMemcmp(buffer1, buffer1, 0) == Memcmp(buffer1, buffer1, 0));
+			EATEST_VERIFY(TimingSafeMemcmp(buffer2, buffer1, 9) == Memcmp(buffer2, buffer1, 9));
+			EATEST_VERIFY(TimingSafeMemcmp(buffer3, buffer2, 9) == Memcmp(buffer3, buffer2, 9));
+			EATEST_VERIFY(TimingSafeMemcmp(buffer2, buffer3, 9) == Memcmp(buffer2, buffer3, 9));
+			EATEST_VERIFY(TimingSafeMemcmp(buffer1, buffer2, 9) == Memcmp(buffer1, buffer2, 9));
+
+			EATEST_VERIFY(TimingSafeMemEqual(buffer1, buffer1, 0) == (Memcmp(buffer1, buffer1, 0) == 0));
+			EATEST_VERIFY(TimingSafeMemEqual(buffer1, buffer1, 9) == (Memcmp(buffer1, buffer1, 9) == 0));
+			EATEST_VERIFY(TimingSafeMemEqual(buffer2, buffer1, 9) == (Memcmp(buffer2, buffer1, 9) == 0));
+			EATEST_VERIFY(TimingSafeMemEqual(buffer3, buffer2, 9) == (Memcmp(buffer3, buffer2, 9) == 0));
+			EATEST_VERIFY(TimingSafeMemEqual(buffer2, buffer3, 9) == (Memcmp(buffer2, buffer3, 9) == 0));
+			EATEST_VERIFY(TimingSafeMemEqual(buffer1, buffer2, 9) == (Memcmp(buffer1, buffer2, 9) == 0));
+
+			EATEST_VERIFY(TimingSafeMemIsClear(buffer1, 0) == true);
+			EATEST_VERIFY(TimingSafeMemIsClear(buffer1, 1) == false);
+			EATEST_VERIFY(TimingSafeMemIsClear(buffer1, 9) == false);
+			EATEST_VERIFY(TimingSafeMemIsClear(buffer4, 1) == true);
+			EATEST_VERIFY(TimingSafeMemIsClear(buffer4, 9) == true);
+		}
+
+
+		{   // Timing tests.
+			// It's not easy to fully validate the constant timing of these functions, due to the 
+			// tiny cycle count differences potentially involved. However, we can pretty easily
+			// test extreme cases and verify that at least the basic logic of the functions are 
+			// timing constant and not optimized away by the compiler.
+
+			Stopwatch stopwatch1(Stopwatch::kUnitsCPUCycles, false);
+			Stopwatch stopwatch2(Stopwatch::kUnitsCPUCycles, false);
+			bool      success = false;
+
+			eastl::vector<uint8_t> vLarge1((eastl_size_t)100000, (uint8_t)0); // Some large sized memory.
+			eastl::vector<uint8_t> vLarge2((eastl_size_t)100000, (uint8_t)0);
+
+			// We run this test multiple times because it may fail due to some execution hiccup and we want to give it 
+			// another chance. In a sense this is a bad idea because it seems to be going against what the function is 
+			// intended to be tested for, but it's impossible to truly know why something didn't execute in constant
+			// time without looking at the executed machine code by hand.
+
+			// TimingSafeMemEqual
+			for(int i = 0; (i < 3) && !success; i++)
+			{
+				stopwatch1.Restart();
+				bool bResult = TimingSafeMemEqual(vLarge1.data(), vLarge2.data(), vLarge1.size());
+				stopwatch1.Stop();
+				EATEST_VERIFY(bResult == true);
+
+				vLarge1[0] = 1;     // Change the first and last bytes. With regular memcmp this changed byte would result in memcmp returning very quickly, but we don't want that.
+				vLarge1[vLarge1.size()-1] = 1;
+				stopwatch2.Restart();
+				bResult = TimingSafeMemEqual(vLarge1.data(), vLarge2.data(), vLarge1.size());
+				stopwatch2.Stop();
+				EATEST_VERIFY(bResult == false);
+				success = (((stopwatch1.GetElapsedTimeFloat() - stopwatch2.GetElapsedTimeFloat()) / stopwatch1.GetElapsedTimeFloat()) < 0.25); // We give it a lot of leeway so our unit tests don't frequently fail.
+				vLarge1[0] = 0;     // Restore the changed bytes.
+				vLarge1[vLarge1.size()-1] = 0;
+			}
+
+			EATEST_VERIFY_MSG(success, "TimingSafeMemEqual didn't seem to be able to execute in constant time.");
+
+
+			// TimingSafeMemcmp
+			for(int i = 0; (i < 3) && !success; i++)
+			{
+				stopwatch1.Restart();
+				int iResult = TimingSafeMemcmp(vLarge1.data(), vLarge2.data(), vLarge1.size());
+				stopwatch1.Stop();
+				EATEST_VERIFY(iResult == 0);
+
+				vLarge1[0] = 1; // Change the first and last bytes. With regular memcmp this changed byte would result in memcmp returning very quickly, but we don't want that.
+				vLarge1[vLarge1.size()-1] = 1;
+				stopwatch2.Restart();
+				iResult = TimingSafeMemcmp(vLarge1.data(), vLarge2.data(), vLarge1.size());
+				stopwatch2.Stop();
+				EATEST_VERIFY(iResult == 1);
+				success = ((fabsf(stopwatch1.GetElapsedTimeFloat() - stopwatch2.GetElapsedTimeFloat()) / stopwatch1.GetElapsedTimeFloat()) < 0.25); // We give it a lot of leeway so our unit tests don't frequently fail.
+				vLarge1[0] = 0;
+				vLarge1[vLarge1.size()-1] = 0;
+			}
+
+			EATEST_VERIFY_MSG(success, "TimingSafeMemcmp didn't seem to be able to execute in constant time.");
+
+
+			// TimingSafeMemIsClear
+			for(int i = 0; (i < 3) && !success; i++)
+			{
+				stopwatch1.Restart();
+				bool bResult = TimingSafeMemIsClear(vLarge1.data(), vLarge1.size());
+				stopwatch1.Stop();
+				EATEST_VERIFY(bResult == true);
+
+				vLarge1[0] = 1;
+				vLarge1[vLarge1.size()-1] = 1;
+				stopwatch2.Restart();
+				bResult = TimingSafeMemIsClear(vLarge1.data(), vLarge1.size());
+				stopwatch2.Stop();
+				EATEST_VERIFY(bResult == false);
+				success = ((fabsf(stopwatch1.GetElapsedTimeFloat() - stopwatch2.GetElapsedTimeFloat()) / stopwatch1.GetElapsedTimeFloat()) < 0.25); // We give it a lot of leeway so our unit tests don't frequently fail.
+				vLarge1[0] = 0;
+				vLarge1[vLarge1.size()-1] = 0;
+			}
+
+			EATEST_VERIFY_MSG(success, "TimingSafeMemIsClear didn't seem to be able to execute in constant time.");
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static void TestMemcpySpeed()
+{
+	using namespace EA::StdC;
+
+	struct SizeOffset
+	{
+		size_t mSize;
+		size_t mOffset1;
+		size_t mOffset2;
+	};
+
+	Stopwatch   s(Stopwatch::kUnitsCPUCycles);
+	uint32_t    kSeed = 0x12345678;
+	Random      r(kSeed);
+	size_t      kSizeArraySize = 512;     // We don't want this too large, else we start getting a lot of cache effects.
+	SizeOffset* sizeArray = new SizeOffset[kSizeArraySize];
+
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+	{
+		sizeArray[i].mOffset1 = (size_t)(uint32_t)RandomInt32UniformRange(r, 0, 32);
+		sizeArray[i].mOffset2 = (size_t)(uint32_t)RandomInt32UniformRange(r, 0, 32);
+	}
+
+
+	////////////////////////
+	// Small copies
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 0, 256);
+
+	s.Restart();
+	for(size_t j = 0; j < 128; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemcpyC(gMem1 + so.mOffset1, gMem2 + so.mOffset2, so.mSize);
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Medium copies
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 256, 4096);
+
+	s.Restart();
+	for(size_t j = 0; j < 64; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemcpyC(gMem1 + so.mOffset1, gMem2 + so.mOffset2, so.mSize);
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Large copies
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 4096, 262144);
+
+	s.Restart();
+	for(size_t j = 0; j < 32; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemcpyC(gMem1 + (so.mOffset1 * 8), gMem2 + (so.mOffset2 * 8), so.mSize); // Test with 8 byte alignments.
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Giant copies
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 262144, 4194304);
+
+	s.Restart();
+	for(size_t j = 0; j < 16; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemcpyC(gMem1 + (so.mOffset1 * 128), gMem2 + (so.mOffset2 * 128), so.mSize); // Test with 128 byte alignments.
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+	delete[] sizeArray;
+}
+
+
+static void TestMemmoveSpeed()
+{
+	using namespace EA::StdC;
+	// To do.
+}
+
+
+static void TestMemsetSpeed()
+{
+	using namespace EA::StdC;
+	// To do.
+}
+
+
+static void TestMemclearSpeed()
+{
+	using namespace EA::StdC;
+
+	struct SizeOffset
+	{
+		size_t mSize;
+		size_t mOffset1;
+	};
+
+	Stopwatch   s(Stopwatch::kUnitsCPUCycles);
+	uint32_t    kSeed = 0x12345678;
+	Random      r(kSeed);
+	size_t      kSizeArraySize = 512;     // We don't want this too large, else we start getting a lot of cache effects.
+	SizeOffset* sizeArray = new SizeOffset[kSizeArraySize];
+
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mOffset1 = (size_t)(uint32_t)RandomInt32UniformRange(r, 0, 32);
+
+
+	////////////////////////
+	// Small clears
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 0, 256);
+
+	s.Restart();
+	for(size_t j = 0; j < 128; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemclearC(gMem1 + so.mOffset1, so.mSize);
+			// memset(gMem1 + so.mOffset1, 0, so.mSize);
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Medium clears
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 256, 4096);
+
+	s.Restart();
+	for(size_t j = 0; j < 64; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemclearC(gMem1 + so.mOffset1, so.mSize);
+			// memset(gMem1 + so.mOffset1, 0, so.mSize);
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Large clears
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 4096, 262144);
+
+	s.Restart();
+	for(size_t j = 0; j < 32; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemclearC(gMem1 + (so.mOffset1 * 8), so.mSize); // Test with 8 byte alignments.
+			// memset(gMem1 + (so.mOffset1 * 8), 0, so.mSize); // Test with 8 byte alignments.
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+
+	////////////////////////
+	// Giant clears
+	for(size_t i = 0; i < kSizeArraySize; ++i)
+		sizeArray[i].mSize = (size_t)(uint32_t)RandomInt32UniformRange(r, 262144, 4194304);
+
+	s.Restart();
+	for(size_t j = 0; j < 16; ++j) // Do a double loop so that we can get a lot of copies done without sizeArray being so large that it starts having cache effects.
+	{
+		for(size_t i = 0; i < kSizeArraySize; ++i)
+		{
+			const SizeOffset& so = sizeArray[i];
+			MemclearC(gMem1 + (so.mOffset1 * 128), so.mSize); // Test with 128 byte alignments.
+			// memset(gMem1 + (so.mOffset1 * 128), 0, so.mSize); // Test with 128 byte alignments.
+		}
+	}
+	s.Stop();
+	//Printf("%I64u\n", s.GetElapsedTime());
+
+	delete[] sizeArray;
+}
+
+
+
+int TestMemory()
+{
+	EA::UnitTest::Report("TestMemory\n");
+
+	int nErrorCount = 0;
+
+	// Set up large aligned memory blocks for memory tests.
+	// kBaseMemSize * 2 because we will set gMem1 to be kBaseMemSize bytes into pMem1Aligned so we can read bytes prior to the tested space.
+	const size_t size           = (kBaseMemSize * 2) + kBaseMemAlignment;
+	uint8_t*     pMem1Unaligned = new uint8_t[size];
+	uint8_t*     pMem2Unaligned = new uint8_t[size];
+
+	if(pMem1Unaligned && pMem2Unaligned)
+	{
+		memset(pMem1Unaligned, kByte1, size);
+		memset(pMem2Unaligned, kByte2, size);
+
+		// Set gMem1/gMem2 to be kBaseMemSize bytes into pMem1Unaligned and be of alignment = kBaseMemAlignment.
+		gMem1 = (uint8_t*)(((uintptr_t)pMem1Unaligned + kBaseMemSize + (kBaseMemAlignment - 1)) & ~(kBaseMemAlignment - 1));
+		gMem2 = (uint8_t*)(((uintptr_t)pMem2Unaligned + kBaseMemSize + (kBaseMemAlignment - 1)) & ~(kBaseMemAlignment - 1));
+
+		nErrorCount += TestEAAlloca();
+		nErrorCount += TestEAMalloca();
+		nErrorCount += TestMemset();
+		nErrorCount += TestMemfill();
+		nErrorCount += TestMemclear();
+		nErrorCount += TestMemcheck();
+		nErrorCount += TestMemchr();
+		nErrorCount += TestMemcmp();
+		nErrorCount += TestMemmem();
+		nErrorCount += TestMemcpy();
+		nErrorCount += TestMemmove();
+		nErrorCount += TestTimingSafe();
+
+		TestMemcpySpeed();
+		TestMemmoveSpeed();
+		TestMemsetSpeed();
+		TestMemclearSpeed();
+
+		EA_CACHE_PREFETCH_128(gMem1);
+		EA_CACHE_ZERO_128(gMem1);
+
+		delete[] pMem1Unaligned;
+		delete[] pMem2Unaligned;
+	}
+
+	// template<size_t> StaticMemory
+	struct MyClass{ char buffer[37]; };
+	EA::StdC::StaticMemory<sizeof(MyClass)> mStaticMemory;
+	MyClass* pClass = new(mStaticMemory.Memory()) MyClass;
+	memset(pClass->buffer, 0, sizeof(pClass->buffer));
+	EATEST_VERIFY(EA::StdC::Memcheck8(pClass->buffer, 0, sizeof(pClass->buffer)) == NULL);
+
+	return nErrorCount;
+}
+
+#if defined(_MSC_VER)
+	#pragma warning(pop)
+#endif
+
+
+
+
+
+

+ 213 - 0
test/source/TestProcess.cpp

@@ -0,0 +1,213 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAProcess.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+	template <typename T> // T is one of char8_t, char16_t, char32_t.
+	static void TestEnvironmentVar(int& nErrorCount)
+	{
+		using namespace EA::StdC;
+
+		T      name[20];
+		T      valueIn[32];
+		T      valueOut[32];
+		bool   bResult;
+		size_t nRequiredStrlen;
+		size_t sizeError = (size_t)-1;
+
+		// Verify that nothing is found:
+		Strlcpy(name, "NameNonExisting", EAArrayCount(name));
+		valueOut[0] = 0;
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, EAArrayCount(valueOut));
+		EATEST_VERIFY((nRequiredStrlen == sizeError) && (valueOut[0] == 0));
+
+		// Verify that something can be set, then found:
+		Strlcpy(name,  "NameExisting", EAArrayCount(name));
+		Strlcpy(valueIn, "ValueExisting", EAArrayCount(valueIn));
+		bResult = SetEnvironmentVar(name, valueIn);
+		EATEST_VERIFY(bResult);
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, EAArrayCount(valueOut));
+		EATEST_VERIFY((nRequiredStrlen == 13) && (Strcmp(valueIn, valueOut) == 0));
+
+		// Verify that a too-small output capacity is handled properly:
+		memset(valueOut, 0xff, sizeof(valueOut));
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, 3);
+		EATEST_VERIFY((nRequiredStrlen == 13) && (valueOut[3] == (T)-1));
+
+		// Verify that something can be reset, then found with the new value:
+		Strlcpy(valueIn, "ValueExistingNew", EAArrayCount(valueIn));
+		bResult = SetEnvironmentVar(name, valueIn);
+		EATEST_VERIFY(bResult);
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, EAArrayCount(valueOut));
+		EATEST_VERIFY((nRequiredStrlen == 16) && (Strcmp(valueIn, valueOut) == 0));
+
+		// Verify that something can be cleared, then not found:
+		bResult = SetEnvironmentVar(name, NULL);
+		EATEST_VERIFY(bResult);
+		valueOut[0] = 0;
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, EAArrayCount(valueOut));
+		EATEST_VERIFY((nRequiredStrlen == sizeError) && (valueOut[0] == 0));
+
+		// Verify that something can once again be set, then found:
+		Strlcpy(name, "NameExisting", EAArrayCount(name));
+		Strlcpy(valueIn, "ValueExistingNew2", EAArrayCount(valueIn));
+		bResult = SetEnvironmentVar(name, valueIn);
+		EATEST_VERIFY(bResult);
+		nRequiredStrlen = GetEnvironmentVar(name, valueOut, EAArrayCount(valueOut));
+		EATEST_VERIFY((nRequiredStrlen == 17) && (Strcmp(valueIn, valueOut) == 0));
+
+		// Verify that GetEnvironmentVar(T) yields the same result as GetEnvironmentVar(char8_t) (T may be char16_t).
+		char8_t valueOut8[32];
+		T       valueOutT[32];
+		nRequiredStrlen = GetEnvironmentVar("NameExisting", valueOut8, EAArrayCount(valueOut8));
+		Strlcpy(valueOutT, valueOut8, EAArrayCount(valueOutT)); // Need to convert char8_t to T, as Strcmp only exists for like types.
+		EATEST_VERIFY((nRequiredStrlen == 17) && (Strcmp(valueOut, valueOutT) == 0));
+
+		// Verify that GetEnvironmentVar(T) yields the same result as GetEnvironmentVar(char16_t) (T may be char8_t).
+		char16_t name16[32];
+		char16_t valueOut16[32];
+		Strlcpy(name16, name, EAArrayCount(name16));
+		nRequiredStrlen = GetEnvironmentVar(name16, valueOut16, EAArrayCount(valueOut16));
+		Strlcpy(valueOutT, valueOut16, EAArrayCount(valueOutT)); // Need to convert char16_t to T, as Strcmp only exists for like types.
+		EATEST_VERIFY((nRequiredStrlen == 17) && (Strcmp(valueOut, valueOutT) == 0));
+	}
+#endif
+
+
+
+int TestProcess()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestProcess\n");
+
+	{
+		// size_t GetCurrentProcessPath(char8_t*  pPath);
+		// size_t GetCurrentProcessPath(char16_t* pPath);
+		// size_t GetCurrentProcessPath(char32_t* pPath);
+		// size_t GetCurrentProcessDirectory(char8_t*  pDirectory);
+		// size_t GetCurrentProcessDirectory(char16_t* pDirectory);
+		// size_t GetCurrentProcessDirectory(char32_t* pDirectory);
+	
+		// Currently we have known support for Windows. With other platforms support is inconsistent.
+		#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) || defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_LINUX) || (defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED)
+			char16_t path16[EA::StdC::kMaxPathLength];
+			char8_t  path8[EA::StdC::kMaxPathLength];
+			size_t   n;
+			size_t   processPathLen;
+
+			n = EA::StdC::GetCurrentProcessPath(path16);
+			EATEST_VERIFY(n == EA::StdC::Strlen(path16));
+			EATEST_VERIFY(n > 0);
+
+			processPathLen = EA::StdC::GetCurrentProcessPath(path8);
+			EATEST_VERIFY(processPathLen == EA::StdC::Strlen(path8));
+			EATEST_VERIFY(processPathLen > 0);
+
+			EATEST_VERIFY(EA::StdC::Strlen(path16) == EA::StdC::Strlen(path8));
+
+			#if defined(EA_PLATFORM_APPLE) // Test kPathFlagBundlePath
+				if(n && !EA::StdC::Strend(path8, ".app")) // If we have a path to a file within a bundle (e.g. "/Dir/MyApp.app/Contents/MacOS/MyApp")
+				{
+					n = EA::StdC::GetCurrentProcessPath(path8, EAArrayCount(path8), EA::StdC::kPathFlagBundlePath);
+					EATEST_VERIFY(n > 0); // Should be like "/Dir/MyApp" or "/Dir/MyApp.app"
+				}
+			#endif
+			
+			n = EA::StdC::GetCurrentProcessDirectory(path16);
+			EATEST_VERIFY(n == EA::StdC::Strlen(path16));
+			EATEST_VERIFY(n > 0);
+
+			n = EA::StdC::GetCurrentProcessDirectory(path8);
+			EATEST_VERIFY(n == EA::StdC::Strlen(path8));
+			EATEST_VERIFY(n > 0);
+
+			EATEST_VERIFY(EA::StdC::Strlen(path16) == EA::StdC::Strlen(path8));
+
+			EATEST_VERIFY(processPathLen > n);
+
+			#if defined(EA_PLATFORM_APPLE) // Test kPathFlagBundlePath
+				// We could do a similar test for iOS.
+				if(n && EA::StdC::Strend(path8, "MacOS/")) // If we have a path to a file within a MacOS bundle (e.g. "/Dir/MyApp.app/Contents/MacOS/")
+				{
+					n = EA::StdC::GetCurrentProcessDirectory(path8, EAArrayCount(path8), EA::StdC::kPathFlagBundlePath);
+					EATEST_VERIFY((n > 0) && !EA::StdC::Strend(path8, "MacOS/")); // Should be like "/Dir/"
+				}
+			#elif defined(EA_PLATFORM_SONY)
+				// Do some additional testing of SetCurrentProcessPath with GetCurrentProcessPath / GetCurrentProcessDirectory on Kettle
+				#define KETTLE_PROCESS_FOLDER "/host/folder/subfolder/"
+				#define KETTLE_PROCESS_ELF "test.elf"
+				EA::StdC::SetCurrentProcessPath(KETTLE_PROCESS_FOLDER KETTLE_PROCESS_ELF);
+
+				n = EA::StdC::GetCurrentProcessPath(path16);
+				EATEST_VERIFY(EA::StdC::Strcmp(path16, EA_CHAR16(KETTLE_PROCESS_FOLDER KETTLE_PROCESS_ELF)) == 0);
+				EATEST_VERIFY(n == EA::StdC::Strlen(KETTLE_PROCESS_FOLDER KETTLE_PROCESS_ELF));
+
+				n = EA::StdC::GetCurrentProcessPath(path8);
+				EATEST_VERIFY(EA::StdC::Strcmp(path8, KETTLE_PROCESS_FOLDER KETTLE_PROCESS_ELF) == 0);
+				EATEST_VERIFY(n == EA::StdC::Strlen(KETTLE_PROCESS_FOLDER KETTLE_PROCESS_ELF));
+
+				n = EA::StdC::GetCurrentProcessDirectory(path16);
+				EATEST_VERIFY(EA::StdC::Strcmp(path16, EA_CHAR16(KETTLE_PROCESS_FOLDER)) == 0);
+				EATEST_VERIFY(n == EA::StdC::Strlen(KETTLE_PROCESS_FOLDER));
+				EATEST_VERIFY(n > 0);
+
+				n = EA::StdC::GetCurrentProcessDirectory(path8);
+				EATEST_VERIFY(EA::StdC::Strcmp(path8, KETTLE_PROCESS_FOLDER) == 0);
+				EATEST_VERIFY(n == EA::StdC::Strlen(KETTLE_PROCESS_FOLDER));
+			#endif
+			
+		#endif
+	}
+
+	{
+		// size_t GetEnvironmentVar(const char8_t*  pName, char8_t*  pValue, size_t valueCapacity);
+		// size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity);
+		// size_t GetEnvironmentVar(const char32_t* pName, char32_t* pValue, size_t valueCapacity);
+		// bool   SetEnvironmentVar(const char8_t*  pName, const char8_t* pValue);
+		// bool   SetEnvironmentVar(const char16_t* pName, const char16_t* pValue);
+		// bool   SetEnvironmentVar(const char32_t* pName, const char32_t* pValue);
+
+		#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) // To do: Enable this for other platforms when possible.
+			TestEnvironmentVar<char8_t>(nErrorCount);
+			TestEnvironmentVar<char16_t>(nErrorCount);
+		  //TestEnvironmentVar<char32_t>(nErrorCount); // Doesn't yet exist.
+		#endif
+	}
+
+	/*
+	{
+		// int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait = false);
+		// int Spawn(const char8_t*  pPath, const char8_t*  const* pArgumentArray, bool wait = false);
+
+		// int ExecuteShellCommand(const char16_t* pCommand);
+		// int ExecuteShellCommand(const char8_t*  pCommand);
+
+		// bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar = NULL);
+		// bool SearchEnvironmentPath(const char8_t*  pFileName, char8_t*  pPath, const char8_t*  pEnvironmentVar = NULL);
+
+		// bool OpenFile(const char16_t* pPath); // e.g. http://www.bozo.com/somefile.html
+		// bool OpenFile(const char8_t*  pPath); // e.g. /system/settings/somefile.txt
+	}
+	*/
+
+	return nErrorCount;
+}
+

+ 1166 - 0
test/source/TestRandom.cpp

@@ -0,0 +1,1166 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifdef _MSC_VER
+	#pragma warning(disable: 4244) // This warning is being generated due to a bug in VC++.
+#endif
+
+#include <EABase/eabase.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EARandomDistribution.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EASTL/bitset.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+	#pragma warning(disable: 4275) // non dll-interface class 'stdext::exception' used as base for dll-interface class 'std::bad_cast'
+#endif
+
+#ifndef EA_PLATFORM_ANDROID
+	#include <algorithm>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#if defined(_MSC_VER) && defined(EA_PLATFORM_MICROSOFT)
+	#include <crtdbg.h>
+#endif
+#if defined(EA_PLATFORM_WINDOWS)
+	#include <Windows.h>
+#endif
+#if EASTDC_TIME_H_AVAILABLE
+	#include <time.h>
+#endif
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+	#pragma warning(push)
+	#pragma warning(disable: 4365) // 'argument' : conversion from 'int' to 'uint32_t', signed/unsigned mismatch
+#endif
+
+
+using namespace EA::StdC;
+
+
+// Forward declarations
+void rt_init(int binmode);
+void rt_add(void* buf, int bufl);
+void rt_end(double* r_ent, double* r_chisq, double* r_mean, double* r_montepicalc, double* r_scc);
+
+static int TestDieHard()
+{
+	int nErrorCount(0);
+
+	// Write out 9MB file for DieHard tests.
+	#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
+		if(GetAsyncKeyState(VK_SCROLL)) // If the Scroll Lock key is alive.
+		{
+			// Ideally we would port the DieHard code to here, but it is not well 
+			// written for modularity. For the time being, we write out the 9MB
+			// data file that DieHard.exe can analyze. As of this writing, DieHard.exe
+			// is part of the EAOS UTF Research repository.
+
+			{
+				RandomLinearCongruential randomLC;
+
+				FILE* pFile = fopen("RandomLinearCongruentialData.txt", "w");
+
+				if(pFile)
+				{
+					for(uint32_t i = 0; i < 12000000; i += 4)
+					{
+						const uint32_t value = randomLC.RandomUint32Uniform();
+						fwrite(&value, 1, 4, pFile);
+					}
+
+					fclose(pFile);
+				}
+			}
+
+			{
+				RandomMersenneTwister randomMT;
+
+				FILE* pFile = fopen("RandomMersenneTwisterData.txt", "w");
+
+				if(pFile)
+				{
+					for(uint32_t i = 0; i < 12000000; i += 4)
+					{
+						const uint32_t value = randomMT.RandomUint32Uniform();
+						fwrite(&value, 1, 4, pFile);
+					}
+
+					fclose(pFile);
+				}
+			}
+		}
+	#endif
+
+	return nErrorCount;
+}
+
+
+namespace 
+{
+	#if defined(EA_PLATFORM_DESKTOP) && !defined(EA_DEBUG)
+		// This exists for the purpose of testing distributions. It implements a seed that is continuously 
+		// increasting and thus over the course of 0x100000000 (2^32) calls to RandomUint32Uniform returns a statistically
+		// even distribution of bits. Note that truly random data won't behave this way and formal tests for
+		// randomness would identify this as being not random. But that's not the purpose of this class; 
+		// the purpose is to help test if there are distribution problems in the range and distribution adapters.
+		// Note that it's important that you do 0x100000000 calls with this or else the results of it won't 
+		// be evenly distributed as designed.
+		class FakeIncrementingRandom
+		{
+		public:
+			FakeIncrementingRandom() 
+				: mnSeed(0) {}
+			
+			//FakeIncrementingRandom(const FakeIncrementingRandom& x) 
+			//   : mnSeed(x.mnSeed) {}
+
+			//FakeIncrementingRandom& operator=(const FakeIncrementingRandom& x)
+			//    { mnSeed = x.mnSeed; return *this; }
+
+			//uint32_t GetSeed() const
+			//    { return mnSeed; }
+
+			//void SetSeed(uint32_t nSeed)
+			//    { mnSeed = nSeed; }
+
+			//uint32_t operator()(uint32_t nLimit)
+			//    { return EA::StdC::RandomLimit(*this, nLimit); }
+
+			uint32_t RandomUint32Uniform()
+				{ return mnSeed++; }
+
+		protected:
+			uint32_t mnSeed;
+		};
+	#endif
+}
+
+
+// TestRandom
+// Note that thus function itself is not meant as a comprehensive 
+// test for randomness. Instead this function does a basic test 
+// for randomness and then optionally writes out files to disk
+// for analysis by a comprehensive tool like DieHard. 
+//
+int TestRandom()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestRandom\n");
+
+	{   // Bug report regression.
+		// User Fei Jiang reports that RandomLinearCongruential::RandomUnit32Uniform(uint32_t nLimit) returns 
+		// different values on PS3 in debug vs. debug-opt builds with SN compiler.
+
+		RandomLinearCongruential rlc(UINT32_C(2474210934));
+		uint32_t seed = rlc.GetSeed();
+		//EA::UnitTest::Report("seed: %u\n", (unsigned)seed);
+		EATEST_VERIFY(seed == UINT32_C(2474210934));
+
+		uint32_t result = rlc.RandomUint32Uniform(57);
+		//EA::UnitTest::Report("result: %u\n", (unsigned)result);
+		EATEST_VERIFY(result == 23);  // 743483
+	}
+
+	// Load priming
+	// We call a function from each generator used below to minimize 
+	// an loading effects on benchmarking.
+	int rTemp = rand();
+	EATEST_VERIFY(rTemp >= 0); // "Returns a pseudo-random integral number in the range 0 to RAND_MAX."
+
+	RandomLinearCongruential randomLCPrimer;
+	randomLCPrimer.RandomUint32Uniform();
+
+	RandomMersenneTwister randomMTPrimer;
+	randomMTPrimer.RandomUint32Uniform();
+
+	TestDieHard();
+
+	//#define SPEED_TESTS_ENABLED
+	#ifdef SPEED_TESTS_ENABLED
+		// Speed tests.
+		// Results on a Pentium 4 PC were:
+		//    rand(): 8172 clocks.
+		//    RandomLinearCongruential: 4687 clocks.
+		//    RandomMersenneTwister: 6157 clocks.
+		{
+			clock_t timeStart;
+			clock_t timeTotal;
+			const int kIterationCount(5000000);
+
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)rand();
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("rand(): %d clocks.\n", (int)timeTotal);
+
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)(rand() % 37997);
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("rand() w/limit: %d clocks.\n", (int)timeTotal);
+
+
+			RandomLinearCongruential randomLC;
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)randomLC.RandomUint32Uniform();
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("RandomLinearCongruential: %d clocks.\n", (int)timeTotal);
+
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)randomLC.RandomUint32Uniform(37997);
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("RandomLinearCongruential w/limit: %d clocks.\n", (int)timeTotal);
+
+
+			RandomMersenneTwister randomMT;
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)randomMT.RandomUint32Uniform();
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("RandomMersenneTwister: %d clocks.\n", (int)timeTotal);
+
+			timeStart = clock();
+			for(int i(0); i < kIterationCount; i++)
+				EA::UnitTest::WriteToEnsureFunctionCalled() = (int)randomMT.RandomUint32Uniform(32997);
+			timeTotal = clock() - timeStart;
+			EA::UnitTest::Report("RandomMersenneTwister w/limit: %d clocks.\n", (int)timeTotal);
+		}
+	#endif
+
+
+	// Test output ranges
+	{  // RandomLinearCongruential test
+		RandomLinearCongruential random;
+		int32_t  nRandom;
+		double   dRandom;
+
+		for(unsigned i(0); i < 100; i++)
+		{
+			for(uint32_t j(5); j < (UINT32_MAX / 2); j *= 5)
+			{
+				uint32_t nU32 = random.RandomUint32Uniform(j);
+				EATEST_VERIFY(nU32 < j);
+
+				dRandom = random.RandomDoubleUniform((double)j);
+				EATEST_VERIFY(0.0 <= dRandom && dRandom < j);
+
+				dRandom = random.RandomDoubleUniform();
+				EATEST_VERIFY(0.0 <= dRandom && dRandom < 1.0);
+			}
+
+			nRandom = Random2(random);
+			EATEST_VERIFY(nRandom < 2);
+
+			nRandom = Random4(random);
+			EATEST_VERIFY(nRandom < 4);
+
+			nRandom = Random8(random);
+			EATEST_VERIFY(nRandom < 8);
+
+			nRandom = Random16(random);
+			EATEST_VERIFY(nRandom < 16);
+
+			nRandom = Random32(random);
+			EATEST_VERIFY(nRandom < 32);
+
+			nRandom = Random64(random);
+			EATEST_VERIFY(nRandom < 642);
+
+			nRandom = Random128(random);
+			EATEST_VERIFY(nRandom < 128);
+
+			nRandom = Random256(random);
+			EATEST_VERIFY(nRandom < 256);
+
+
+			// RandomPowerOfTwo
+			for(uint32_t k(1); k < 31; k++)
+			{
+				nRandom = RandomPowerOfTwo(random, k);
+				EATEST_VERIFY((uint32_t)nRandom < (uint32_t)(2 << k));
+			}
+
+
+			// RandomInt32UniformRange
+			for(int32_t nBegin(-10000); nBegin < 10000; nBegin += Random256(random))
+			{
+				int32_t nEnd    = nBegin + 1 + Random256(random);
+				int32_t iRandom = RandomInt32UniformRange(random, nBegin, nEnd);
+
+				EATEST_VERIFY_F((iRandom >= nBegin) && (iRandom < nEnd), "RandomInt32UniformRange failure: iRandom: %I32d, nBegin: %I32d, nEnd: %I32d", iRandom, nBegin, nEnd);
+			}
+
+
+			// RandomDoubleUniformRange
+			for(int32_t dBegin(-10000); dBegin < 10000; dBegin += Random256(random))
+			{
+				int32_t dEnd = dBegin + 1 + Random256(random);
+				dRandom = RandomDoubleUniformRange(random, (double)dBegin, (double)dEnd);
+
+				EATEST_VERIFY_F((dRandom >= dBegin) && (dRandom < dEnd), "RandomDoubleUniformRange failure: dRandom: %f, dBegin: %f, dEnd: %f", dRandom, (double)dBegin, (double)dEnd);
+			}
+
+
+			// RandomUint32WeightedChoice
+			const uint32_t kLimit = 37;
+			float weights[kLimit];
+			for(uint32_t q(0); q < kLimit; q++)
+				weights[q] = (float)RandomDoubleUniformRange(random, 0.5, 30.0);
+			for(uint32_t r(0); r < 1000; r++)
+			{
+				uint32_t nU32 = RandomUint32WeightedChoice(random, kLimit, weights);
+
+				EATEST_VERIFY_F(nU32 < kLimit, "RandomUint32WeightedChoice failure: nU32: %I32u, kLimit: %I32u", nU32, kLimit);
+			}
+
+
+			// RandomInt32GaussianRange
+			for(int r(0); r < 1000; r++)
+			{
+				const int32_t nBegin  = (int32_t)random.RandomUint32Uniform(1000);
+				const int32_t nEnd    = nBegin + (int32_t)random.RandomUint32Uniform(1000) + 1;
+				const int32_t iRandom = RandomInt32GaussianRange(random, nBegin, nEnd);
+
+				EATEST_VERIFY((iRandom >= nBegin) && (iRandom < nEnd));
+			}
+
+
+			// RandomFloatGaussianRange
+			for(int r(0); r < 1000; r++)
+			{
+				const float fBegin  = (float)random.RandomDoubleUniform(1000);
+				const float fEnd    = fBegin + (float)random.RandomDoubleUniform(1000) + 1.0f;
+				const float fRandom = RandomFloatGaussianRange(random, fBegin, fEnd);
+
+				EATEST_VERIFY((fRandom >= fBegin) && (fRandom < fEnd));
+			}
+
+
+			// RandomInt32TriangleRange
+			for(int r(0); r < 1000; r++)
+			{
+				const int32_t nBegin  = (int32_t)random.RandomUint32Uniform(1000);
+				const int32_t nEnd    = nBegin + (int32_t)random.RandomUint32Uniform(1000) + 1;
+				const int32_t iRandom = RandomInt32TriangleRange(random, nBegin, nEnd);
+
+				EATEST_VERIFY((iRandom >= nBegin) && (iRandom < nEnd));
+			}
+
+
+			// RandomFloatTriangleRange
+			for(int r(0); r < 1000; r++)
+			{
+				const float fBegin  = (float)random.RandomDoubleUniform(1000);
+				const float fEnd    = fBegin + (float)random.RandomDoubleUniform(1000) + 1.0f;
+				const float fRandom = RandomFloatTriangleRange(random, fBegin, fEnd);
+
+				EATEST_VERIFY((fRandom >= fBegin) && (fRandom < fEnd));
+			}
+
+
+		}
+	}
+
+
+	{ // RandomInt32Poisson
+		const float fMean = 5.f;
+		const size_t maxK = 30;
+
+		RandomMersenneTwister random;
+
+		for(int i = 0; i < 1000; i++)
+		{
+			int32_t rn = RandomInt32Poisson(random.RandomDoubleUniform(), fMean);
+			EATEST_VERIFY(rn < maxK);
+		}
+	}
+
+
+	{  // RandomLinearCongruential test
+		RandomMersenneTwister random;
+		int32_t nRandom;
+		double  dRandom;
+
+		for(unsigned i(0); i < 1000; i++)
+		{
+			for(uint32_t j(5); j < UINT32_MAX / 2; j *= 5)
+			{
+				uint32_t nU32 = random.RandomUint32Uniform(j);
+				EATEST_VERIFY(nU32 < j);
+
+				dRandom = random.RandomDoubleUniform((double)j);
+				EATEST_VERIFY(0.0 <= dRandom && dRandom < j);
+
+				dRandom = random.RandomDoubleUniform();
+				EATEST_VERIFY(0.0 <= dRandom && dRandom < 1.0);
+			}
+
+			nRandom = Random2(random);
+			EATEST_VERIFY(nRandom < 2);
+
+			nRandom = Random4(random);
+			EATEST_VERIFY(nRandom < 4);
+
+			nRandom = Random8(random);
+			EATEST_VERIFY(nRandom < 8);
+
+			nRandom = Random16(random);
+			EATEST_VERIFY(nRandom < 16);
+
+			nRandom = Random32(random);
+			EATEST_VERIFY(nRandom < 32);
+
+			nRandom = Random64(random);
+			EATEST_VERIFY(nRandom < 64);
+
+			nRandom = Random128(random);
+			EATEST_VERIFY(nRandom < 128);
+
+			nRandom = Random256(random);
+			EATEST_VERIFY(nRandom < 256);
+
+			for(uint32_t k(1); k < 31; k++)
+			{
+				nRandom = RandomPowerOfTwo(random, k);
+				EATEST_VERIFY((uint32_t)nRandom < (uint32_t)(2 << k));
+			}
+
+			for(int nBegin(-10000); nBegin < 10000; nBegin += Random256(random))
+			{
+				int32_t nEnd    = nBegin + 1 + Random256(random);
+				int32_t iRandom = RandomInt32UniformRange(random, nBegin, nEnd);
+
+				EATEST_VERIFY((iRandom >= nBegin) && (iRandom < nEnd));
+			}
+
+			for(int dBegin(-10000); dBegin < 10000; dBegin += Random256(random))
+			{
+				int32_t dEnd = dBegin + 1 + Random256(random);
+				dRandom = RandomDoubleUniformRange(random, (double)dBegin, (double)dEnd);
+
+				EATEST_VERIFY((dRandom >= dBegin) && (dRandom < dEnd));
+			}
+
+			const unsigned int kLimit = 37;
+			float weights[kLimit];
+			for(unsigned int q(0); q < kLimit; q++)
+				weights[q] = (float)RandomDoubleUniformRange(random, 0.5, 30.0);
+			for(unsigned int r(0); r < 100; r++)
+			{
+				uint32_t nU32 = RandomUint32WeightedChoice(random, kLimit, weights);
+
+				EATEST_VERIFY(nU32 < kLimit);
+			}
+		}
+	}
+
+
+	//NOTICE:
+	//Need Paul to look at this.
+	//At times, getting values outside of the assertion range.
+	#if !defined(EA_PLATFORM_IPHONE)
+
+		// Do basic randomness testing.
+		// Just because a random number generator passes known basic tests
+		// doesn't mean it doesn't have a major flaw.
+
+		{  // C runtime rand() test, provided for comparison.
+			int nErrorCountCRand(0); //We don't want to report these as part of our test.
+
+			rt_init(false);
+			for(int i(0); i < 100000; i++)
+			{
+				uint8_t nRandom = (uint8_t)(rand() & UINT8_MAX);
+				rt_add(&nRandom, sizeof(nRandom));
+			}
+
+			// See the rt_end documentation for detailed explanations
+			// of what each of these metrics mean.
+			double r_ent, r_chisq, r_mean, r_montepicalc, r_scc;
+			rt_end(&r_ent, &r_chisq, &r_mean, &r_montepicalc, &r_scc);
+
+			if(r_ent < 7.8)
+				nErrorCountCRand++;
+			else if(r_chisq < 200)
+				nErrorCountCRand++;
+			else if(r_mean < 127.2 || r_mean > 127.9)
+				nErrorCountCRand++;
+			else if(r_montepicalc < 3.11 || r_montepicalc > 3.17)
+				nErrorCountCRand++;
+			else if(r_scc > 0.01)
+				nErrorCountCRand++;
+		}
+
+
+		{  // RandomLinearCongruential test
+			RandomLinearCongruential random;
+
+			rt_init(false);
+			for(int i(0); i < 100000; i++)
+			{
+				uint32_t nRandom = random.RandomUint32Uniform();
+				rt_add(&nRandom, sizeof(nRandom));
+			}
+
+			// See the rt_end documentation for detailed explanations
+			// of what each of these metrics mean.
+			double r_ent, r_chisq, r_mean, r_montepicalc, r_scc;
+			rt_end(&r_ent, &r_chisq, &r_mean, &r_montepicalc, &r_scc);
+
+			EATEST_VERIFY(r_ent >= 7.8);
+		  //EATEST_VERIFY(r_chisq >= 200);                      Disabled until we can figure out why it occasionally fails.
+		  //EATEST_VERIFY(r_mean >= 127.2 && r_mean < 127.9);   Disabled until we can figure out why it occasionally fails.
+			EATEST_VERIFY(r_montepicalc >= 3.11 && r_montepicalc < 3.17);
+			EATEST_VERIFY(r_scc <= 0.01);
+		}
+
+
+
+		{  // RandomMersenneTwister test
+			RandomMersenneTwister random;
+
+			rt_init(false);
+			for(int i(0); i < 100000; i++)
+			{
+				uint32_t nRandom = random.RandomUint32Uniform();
+				rt_add(&nRandom, sizeof(nRandom));
+			}
+
+			// See the rt_end documentation for detailed explanations
+			// of what each of these metrics mean.
+			double r_ent, r_chisq, r_mean, r_montepicalc, r_scc;
+			rt_end(&r_ent, &r_chisq, &r_mean, &r_montepicalc, &r_scc);
+
+			EATEST_VERIFY(r_ent >= 7.8);
+		  //EATEST_VERIFY(r_chisq >= 200);                      Disabled until we can figure out why it occasionally fails.
+		  //EATEST_VERIFY(r_mean >= 127.2 && r_mean < 127.9);   Disabled until we can figure out why it occasionally fails.
+			EATEST_VERIFY(r_montepicalc >= 3.11 && r_montepicalc < 3.17);
+			EATEST_VERIFY(r_scc <= 0.01);
+		}
+	#endif
+
+
+	{ // RandomMersenneTwister seed serialization test.
+		RandomMersenneTwister rmt;
+		uint32_t              seedArray[RandomMersenneTwister::kSeedArrayCount * 2];
+		uint32_t              rand1, rand2;
+		unsigned              size;
+
+		size = rmt.GetSeed(seedArray, RandomMersenneTwister::kSeedArrayCount);
+		EATEST_VERIFY(size == RandomMersenneTwister::kSeedArrayCount);
+		rand1 = rmt.RandomUint32Uniform();
+		rmt.RandomUint32Uniform();
+		rmt.SetSeed(seedArray, size);
+		rand2 = rmt.RandomUint32Uniform();
+		EATEST_VERIFY(rand1 == rand2);
+
+		size = rmt.GetSeed(seedArray, RandomMersenneTwister::kSeedArrayCount * 2);
+		EATEST_VERIFY(size == RandomMersenneTwister::kSeedArrayCount);
+		rand1 = rmt.RandomUint32Uniform();
+		rmt.RandomUint32Uniform();
+		rmt.SetSeed(seedArray, size);
+		rand2 = rmt.RandomUint32Uniform();
+		EATEST_VERIFY(rand1 == rand2);
+
+		size = rmt.GetSeed(seedArray, RandomMersenneTwister::kSeedArrayCount / 2);
+		EATEST_VERIFY(size == RandomMersenneTwister::kSeedArrayCount / 2);
+		rand1 = rmt.RandomUint32Uniform();
+		rmt.RandomUint32Uniform();
+		rmt.SetSeed(seedArray, size);
+		// We can't test for equality or inequality of rand1 and rand2
+
+		// This is just a pathological test.
+		size = rmt.GetSeed(seedArray, 0);
+		EATEST_VERIFY(size == 0);
+		rand1 = rmt.RandomUint32Uniform();
+		rmt.RandomUint32Uniform();
+		rmt.SetSeed(seedArray, size);
+		rand2 = rmt.RandomUint32Uniform();
+		EATEST_VERIFY(rand1 != rand2);      // They should be different (actually one out of 4 billion times they shouldn't be) because we didn't read the entire state, but only half of it.
+	}
+
+
+	{
+		#if defined(EA_PLATFORM_DESKTOP) && !defined(EA_DEBUG) // Do this test only on fast machines, as it's compute-intensive.
+			// Range tests with FakeIncrementingRandom
+			const size_t sizes[] = { 2, 5, 10 };
+			eastl::vector<uint32_t> countBuckets(sizes[EAArrayCount(sizes) - 1], 0);
+
+			for(size_t a = 0; a < EAArrayCount(sizes); a++)
+			{
+				size_t s = sizes[a];
+
+				FakeIncrementingRandom fir;
+				eastl::fill(countBuckets.begin(), countBuckets.end(), 0);
+
+				for(uint64_t i = 0, iEnd = UINT64_C(0x100000000) / s * s; i < iEnd; i++)
+				{
+					if((i % 0x10000000) == 0)
+					   EA::UnitTest::Report("."); // Keepalive output.
+					uint32_t b = EA::StdC::RandomLimit(fir, static_cast<uint32_t>(s));
+					countBuckets[b]++;
+				}
+
+				for(eastl_size_t b = 1, c = countBuckets[0]; b < s; b++)
+				{
+					if(countBuckets[b] != c)
+					{
+						EATEST_VERIFY(countBuckets[b] == c);
+						EA::UnitTest::Report("Random distribution result buckets for limit of %I32u:\n   ", (uint32_t)s);
+						for(eastl_size_t bb = 0, bbEnd = s; bb < bbEnd; bb++)
+							EA::UnitTest::Report("%I32u%s", (uint32_t)countBuckets[bb], ((bb % 16) == 15) ? "\n" : " ");
+						EA::UnitTest::Report("\n");
+						break;
+					}
+				}
+
+				EA::UnitTest::Report(".\n"); // Keep alive output.
+			}
+		#endif
+	}
+
+	// Write out files suitable for the DieHard test suite.
+	// The version of DieHard that this author most recently
+	// worked with requires 8404992 bytes of data in a file.
+	// A copy of DieHard.exe should accompany this test.
+	// Currently, you drag a file onto it to get the results
+	// of the test. In the future we can implement the entire
+	// test within this file. It is about 3500 lines of code
+	// and would require some massaging to make it work
+	// smoothly with a unit testing system.
+
+	return nErrorCount;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ent Chi-Squared functions
+//
+// Home:
+//     http://www.fourmilab.ch/random/
+// License:
+//     This software is in the public domain. Permission to use, copy, modify, 
+//     and distribute this software and its documentation for any purpose and 
+//     without fee is hereby granted, without any conditions or restrictions. 
+//     This software is provided "as is" without express or implied warranty. 
+///////////////////////////////////////////////////////////////////////////////
+// 
+// Entropy
+//      The information density of the contents of the file, expressed as a 
+//      number of bits per character. The results above, which resulted from 
+//      processing an image file compressed with JPEG, indicate that the 
+//      file is extremely dense in information--essentially random. 
+//      Hence, compression of the file is unlikely to reduce its size. 
+//      By contrast, the C source code of the program has entropy of about 
+//      4.9 bits per character, indicating that optimal compression of the 
+//      file would reduce its size by 38%. [Hamming, pp. 104-108]
+// 
+// Chi-square Test
+//      The chi-square test is the most commonly used test for the randomness
+//      of data, and is extremely sensitive to errors in pseudorandom sequence 
+//      generators. The chi-square distribution is calculated for the stream 
+//      of bytes in the file and expressed as an absolute number and a 
+//      percentage which indicates how frequently a truly random sequence 
+//      would exceed the value calculated. We interpret the percentage as the 
+//      degree to which the sequence tested is suspected of being non-random. 
+//      If the percentage is greater than 99% or less than 1%, the sequence is 
+//      almost certainly not random. If the percentage is between 99% and 95% 
+//      or between 1% and 5%, the sequence is suspect. Percentages between 90% 
+//      and 95% and 5% and 10% indicate the sequence is "almost suspect". 
+//      Note that our JPEG file, while very dense in information, is far from 
+//      random as revealed by the chi-square test.
+// 
+//      Applying this test to the output of various pseudorandom sequence 
+//      generators is interesting. The low-order 8 bits returned by the 
+//      standard Unix rand() function, for example, yields:
+//            Chi square distribution for 500000 samples is 0.01, and randomly 
+//            would exceed this value 99.99 percent of the times. 
+// 
+//      While an improved generator [Park & Miller] reports:
+//            Chi square distribution for 500000 samples is 212.53, and randomly 
+//            would exceed this value 95.00 percent of the times. 
+// 
+//      Thus, the standard Unix generator (or at least the low-order bytes 
+//      it returns) is unacceptably non-random, while the improved generator 
+//      is much better but still sufficiently non-random to cause concern for 
+//      demanding applications. Contrast both of these software generators 
+//      with the chi-square result of a genuine random sequence created by 
+//      timing radioactive decay events.
+//            Chi square distribution for 32768 samples is 237.05, and randomly 
+//            would exceed this value 75.00 percent of the times. 
+// 
+//      See [Knuth, pp. 35-40] for more information on the chi-square test. 
+//      An interactive chi-square calculator is available at this site.
+// 
+// Arithmetic Mean
+//      This is simply the result of summing the all the bytes (bits if the -b 
+//      option is specified) in the file and dividing by the file length. 
+//      If the data are close to random, this should be about 127.5 (0.5 for -b 
+//      option output). If the mean departs from this value, the values are 
+//      consistently high or low.
+// 
+// Monte Carlo Value for Pi
+//      Each successive sequence of six bytes is used as 24 bit X and Y 
+//      co-ordinates within a square. If the distance of the randomly-generated 
+//      point is less than the radius of a circle inscribed within the square, 
+//      the six-byte sequence is considered a "hit". The percentage of hits can 
+//      be used to calculate the value of Pi. For very large streams 
+//      (this approximation converges very slowly), the value will approach the 
+//      correct value of Pi if the sequence is close to random. A 32768 byte 
+//      file created by radioactive decay yielded:
+//            Monte Carlo value for Pi is 3.139648438 (error 0.06 percent). 
+// 
+// Serial Correlation Coefficient
+//      This quantity measures the extent to which each byte in the file 
+//      depends upon the previous byte. For random sequences, this value 
+//      (which can be positive or negative) will, of course, be close to zero. 
+//      A non-random byte stream such as a C program will yield a serial 
+//      correlation coefficient on the order of 0.5. Wildly predictable data 
+//      such as uncompressed bitmaps will exhibit serial correlation coefficients 
+//      approaching 1. See [Knuth, pp. 64-65] for more details.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#define RFALSE       0
+#define RTRUE        1
+#define BINARY_MODE  RTRUE
+#define BYTE_MODE    RFALSE
+#define MONTEN       6 /* Bytes used as Monte Carlo co-ordinates. This should be no more bits than the mantissa of your "double" floating point type. */
+#define log2of10     3.32192809488736234787
+
+static int binary = RFALSE;    /* Treat input as a bitstream */
+static long ccount[256];        /* Bins to count occurrences of values */
+static long totalc = 0;         /* Total bytes counted */
+static double prob[256];        /* Probabilities per bin for entropy */
+static int mp, sccfirst;
+static unsigned int monte[MONTEN];
+static long inmont, mcount;
+static double cexp, incirc, montex, montey, montepi, scc, sccun, sccu0, scclast, scct1, scct2, scct3, ent, chisq, datasum;
+
+
+/*  LOG2  --  Calculate log to the base 2  */
+static double Local_log2(double x)
+{
+	return log2of10 * log10(x);
+}
+
+
+/*  RT_INIT  --  Initialise random test counters. Call with BINARY_MODE or BYTE_MODE */
+void rt_init(int binmode)
+{
+	int i;
+
+	binary = binmode;            /* Set binary / byte mode */
+
+	/* Initialise for calculations */
+	ent = 0.0;                   /* Clear entropy accumulator */
+	chisq = 0.0;                 /* Clear Chi-Square */
+	datasum = 0.0;               /* Clear sum of bytes for arithmetic mean */
+
+	mp = 0;                      /* Reset Monte Carlo accumulator pointer */
+	mcount = 0;                  /* Clear Monte Carlo tries */
+	inmont = 0;                  /* Clear Monte Carlo inside count */
+	incirc = 65535.0 * 65535.0;  /* In-circle distance for Monte Carlo */
+
+	sccfirst = RTRUE;            /* Mark first time for serial correlation */
+	scct1 = scct2 = scct3 = 0.0; /* Clear serial correlation terms */
+
+	incirc = pow(pow(256.0, (double) (MONTEN / 2)) - 1, 2.0);
+
+	for (i = 0; i < 256; i++) {
+		ccount[i] = 0;
+	}
+	totalc = 0;
+}
+
+
+
+/*  RT_ADD  --    Add one or more bytes to accumulation.    */
+void rt_add(void* buf, int bufl)
+{
+	unsigned char* bp =(unsigned char*)buf;
+	int oc, c, bean;
+
+	while (bean = 0, (bufl-- > 0))
+	{
+		oc = *bp++;
+
+		do {
+			if (binary) {
+				c = !!(oc & 0x80);
+			} 
+			else {
+				c = oc;
+			}
+			ccount[c]++;         /* Update counter for this bin */
+			totalc++;
+
+			/* Update inside / outside circle counts for Monte Carlo computation of PI */
+			if (bean == 0) {
+				monte[mp++] = (unsigned int)oc;         /* Save character for Monte Carlo */
+
+				if (mp >= MONTEN) {      /* Calculate every MONTEN character */
+					int mj;
+
+					mp = 0;
+					mcount++;
+					montex = montey = 0;
+					for (mj = 0; mj < MONTEN / 2; mj++) {
+						montex = (montex * 256.0) + monte[mj];
+						montey = (montey * 256.0) + monte[(MONTEN / 2) + mj];
+					}
+					if ((montex * montex + montey *  montey) <= incirc) {
+						inmont++;
+					}
+				}
+			}
+
+			/* Update calculation of serial correlation coefficient */
+			sccun = (double)c;
+
+			if (sccfirst) {
+				sccfirst = RFALSE;
+				scclast = 0;
+				sccu0 = sccun;
+			} 
+			else {
+				scct1 = scct1 + scclast * sccun;
+			}
+			scct2 = scct2 + sccun;
+			scct3 = scct3 + (sccun * sccun);
+			scclast = sccun;
+			oc <<= 1;
+		} while (binary && (++bean < 8));
+	}
+}
+
+
+
+/*  RT_END  -- Complete calculation and return results.  */
+void rt_end(double* r_ent, double* r_chisq, double* r_mean,
+			double* r_montepicalc, double* r_scc)
+{
+	int i;
+	double a;
+
+	/* Complete calculation of serial correlation coefficient */
+
+	scct1 = scct1 + scclast * sccu0;
+	scct2 = scct2 * scct2;
+	scc = totalc * scct3 - scct2;
+	if (scc == 0.0) {
+		scc = -100000;
+	} 
+	else {
+		scc = (totalc * scct1 - scct2) / scc;
+	}
+
+	/* Scan bins and calculate probability for each bin and Chi-Square distribution */
+
+	cexp = totalc / (binary ? 2.0 : 256.0);  /* Expected count per bin */
+	for (i = 0; i < (binary ? 2 : 256); i++) {
+		prob[i] = (double) ccount[i] / totalc;
+		a = ccount[i] - cexp;
+		chisq = chisq + (a * a) / cexp;
+		datasum += ((double) i) * ccount[i];
+	}
+
+	/* Calculate entropy */
+	for (i = 0; i < (binary ? 2 : 256); i++) {
+		if (prob[i] > 0.0) {
+			ent += prob[i] * Local_log2(1 / prob[i]);
+		}
+	}
+
+	/* Calculate Monte Carlo value for PI from percentage of hits within the circle */
+	montepi = 4.0 * (((double) inmont) / mcount);
+
+	/* Return results through arguments */
+	*r_ent = ent;
+	*r_chisq = chisq;
+	*r_mean = datasum / totalc;
+	*r_montepicalc = montepi;
+	*r_scc = scc;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+#if 0
+
+
+static double get_double()
+{
+	return 1.0;
+}
+
+
+static double CalculateSqrm(double a, double b)
+{
+	return ((a - b) * (a - b)) / b;
+}
+
+
+static double CalculatePhi(double x)
+{
+	static const double v[15] = 
+	{ 
+		1.2533141373155,    .6556795424187985,  .4213692292880545,
+		.3045902987101033,  .2366523829135607,  .1928081047153158,
+		.1623776608968675,  .1401041834530502,  .1231319632579329,
+		.1097872825783083,  .09902859647173193, .09017567550106468,
+		.08276628650136917, .0764757610162485,  .07106958053885211
+	};
+
+	// Local variables
+	double cphi, a, b, h;
+	double z, sum, pwr;
+	int    i, j;
+
+	if (fabs(x) > 7.0)
+	{
+		if (x >= 0.0)
+			return 1.0;
+		return 0.0;
+	}
+
+	if (x>=0.0)
+		cphi = 0.0;
+	else
+		cphi = 1.0;
+
+	j   = (int) (fabs(x) + 0.5);
+	j   = std::min<int>(j, 14);
+	z   = (double) j;
+	h   = fabs(x) - z;
+	a   = v[j];
+	b   = z * a - 1.0;
+	pwr = 1.0;
+	sum = a + h * b;
+
+	for (i = 2; i <= (24-j); i += 2)
+	{
+		a    = (a + z * b) / i;
+		b    = (b + z * a) / (i + 1);
+		pwr *= h * h;
+		sum += pwr * (a + h * b);
+	}
+
+	cphi = sum * exp(x * -0.5 * x - 0.918938533204672);
+
+	if (x < 0.0)
+		return cphi;
+
+	return 1.0 - cphi;
+}
+
+
+static double CalculateChisq(double x, int n)
+{
+	// System generated locals
+	double ret_val;
+
+	// Local variables
+	double  d;
+	long    i, l;
+	double  s, t;
+	double  xmin;
+
+	if (x <= 0.0)
+		return 0.0;
+
+	if (n > 20)
+	{
+		t = (pow( x / n, 0.33333) - 1.0 + 0.22222 / n) / sqrt(0.22222 / n);
+		return CalculatePhi(std::min(t, 8.0));
+	}
+
+	l = 4 - n % 2;
+
+	d = (double) std::min(1, n / 3);
+	ret_val = 0.0;
+
+	for (i = l; i <= n; i += 2)
+	{
+		d = d * x / (i - 2);
+		ret_val += d;
+	}
+
+	xmin = std::min(x * 0.5, 50.0);
+
+	if (l == 3)
+	{
+		s = sqrt( xmin );
+		return  CalculatePhi(s/0.7071068) - exp(-xmin) * 0.564189 * ret_val / s;
+	}
+
+	return 1.0 - exp(-xmin) * (ret_val + 1.0);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestCraps
+//
+// This is the Craps test. It plays 200,000 games of craps, finds
+// the number of wins and the number of throws necessary to end
+// each game. The number of wins should be (very close to) a
+// normal with mean 200000p and variance 200000p(1 - p), with
+// p = 244 / 495. Throws necessary to complete the game can vary
+// from 1 to infinity, but counts for all > 21 are lumped with 21.
+// A chi-square test is made on the #-of-throws cell counts.
+// Each 32-bit integer from the test file provides the value for
+// the throw of a die, by floating to [0, 1), multiplying by 6
+// and taking 1 plus the integer part of the result.
+//
+static void TestCraps(double& pvalueWins, double& pvalueThrows)
+{
+	static  long   nt[22];
+	static  double e[22];
+
+	double  t;
+	double  pwins;
+	double  av;            // Expected win count.
+	double  sd;
+	double  ex;
+	double  sum;
+	long    ng;
+	long    gc;
+	long    nwins;        // Actual win count.
+	double  pthrows;
+	int     nthrows;
+	int     point;
+	int     i, m, k;
+
+	e[1] = 1.0 / 3.0;
+	sum  = e[1];
+
+	for (k = 2; k <= 20; ++k)
+	{
+		e[k] = ( pow(27.0/36.0, (double) k-2) * 27.0 +
+				 pow(26.0/36.0, (double) k-2) * 40.0 +
+				 pow(25.0/36.0, (double) k-2) * 55.0 ) / 648.0;
+
+		sum += e[k];
+	}
+
+	e[21] = 1.0 - sum;
+	ng    = 200000;
+	nwins = 0;
+
+	for (i = 1; i <= 21; ++i)
+		nt[i] = 0;
+
+	for (gc = 1; gc <= ng; ++gc)
+	{
+		point = (int)(get_double() * 6.0) + (int)(get_double() * 6.0) + 2;
+		nthrows = 1;
+
+		if ((point == 7) || (point == 11))
+			++nwins;
+		else if ((point != 2) && (point != 3) && (point != 12))
+		{
+			for(;;)
+			{
+				++nthrows;
+
+				k = (int)(get_double() * 6.0) + (int)(get_double() * 6.0) + 2;
+
+				if (k == 7)
+					break;
+
+				if (k == point)
+				{
+					++nwins;
+					break;
+				}
+			}
+		}
+
+		m = std::min<int>(21, nthrows);
+		++nt[m];
+	}
+
+	av = ng * 244.0 / 495.0;
+	sd = sqrt(av * 251.0 / 495.0);
+	t  = (nwins - av) / sd;
+
+	//dprintf("  Results of craps test for %s\n", filename);
+	//dprintf("  No. of wins:  Observed Expected\n");
+	//dprintf("  %9ld %11.2f\n", nwins, av);
+
+	pwins = CalculatePhi(t);
+
+	//dprintf(" %8ld= No. of wins, z-score=%6.3f pvalue=%7.5f\n", nwins, t, pwins);
+	//dprintf("    Analysis of Throws-per-Game:\n");
+
+	sum = 0.0;
+
+	for (i = 1; i <= 21; ++i)
+	{
+		ex = ng * e[i];
+		sum += CalculateSqrm((double)nt[i], ex);
+	}
+
+	pthrows = CalculateChisq(sum, 20);
+
+	//dprintf(" Chisq=%7.2f for 20 degrees of freedom, p=%8.5f\n", sum, pthrows);
+	//dprintf("                    Throws Observed Expected  Chisq      Sum\n");
+
+	//sum = 0.0;
+
+	//for (i = 1; i <= 21; ++i)
+	//{
+	//     ex = ng * e[i];
+	//     t = sqrm((double)nt[i], ex);
+	//     sum += t;
+	//
+	//     dprintf("%19d %8ld %10.1f %7.3f %8.3f\n", i, nt[i], ex, t, sum);
+	//}
+
+	//save_pvalue(pwins);
+	//save_pvalue(pthrows);
+
+	//dprintf("                SUMMARY  FOR %s\n", filename);
+	//dprintf("                     p-value for no. of wins:%8.6f\n", pwins);
+	//dprintf("                     p-value for throws/game:%8.6f\n", pthrows);
+
+	pvalueWins   = pwins;
+	pvalueThrows = pthrows;
+}
+
+#endif
+
+
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 3001 - 0
test/source/TestScanf.cpp

@@ -0,0 +1,3001 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAScanf.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EAMathHelp.h>
+#include <EAStdC/Int128_t.h>
+#include <EAStdC/internal/Config.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EAAssert/eaassert.h>
+#include <limits.h>
+#include <float.h>
+#include <string.h>
+
+
+
+
+#if !defined(LLONG_MAX) // Some C++ standard libraries don't define LLONG_MAX, etc.
+	#if defined(__LONG_LONG_MAX__) // GCC
+		#define  LLONG_MIN  (-LLONG_MAX-1)
+		#define  LLONG_MAX  __LONG_LONG_MAX__
+		#define ULLONG_MIN  0
+		#define ULLONG_MAX  (LLONG_MAX * 2ULL + 1)
+	#else
+		#define  LLONG_MIN  INT64_MIN
+		#define  LLONG_MAX  INT64_MAX
+		#define ULLONG_MIN  0
+		#define ULLONG_MAX  UINT64_MAX
+	#endif
+#endif
+
+#ifdef EA_ASSERT_ENABLED
+	#define VERIFY_ASSERTCOUNT(count) EATEST_VERIFY(v.assertCount_ == count)
+#else
+	#define VERIFY_ASSERTCOUNT(count)
+#endif
+
+
+struct Values
+{
+	// char_ is defined explicitly as 'signed char' rather than 'char' (which is determined signed/unsigned by the compiler) on purpose.
+	// If char were interpreted as unsigned char then there will be no type promotion for negative values which our tests assume (we want 0xFF to be promoted to 0xFFFFFFFF). 
+	// Why work around the unsigned/signed char issue? There would be little gain duplicating test code per platform+compiler, particularly when a user 
+	// can force the signedness to be one way or the other regardless. Additionally, we already test our suite for explicit signed and unsigned char types.
+	signed char         char_[8];
+	signed char         schar_[8];
+	unsigned char       uchar_[8];
+	wchar_t             wchar_[8];
+	short               short_[8];
+	unsigned short      ushort_[8];
+	int                 int_[8];
+	unsigned int        uint_[8];
+	long                long_[8];
+	unsigned long       ulong_[8];
+	long long           longlong_[8];
+	unsigned long long  ulonglong_[8];
+	int8_t              int8_[8];
+	uint8_t             uint8_[8];
+	int16_t             int16_[8];
+	uint16_t            uint16_[8];
+	int32_t             int32_[8];
+	uint32_t            uint32_[8];
+	int64_t             int64_[8];
+	uint64_t            uint64_[8];
+  //EA::StdC::int128_t  int128_[8];     // int128_t has constructors and so is not a POD and cannot be part of the Values union.
+  //EA::StdC::uint128_t uint128_[8];
+	char8_t             char8_[8];
+	char16_t            char16_[8];
+	char32_t            char32_[8];
+	char8_t             str8_[8][64];
+	char16_t            str16_[8][64];
+	char32_t            str32_[8][64];
+	wchar_t             strw_[8][64];
+	float               float_[8];
+	double              double_[8];
+	size_t              size_[8];
+	ptrdiff_t           ptrdiff_[8];
+	intptr_t            intptr_[8];
+	uintptr_t           uintptr_[8];
+	intmax_t            intmax_[8];
+	uintmax_t           uintmax_[8];
+	void*               voidptr_[8];
+	static uint32_t     assertCount_; //Static to allow for use from lambda with empty capture set.  Implicitly not thread safe.
+
+	int Clear()
+	{
+		memset(this, 0xff, sizeof(*this));
+		assertCount_ = 0;
+		return 0;
+	}
+};
+
+uint32_t Values::assertCount_ = 0;
+
+static int TestCRTVsscanf(const char8_t* pBuffer, const char8_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	int n = EA::StdC::Vsscanf(pBuffer, pFormat, vList);
+	va_end(vList);
+	return n;
+}
+
+static int TestCRTVsscanf(const char16_t* pBuffer, const char16_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	int n = EA::StdC::Vsscanf(pBuffer, pFormat, vList);
+	va_end(vList);
+	return n;
+}
+
+static int TestCRTVsscanf(const char32_t* pBuffer, const char32_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	int n = EA::StdC::Vsscanf(pBuffer, pFormat, vList);
+	va_end(vList);
+	return n;
+}
+
+
+
+static inline double DoubleAbsoluteDifference(double x1, double x2)
+{
+	return (x1 < x2) ? (x2 - x1) : (x1 - x2);
+}
+
+static inline bool DoubleEqual(double x1, double x2)
+{
+	if(x1 < 1e-15)
+		return (x2 < 1e-15);
+	else if(x2 < 1e-15)
+		return (x1 < 1e-15);
+	else
+		return DoubleAbsoluteDifference((x1 - x2) / x1, 1e-15) < 1e-13;
+}
+
+static inline double FloatAbsoluteDifference(float x1, float x2)
+{
+	return (x1 < x2) ? (x2 - x1) : (x1 - x2);
+}
+
+static inline bool FloatEqual(float x1, float x2)
+{
+	if(x1 < 1e-7f)
+		return (x2 < 1e-7f);
+	else if(x2 < 1e-7f)
+		return (x1 < 1e-7f);
+	else
+		return FloatAbsoluteDifference((x1 - x2) / x1, 1e-7f) < 1e-5f;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfLimits
+//
+static int TestScanfLimits()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// Until our custom hand-implemented scanf is some day completed, we don't
+	// have a portable version of the scanf family of functions. In the meantime
+	// we can only support it on Microsoft platforms as opposed to GCC.
+	int    n;
+	Values v;
+
+	{ // Test limits
+
+		// char
+		v.Clear();
+		n = Sscanf("-128 127", "%hhd %hhd", &v.schar_[0], &v.schar_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.schar_[0] == SCHAR_MIN);
+		EATEST_VERIFY(v.schar_[1] == SCHAR_MAX);
+
+		v.Clear();
+		n = Sscanf("0x80 0x7f", "%hhx %hhx", &v.schar_[0], &v.schar_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.schar_[0] == SCHAR_MIN);
+		EATEST_VERIFY(v.schar_[1] == SCHAR_MAX);
+
+
+		// unsigned char
+		v.Clear();
+		n = Sscanf("-0 255", "%hhu %hhu", &v.uchar_[0], &v.uchar_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.uchar_[0] == 0);
+		EATEST_VERIFY(v.uchar_[1] == UCHAR_MAX);
+
+		v.Clear();
+		n = Sscanf("0 0xff", "%hhx %hhx", &v.uchar_[0], &v.uchar_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.uchar_[0] == 0);
+		EATEST_VERIFY(v.uchar_[1] == UCHAR_MAX);
+
+
+		// short
+		v.Clear();
+		n = Sscanf("-32768 32767", "%hd %hd", &v.short_[0], &v.short_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.short_[0] == SHRT_MIN);
+		EATEST_VERIFY(v.short_[1] == SHRT_MAX);
+
+		v.Clear();
+		n = Sscanf("0x8000 0x7fff", "%hx %hx", &v.short_[0], &v.short_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.short_[0] == SHRT_MIN);
+		EATEST_VERIFY(v.short_[1] == SHRT_MAX);
+
+
+		// unsigned short
+		v.Clear();
+		n = Sscanf("-0 65535", "%hu %hu", &v.ushort_[0], &v.ushort_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ushort_[0] == 0);
+		EATEST_VERIFY(v.ushort_[1] == USHRT_MAX);
+
+		v.Clear();
+		n = Sscanf("0 0xffff", "%hx %hx", &v.ushort_[0], &v.ushort_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ushort_[0] == 0);
+		EATEST_VERIFY(v.ushort_[1] == USHRT_MAX);
+
+
+		// int
+		v.Clear();
+		n = Sscanf("-0 +0 -2147483648 +2147483647", "%d %d %d %d", &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+		EATEST_VERIFY(n == 4);
+		EATEST_VERIFY(v.int_[0] == 0);
+		EATEST_VERIFY(v.int_[1] == 0);
+		EATEST_VERIFY(v.int_[2] == INT32_MIN); 
+		EATEST_VERIFY(v.int_[3] == INT32_MAX);
+
+		v.Clear();
+		n = Sscanf("-0 +0 0x80000000 0x7fffffff", "%x %x %x %x", &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+		EATEST_VERIFY(n == 4);
+		EATEST_VERIFY(v.int_[0] == 0);
+		EATEST_VERIFY(v.int_[1] == 0);
+		EATEST_VERIFY(v.int_[2] == INT32_MIN); 
+		EATEST_VERIFY(v.int_[3] == INT32_MAX);
+
+
+		// unsigned int
+		v.Clear();
+		n = Sscanf("0 4294967295", "%u %u", &v.uint_[0], &v.uint_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.uint_[0] == 0);
+		EATEST_VERIFY(v.uint_[1] == UINT32_MAX);
+
+		v.Clear();
+		n = Sscanf("0000000 0xffffffff", "%x %x", &v.uint_[0], &v.uint_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.uint_[0] == 0);
+		EATEST_VERIFY(v.uint_[1] == UINT32_MAX);
+
+
+		// long
+		#if LONG_MAX == INT32_MAX
+			v.Clear();
+			n = Sscanf("-2147483648 +2147483647", "%ld %ld", &v.long_[0], &v.long_[1]);
+		#elif LONG_MAX == INT64_MAX
+			v.Clear();
+			n = Sscanf("-9223372036854775808 9223372036854775807", "%ld %ld", &v.long_[0], &v.long_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.long_[0] == LONG_MIN);
+		EATEST_VERIFY(v.long_[1] == LONG_MAX);
+
+		#if LONG_MAX == INT32_MAX
+			v.Clear();
+			n = Sscanf("0x80000000 0x7fffffff", "%lx %lx", &v.long_[0], &v.long_[1]);
+		#elif LONG_MAX == INT64_MAX
+			v.Clear();
+			n = Sscanf("0x8000000000000000 0x7fffffffffffffff", "%lx %lx", &v.long_[0], &v.long_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.long_[0] == LONG_MIN);
+		EATEST_VERIFY(v.long_[1] == LONG_MAX);
+
+
+		// ulong
+		#if ULONG_MAX == UINT_MAX
+			v.Clear();
+			n = Sscanf("-0 4294967295", "%ld %ld", &v.ulong_[0], &v.ulong_[1]);
+		#elif ULONG_MAX > UINT_MAX
+			v.Clear();
+			n = Sscanf("-0 18446744073709551615", "%ld %ld", &v.ulong_[0], &v.ulong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ulong_[0] == 0);
+		EATEST_VERIFY(v.ulong_[1] == ULONG_MAX);
+
+		#if ULONG_MAX == UINT_MAX
+			v.Clear();
+			n = Sscanf("0 0xffffffff", "%lx %lx", &v.ulong_[0], &v.ulong_[1]);
+		#elif ULONG_MAX > UINT_MAX
+			v.Clear();
+			n = Sscanf("-0 0xffffffffffffffff", "%lx %lx", &v.ulong_[0], &v.ulong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ulong_[0] == 0);
+		EATEST_VERIFY(v.ulong_[1] == ULONG_MAX);
+
+
+		// long long
+		#if LLONG_MAX == INT64_MAX
+			v.Clear();
+			n = Sscanf("-9223372036854775808 9223372036854775807", "%lld %lld", &v.longlong_[0], &v.longlong_[1]);
+		#else
+			v.Clear();
+			n = Sscanf("-170141183460469231731687303715884105728 170141183460469231731687303715884105727", "%lld %lld", &v.longlong_[0], &v.longlong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.longlong_[0] == LLONG_MIN);
+		EATEST_VERIFY(v.longlong_[1] == LLONG_MAX);
+
+		#if LLONG_MAX == INT64_MAX
+			v.Clear();
+			n = Sscanf("0x8000000000000000 0x7fffffffffffffff", "%llx %llx", &v.longlong_[0], &v.longlong_[1]);
+		#else
+			v.Clear();
+			n = Sscanf("0x80000000000000000000000000000000 0x7fffffffffffffffffffffffffffffff", "%llx %llx", &v.longlong_[0], &v.longlong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.longlong_[0] == LLONG_MIN);
+		EATEST_VERIFY(v.longlong_[1] == LLONG_MAX);
+
+		// unsigned long long
+		#if ULLONG_MAX == UINT64_MAX
+			v.Clear();
+			n = Sscanf("0 18446744073709551615", "%lld %lld", &v.ulonglong_[0], &v.ulonglong_[1]);
+		#else
+			v.Clear();
+			n = Sscanf("0 170141183460469231731687303715884105727", "%lld %lld", &v.ulonglong_[0], &v.ulonglong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ulonglong_[0] == 0);
+		EATEST_VERIFY(v.ulonglong_[1] == ULLONG_MAX);
+
+		#if ULLONG_MAX == UINT64_MAX
+			v.Clear();
+			n = Sscanf("0x0000000000000000 0xffffffffffffffff", "%llx %llx", &v.ulonglong_[0], &v.ulonglong_[1]);
+		#else
+			v.Clear();
+			n = Sscanf("0x00000000000000000000000000000000 0xffffffffffffffffffffffffffffffff", "%llx %llx", &v.ulonglong_[0], &v.ulonglong_[1]);
+		#endif
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(v.ulonglong_[0] == 0);
+		EATEST_VERIFY(v.ulonglong_[1] == ULLONG_MAX);
+
+
+		// float
+		v.Clear();
+		n = Sscanf("1.175494351e-38 3.402823466e+38", "%f %f", &v.float_[0], &v.float_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(FloatEqual(v.float_[0], 1.1754944e-38f)); // This is actually not a good enough test.
+		EATEST_VERIFY(FloatEqual(v.float_[1], 3.4028234e+38f));
+
+
+		// double
+		v.Clear();
+		n = Sscanf("2.2250738585072014e-308 1.7976931348623158e+308", "%lf %lf", &v.double_[0], &v.double_[1]);
+		EATEST_VERIFY(n == 2);
+		EATEST_VERIFY(DoubleEqual(v.double_[0], 2.2250738585072014e-308)); // This is actually not a good enough test.
+		#if defined(EA_PLATFORM_WIN32)
+			EATEST_VERIFY(DoubleEqual(v.double_[1], 1.7976931348623158e+308));
+		#endif
+	}
+
+	// Regression of a user-reported bug:
+	{
+		float f;
+		Sscanf("0.0001100f", "%f", &f);
+		EATEST_VERIFY(FloatEqual(f, 0.0001100f));
+		if(!FloatEqual(f, 0.0001100f))
+			EA::UnitTest::Report("%f\n", f);
+	}
+
+	// Test fractions
+	{
+		const int nErrorCountSaved = nErrorCount; // We use this to exit the loop below upon the first found error.
+		char buffer[10] = { '0', '.', '0', '0', '0', '0', '0', '0' };
+		
+		for(int i = 0; (i < 1000000) && (nErrorCount == nErrorCountSaved); i += 23)
+		{
+			buffer[2] = (char)('0' + (i / 100000 % 10));
+			buffer[3] = (char)('0' + (i / 10000 % 10));
+			buffer[4] = (char)('0' + (i / 1000 % 10));
+			buffer[5] = (char)('0' + (i / 100 % 10));
+			buffer[6] = (char)('0' + (i / 10 % 10));
+			buffer[7] = (char)('0' + (i / 1 % 10));
+
+			n = Sscanf(buffer, "%f", &v.float_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(FloatEqual(v.float_[0], (float)i / 1000000));
+			if(!FloatEqual(v.float_[0], (float)i / 1000000))
+				EA::UnitTest::Report("%f %f\n", v.float_[0], (float)i / 1000000);
+
+			char buffer2[32];
+			sprintf(buffer2, "%f", v.float_[0]);
+			EATEST_VERIFY(Strcmp(buffer2, buffer) == 0);
+			if(Strcmp(buffer2, buffer) != 0)
+				EA::UnitTest::Report("%s %s\n", buffer2, buffer);
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfMisc
+//
+static int TestScanfMisc()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	int    n;
+	Values v;
+
+	{   // Test of sscanf calls culled from some EA code.
+		{
+			v.Clear();
+			n = Sscanf("", "");
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16(""), EA_CHAR16(""));
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32(""), EA_CHAR32(""));
+			EATEST_VERIFY(n == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("a", "%s", v.str8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("a"), EA_CHAR16("%s"), v.strw_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("a")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("a"), EA_CHAR32("%s"), v.strw_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("a")) == 0);
+		}
+
+		{   // String tests
+			// We accept %hc, %c, %lc, %I8c, %I16c, %I32c (regular, regular, wide, char8_t, char16_t, char32_t)
+			// We accept %hC, %C, %lC, %I8C, %I16C, %I32C (regular, wide,    wide, char8_t, char16_t, char32_t)
+			// We accept %hs, %s, %ls, %I8s, %I16s, %I32s (regular, regular, wide, char8_t, char16_t, char32_t)
+			// We accept %hS, %S, %lS, %I8s, %I16s, %I32s (regular, wide,    wide, char8_t, char16_t, char32_t)
+
+			{   // char8_t
+				v.Clear();
+				n = Sscanf("a b c d e f", "%hc %c %lc %I8c %I16c %I32c", &v.char_[0], &v.char_[1], &v.wchar_[0], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+				EATEST_VERIFY(n == 6);
+				EATEST_VERIFY((v.char_[0] == 'a') && (v.char_[1] == 'b') && (v.wchar_[0] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+
+				v.Clear();
+				n = Sscanf("a b c d e f", "%hC %C %lC %I8C %I16C %I32C", &v.char_[0], &v.wchar_[0], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+				EATEST_VERIFY(n == 6);
+				EATEST_VERIFY((v.char_[0] == 'a') && (v.wchar_[0] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+
+				v.Clear();
+				n = Sscanf("a b c d e f", "%hs %s %ls %I8s %I16s %I32s", &v.str8_[0], &v.str8_[1], &v.strw_[0], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+				EATEST_VERIFY(n == 6);
+				EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+				EATEST_VERIFY(Strcmp(v.str8_[1], "b") == 0);
+				EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("c")) == 0);
+				EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+				EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+				EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+
+				v.Clear();
+				n = Sscanf("a b c d e f", "%hS %S %lS %I8S %I16S %I32S", &v.str8_[0], &v.strw_[0], &v.strw_[1], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+				EATEST_VERIFY(n == 6);
+				EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+				EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("b")) == 0);
+				EATEST_VERIFY(Strcmp(v.strw_[1], EA_WCHAR("c")) == 0);
+				EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+				EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+				EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+			}
+
+			{   // char16_t
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT // Microsoft style means that the meanings of S/C and s/c are reversed for non-char8_t Sprintf.
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hc %c %lc %I8c %I16c %I32c"), &v.char_[0], &v.wchar_[0], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.wchar_[0] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#else
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hc %c %lc %I8c %I16c %I32c"), &v.char_[0], &v.char_[1], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.char_[1] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hC %C %lC %I8C %I16C %I32C"), &v.char_[0], &v.char_[1], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.char_[1] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#else
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hC %C %lC %I8C %I16C %I32C"), &v.char_[0], &v.wchar_[0], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.wchar_[0] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hs %s %ls %I8s %I16s %I32s"), &v.str8_[0], &v.strw_[0], &v.strw_[1], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("b")) == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[1], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#else
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hs %s %ls %I8s %I16s %I32s"), &v.str8_[0], &v.str8_[1], &v.strw_[0], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[1], "b") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hS %S %lS %I8S %I16S %I32S"), &v.str8_[0], &v.str8_[1], &v.strw_[0], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[1], "b") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#else
+					n = Sscanf(EA_CHAR16("a b c d e f"), EA_CHAR16("%hS %S %lS %I8S %I16S %I32S"), &v.str8_[0], &v.strw_[0], &v.strw_[1], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("b")) == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[1], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#endif
+			}
+
+			{   // char32_t
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT // Microsoft style means that the meanings of S/C and s/c are reversed for non-char8_t Sprintf.
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hc %c %lc %I8c %I16c %I32c"), &v.char_[0], &v.wchar_[0], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.wchar_[0] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#else
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hc %c %lc %I8c %I16c %I32c"), &v.char_[0], &v.char_[1], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.char_[1] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hC %C %lC %I8C %I16C %I32C"), &v.char_[0], &v.char_[1], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.char_[1] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#else
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hC %C %lC %I8C %I16C %I32C"), &v.char_[0], &v.wchar_[0], &v.wchar_[1], &v.char8_[0], &v.char16_[0], &v.char32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY((v.char_[0] == 'a') && (v.wchar_[0] == 'b') && (v.wchar_[1] == 'c') && (v.char8_[0] == 'd') && (v.char16_[0] == 'e') && (v.char32_[0] == 'f'));
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hs %s %ls %I8s %I16s %I32s"), &v.str8_[0], &v.strw_[0], &v.strw_[1], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("b")) == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[1], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#else
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hs %s %ls %I8s %I16s %I32s"), &v.str8_[0], &v.str8_[1], &v.strw_[0], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[1], "b") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#endif
+
+				v.Clear();
+				#if EASCANF_MS_STYLE_S_FORMAT
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hS %S %lS %I8S %I16S %I32S"), &v.str8_[0], &v.str8_[1], &v.strw_[0], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[1], "b") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#else
+					n = Sscanf(EA_CHAR32("a b c d e f"), EA_CHAR32("%hS %S %lS %I8S %I16S %I32S"), &v.str8_[0], &v.strw_[0], &v.strw_[1], &v.str8_[2], &v.str16_[0], &v.str32_[0]);
+					EATEST_VERIFY(n == 6);
+					EATEST_VERIFY(Strcmp(v.str8_[0], "a") == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("b")) == 0);
+					EATEST_VERIFY(Strcmp(v.strw_[1], EA_WCHAR("c")) == 0);
+					EATEST_VERIFY(Strcmp(v.str8_[2], "d") == 0);
+					EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("e")) == 0);
+					EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("f")) == 0);
+				#endif
+			}
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("abc 123", "%I8s\t%i", v.str8_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "abc") == 0);
+			EATEST_VERIFY(v.int_[0] == 123);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abc 123"), EA_CHAR16("%I16s\t%i"), v.str16_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("abc")) == 0);
+			EATEST_VERIFY(v.int_[0] == 123);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abc 123"), EA_CHAR32("%I32s\t%i"), v.str32_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("abc")) == 0);
+			EATEST_VERIFY(v.int_[0] == 123);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0", "%lf", &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0"), EA_CHAR16("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0"), EA_CHAR32("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-123.456", "%f", &v.float_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -123.456f));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-123.456"), EA_CHAR16("%f"), &v.float_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -123.456f));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-123.456"), EA_CHAR32("%f"), &v.float_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -123.456f));
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-123.456", "%lf", &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], -123.456));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-123.456"), EA_CHAR16("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], -123.456));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-123.456"), EA_CHAR32("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], -123.456));
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-123.456e4", "%lf", &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == -123.456e4);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-123.456e4"), EA_CHAR16("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == -123.456e4);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-123.456e4"), EA_CHAR32("%lf"), &v.double_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.double_[0] == -123.456e4);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("12 abcdef", "%4u%n", &v.uint_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.uint_[0] == 12);
+			EATEST_VERIFY(v.int_[0] == 2);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("12 abcdef"), EA_CHAR16("%4u%n"), &v.uint_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.uint_[0] == 12);
+			EATEST_VERIFY(v.int_[0] == 2);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("12 abcdef"), EA_CHAR32("%4u%n"), &v.uint_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.uint_[0] == 12);
+			EATEST_VERIFY(v.int_[0] == 2);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("Test, more text -999 1.345e22 0x12345678", "%s %s %*s %i %lf 0x%08x", v.str8_[0], v.str8_[1], &v.int_[0], &v.double_[0], &v.uint_[1]);
+			EATEST_VERIFY(n == 5);
+			EATEST_VERIFY(!Strcmp("Test,", v.str8_[0]));
+			EATEST_VERIFY(!Strcmp("more", v.str8_[1]));
+			EATEST_VERIFY(v.int_[0] == -999);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], 1.345e22));
+			EATEST_VERIFY(v.uint_[1] == 0x12345678);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("Test, more text -999 1.345e22 0x12345678"), EA_CHAR16("%I16s %I16s %*s %i %lf 0x%08x"), v.str16_[0], v.str16_[1], &v.int_[0], &v.double_[0], &v.uint_[1]);
+			EATEST_VERIFY(n == 5);
+			EATEST_VERIFY(!Strcmp(EA_CHAR16("Test,"), v.str16_[0]));
+			EATEST_VERIFY(!Strcmp(EA_CHAR16("more"), v.str16_[1]));
+			EATEST_VERIFY(v.int_[0] == -999);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], 1.345e22));
+			EATEST_VERIFY(v.uint_[1] == 0x12345678);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("Test, more text -999 1.345e22 0x12345678"), EA_CHAR32("%I32s %I32s %*s %i %lf 0x%08x"), v.str32_[0], v.str32_[1], &v.int_[0], &v.double_[0], &v.uint_[1]);
+			EATEST_VERIFY(n == 5);
+			EATEST_VERIFY(!Strcmp(EA_CHAR32("Test,"), v.str32_[0]));
+			EATEST_VERIFY(!Strcmp(EA_CHAR32("more"), v.str32_[1]));
+			EATEST_VERIFY(v.int_[0] == -999);
+			EATEST_VERIFY(DoubleEqual(v.double_[0], 1.345e22));
+			EATEST_VERIFY(v.uint_[1] == 0x12345678);
+		}
+
+		#if (EA_PLATFORM_PTR_SIZE == 8)
+		{
+			v.Clear();
+			n = Sscanf("1234567843434343", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == UINT64_C(0x1234567843434343));
+
+			v.Clear();
+			n = Sscanf("ffffffffffffffff", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == UINT64_C(0xffffffffffffffff));
+
+			v.Clear();
+			n = Sscanf("0xffffffffffffffff", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == UINT64_C(0xffffffffffffffff));
+		}
+		#else
+		{
+			v.Clear();
+			n = Sscanf("12345678", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0x12345678);
+
+			v.Clear();
+			n = Sscanf("ffffffff", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+
+			v.Clear();
+			n = Sscanf("0xffffffff", "%p", &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+		}
+		#endif
+	}
+
+
+	// UTF8 / UCS2 conversions
+	{   
+		{
+			v.Clear();
+			n = Sscanf("a " "\xC2" "\xA9" "\xE2" "\x89" "\xA0", "%ls %ls", v.strw_[0], v.strw_[1]); // U+00A9 => 0xC2 0xA9. U+2260 => 0xE2 0x89 0xA0
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.strw_[0][0] == 'a');
+			EATEST_VERIFY(v.strw_[0][1] == 0);
+			EATEST_VERIFY(v.strw_[1][0] == 0x00A9);
+			EATEST_VERIFY(v.strw_[1][1] == 0x2260);
+			EATEST_VERIFY(v.strw_[1][2] == 0);
+
+			v.Clear();
+			n = Sscanf("a " "\xC2" "\xA9" "\xE2" "\x89" "\xA0", "%I16s %I16s", v.str16_[0], v.str16_[1]); // U+00A9 => 0xC2 0xA9. U+2260 => 0xE2 0x89 0xA0
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.str16_[0][0] == 'a');
+			EATEST_VERIFY(v.str16_[0][1] == 0);
+			EATEST_VERIFY(v.str16_[1][0] == 0x00A9);
+			EATEST_VERIFY(v.str16_[1][1] == 0x2260);
+			EATEST_VERIFY(v.str16_[1][2] == 0);
+
+			v.Clear();
+			n = Sscanf("a " "\xC2" "\xA9" "\xE2" "\x89" "\xA0", "%I32s %I32s", v.str32_[0], v.str32_[1]); // U+00A9 => 0xC2 0xA9. U+2260 => 0xE2 0x89 0xA0
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.str32_[0][0] == 'a');
+			EATEST_VERIFY(v.str32_[0][1] == 0);
+			EATEST_VERIFY(v.str32_[1][0] == 0x00A9);
+			EATEST_VERIFY(v.str32_[1][1] == 0x2260);
+			EATEST_VERIFY(v.str32_[1][2] == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf(EA_CHAR16("a \x00A9\x2260"), EA_CHAR16("%hs %hs"), v.str8_[0], v.str8_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY((uint8_t)v.str8_[0][0] == 'a');
+			EATEST_VERIFY((uint8_t)v.str8_[0][1] == 0);
+			EATEST_VERIFY((uint8_t)v.str8_[1][0] == 0xC2);
+			EATEST_VERIFY((uint8_t)v.str8_[1][1] == 0xA9);
+			EATEST_VERIFY((uint8_t)v.str8_[1][2] == 0xE2);
+			EATEST_VERIFY((uint8_t)v.str8_[1][3] == 0x89);
+			EATEST_VERIFY((uint8_t)v.str8_[1][4] == 0xA0);
+			EATEST_VERIFY((uint8_t)v.str8_[1][5] == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("a \x00A9\x2260"), EA_CHAR32("%hs %hs"), v.str8_[0], v.str8_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY((uint8_t)v.str8_[0][0] == 'a');
+			EATEST_VERIFY((uint8_t)v.str8_[0][1] == 0);
+			EATEST_VERIFY((uint8_t)v.str8_[1][0] == 0xC2);
+			EATEST_VERIFY((uint8_t)v.str8_[1][1] == 0xA9);
+			EATEST_VERIFY((uint8_t)v.str8_[1][2] == 0xE2);
+			EATEST_VERIFY((uint8_t)v.str8_[1][3] == 0x89);
+			EATEST_VERIFY((uint8_t)v.str8_[1][4] == 0xA0);
+			EATEST_VERIFY((uint8_t)v.str8_[1][5] == 0);
+		}
+	}
+
+
+	// The following are examples taken from some EA code.
+	{
+		{
+			v.Clear();
+			n = Sscanf("12a", "%ld%c", &v.long_[0], &v.char_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.long_[0] == 12);
+			EATEST_VERIFY(v.char_[0] == 'a');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("12a"), EA_CHAR16("%ld%c"), &v.long_[0], &v.wchar_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.long_[0] == 12);
+			EATEST_VERIFY(v.wchar_[0] == 'a');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("12a"), EA_CHAR32("%ld%c"), &v.long_[0], &v.wchar_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.long_[0] == 12);
+			EATEST_VERIFY(v.wchar_[0] == 'a');
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("#define width 640 #define height 480", "#define %*s %d #define %*s %d", &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 640);
+			EATEST_VERIFY(v.int_[1] == 480);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("#define width 640 #define height 480"), EA_CHAR16("#define %*s %d #define %*s %d"), &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 640);
+			EATEST_VERIFY(v.int_[1] == 480);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("#define width 640 #define height 480"), EA_CHAR32("#define %*s %d #define %*s %d"), &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 640);
+			EATEST_VERIFY(v.int_[1] == 480);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("00010101", "%04d%02d%02d", &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("00010101"), EA_CHAR16("%04d%02d%02d"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("00010101"), EA_CHAR32("%04d%02d%02d"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0xfafbfcfd", "%lx", &v.long_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uint32_t)v.long_[0] == 0xfafbfcfd);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0xfafbfcfd"), EA_CHAR16("%lx"), &v.long_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uint32_t)v.long_[0] == 0xfafbfcfd);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0xfafbfcfd"), EA_CHAR32("%lx"), &v.long_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uint32_t)v.long_[0] == 0xfafbfcfd);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("127.255.3.0", "%u.%u.%u.%u", &v.uint_[0], &v.uint_[1], &v.uint_[2], &v.uint_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.uint_[0] == 127);
+			EATEST_VERIFY(v.uint_[1] == 255);
+			EATEST_VERIFY(v.uint_[2] ==   3);
+			EATEST_VERIFY(v.uint_[3] ==   0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("127.255.3.0"), EA_CHAR16("%u.%u.%u.%u"), &v.uint_[0], &v.uint_[1], &v.uint_[2], &v.uint_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.uint_[0] == 127);
+			EATEST_VERIFY(v.uint_[1] == 255);
+			EATEST_VERIFY(v.uint_[2] ==   3);
+			EATEST_VERIFY(v.uint_[3] ==   0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("127.255.3.0"), EA_CHAR32("%u.%u.%u.%u"), &v.uint_[0], &v.uint_[1], &v.uint_[2], &v.uint_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.uint_[0] == 127);
+			EATEST_VERIFY(v.uint_[1] == 255);
+			EATEST_VERIFY(v.uint_[2] ==   3);
+			EATEST_VERIFY(v.uint_[3] ==   0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0.255.3.127", "%d.%d.%d.%d", &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] ==   0);
+			EATEST_VERIFY(v.int_[1] == 255);
+			EATEST_VERIFY(v.int_[2] ==   3);
+			EATEST_VERIFY(v.int_[3] == 127);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0.255.3.127"), EA_CHAR16("%d.%d.%d.%d"), &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] ==   0);
+			EATEST_VERIFY(v.int_[1] == 255);
+			EATEST_VERIFY(v.int_[2] ==   3);
+			EATEST_VERIFY(v.int_[3] == 127);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0.255.3.127"), EA_CHAR32("%d.%d.%d.%d"), &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] ==   0);
+			EATEST_VERIFY(v.int_[1] == 255);
+			EATEST_VERIFY(v.int_[2] ==   3);
+			EATEST_VERIFY(v.int_[3] == 127);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("ff12ee", "%2x%2x%2x", &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 0xff);
+			EATEST_VERIFY(v.int_[1] == 0x12);
+			EATEST_VERIFY(v.int_[2] == 0xee);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("ff12ee"), EA_CHAR16("%2x%2x%2x"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 0xff);
+			EATEST_VERIFY(v.int_[1] == 0x12);
+			EATEST_VERIFY(v.int_[2] == 0xee);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("ff12ee"), EA_CHAR32("%2x%2x%2x"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.int_[0] == 0xff);
+			EATEST_VERIFY(v.int_[1] == 0x12);
+			EATEST_VERIFY(v.int_[2] == 0xee);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("f2e", "%1hhx%1hhx%1hhx", &v.uint8_[0], &v.uint8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.uint8_[0] == 0xf);
+			EATEST_VERIFY(v.uint8_[1] == 0x2);
+			EATEST_VERIFY(v.uint8_[2] == 0xe);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("f2e"), EA_CHAR16("%1hhx%1hhx%1hhx"), &v.uint8_[0], &v.uint8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.uint8_[0] == 0xf);
+			EATEST_VERIFY(v.uint8_[1] == 0x2);
+			EATEST_VERIFY(v.uint8_[2] == 0xe);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("f2e"), EA_CHAR32("%1hhx%1hhx%1hhx"), &v.uint8_[0], &v.uint8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(v.uint8_[0] == 0xf);
+			EATEST_VERIFY(v.uint8_[1] == 0x2);
+			EATEST_VERIFY(v.uint8_[2] == 0xe);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("Test/123.4 567", "%4s/%5f %3d", v.str8_[0], &v.float_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(!Strcmp(v.str8_[0], "Test"));
+			EATEST_VERIFY(FloatEqual(v.float_[0], 123.4f));
+			EATEST_VERIFY(v.int_[0] == 567);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("Test/123.4 567"), EA_CHAR16("%4I16s/%5f %3d"), v.str16_[0], &v.float_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(!Strcmp(v.str16_[0], EA_CHAR16("Test")));
+			EATEST_VERIFY(FloatEqual(v.float_[0], 123.4f));
+			EATEST_VERIFY(v.int_[0] == 567);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("Test/123.4 567"), EA_CHAR32("%4I32s/%5f %3d"), v.str32_[0], &v.float_[0], &v.int_[0]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY(!Strcmp(v.str32_[0], EA_CHAR32("Test")));
+			EATEST_VERIFY(FloatEqual(v.float_[0], 123.4f));
+			EATEST_VERIFY(v.int_[0] == 567);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("abdefg-hijk-a-mnopqrstu\n", "%32[^-]-%32[^-]-%32[^-]-%32[^\n\r]", v.str8_[0], v.str8_[1], v.str8_[2], v.str8_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(!Strcmp(v.str8_[0], "abdefg"));
+			EATEST_VERIFY(!Strcmp(v.str8_[1], "hijk"));
+			EATEST_VERIFY(!Strcmp(v.str8_[2], "a"));
+			EATEST_VERIFY(!Strcmp(v.str8_[3], "mnopqrstu"));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abdefg-hijk-a-mnopqrstu\n"), EA_CHAR16("%32I16[^-]-%32I16[^-]-%32I16[^-]-%32I16[^\n\r]"), v.str16_[0], v.str16_[1], v.str16_[2], v.str16_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(!Strcmp(v.str16_[0], EA_CHAR16("abdefg")));
+			EATEST_VERIFY(!Strcmp(v.str16_[1], EA_CHAR16("hijk")));
+			EATEST_VERIFY(!Strcmp(v.str16_[2], EA_CHAR16("a")));
+			EATEST_VERIFY(!Strcmp(v.str16_[3], EA_CHAR16("mnopqrstu")));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abdefg-hijk-a-mnopqrstu\n"), EA_CHAR32("%32I32[^-]-%32I32[^-]-%32I32[^-]-%32I32[^\n\r]"), v.str32_[0], v.str32_[1], v.str32_[2], v.str32_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(!Strcmp(v.str32_[0], EA_CHAR32("abdefg")));
+			EATEST_VERIFY(!Strcmp(v.str32_[1], EA_CHAR32("hijk")));
+			EATEST_VERIFY(!Strcmp(v.str32_[2], EA_CHAR32("a")));
+			EATEST_VERIFY(!Strcmp(v.str32_[3], EA_CHAR32("mnopqrstu")));
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("[17;-18R", "[%hu;%huR", &v.short_[0], &v.short_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.short_[0] == 17);
+			EATEST_VERIFY(v.short_[1] == -18);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("[17;-18R"), EA_CHAR16("[%hu;%huR"), &v.short_[0], &v.short_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.short_[0] == 17);
+			EATEST_VERIFY(v.short_[1] == -18);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("[17;-18R"), EA_CHAR32("[%hu;%huR"), &v.short_[0], &v.short_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.short_[0] == 17);
+			EATEST_VERIFY(v.short_[1] == -18);
+		}
+	
+		{
+			v.Clear();
+			n = Sscanf("0x01-0x02-0304", "%08x-%04x-%02x%02x", &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] == 0x01);
+			EATEST_VERIFY(v.int_[1] == 0x02);
+			EATEST_VERIFY(v.int_[2] == 0x03);
+			EATEST_VERIFY(v.int_[3] == 0x04);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0x01-0x02-0304"), EA_CHAR16("%08x-%04x-%02x%02x"), &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] == 0x01);
+			EATEST_VERIFY(v.int_[1] == 0x02);
+			EATEST_VERIFY(v.int_[2] == 0x03);
+			EATEST_VERIFY(v.int_[3] == 0x04);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0x01-0x02-0304"), EA_CHAR32("%08x-%04x-%02x%02x"), &v.int_[0], &v.int_[1], &v.int_[2], &v.int_[3]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(v.int_[0] == 0x01);
+			EATEST_VERIFY(v.int_[1] == 0x02);
+			EATEST_VERIFY(v.int_[2] == 0x03);
+			EATEST_VERIFY(v.int_[3] == 0x04);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-3.14 .2 48 1a", "%f %f %x %x", &v.float_[0], &v.float_[1], &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -3.14f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], .2f));
+			EATEST_VERIFY(v.int_[0] == 0x48);
+			EATEST_VERIFY(v.int_[1] == 0x1a);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-3.14 .2 48 1a"), EA_CHAR16("%f %f %x %x"), &v.float_[0], &v.float_[1], &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -3.14f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], .2f));
+			EATEST_VERIFY(v.int_[0] == 0x48);
+			EATEST_VERIFY(v.int_[1] == 0x1a);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-3.14 .2 48 1a"), EA_CHAR32("%f %f %x %x"), &v.float_[0], &v.float_[1], &v.int_[0], &v.int_[1]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(FloatEqual(v.float_[0], -3.14f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], .2f));
+			EATEST_VERIFY(v.int_[0] == 0x48);
+			EATEST_VERIFY(v.int_[1] == 0x1a);
+		}
+
+		{   // Regression for %x '_' bug.
+			{
+				v.Clear();
+				n = EA::StdC::Sscanf("_s", "%x_%c", &v.int_[0], &v.char_[1]);
+				EATEST_VERIFY(n == 0);
+				EATEST_VERIFY(v.int_[0] == -1);
+				EATEST_VERIFY(v.char_[1] == -1);
+
+				v.Clear();
+				n = EA::StdC::Sscanf("1a01_", "%x_%c", &v.int_[0], &v.char_[1]);  // EAStdC was mistakenly reading this as 0x1a018 instead of 0x1a01
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY(v.char_[1] == -1);
+
+				v.Clear();
+				n = EA::StdC::Sscanf("1a01_s", "%x_%c", &v.int_[0], &v.char_[1]);
+				EATEST_VERIFY(n == 2);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY(v.char_[1] == 's');
+			}
+
+			{
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR16("_s"), EA_CHAR16("%x_%I16c"), &v.int_[0], &v.char16_[1]);
+				EATEST_VERIFY(n == 0);
+				EATEST_VERIFY(v.int_[0] == -1);
+				EATEST_VERIFY((uint16_t)v.char16_[1] == 0xffff);
+
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR16("1a01_"), EA_CHAR16("%x_%I16c"), &v.int_[0], &v.char16_[1]);  // EAStdC was mistakenly reading this as 0x1a018 instead of 0x1a01
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY((uint16_t)v.char16_[1] == 0xffff);
+
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR16("1a01_s"), EA_CHAR16("%x_%I16c"), &v.int_[0], &v.char16_[1]);
+				EATEST_VERIFY(n == 2);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY(v.char16_[1] == 's');
+			}
+
+			{
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR32("_s"), EA_CHAR32("%x_%I32c"), &v.int_[0], &v.char32_[1]);
+				EATEST_VERIFY(n == 0);
+				EATEST_VERIFY(v.int_[0] == -1);
+				EATEST_VERIFY((uint32_t)v.char32_[1] == 0xffffffff);
+
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR32("1a01_"), EA_CHAR32("%x_%I32c"), &v.int_[0], &v.char32_[1]);  // EAStdC was mistakenly reading this as 0x1a018 instead of 0x1a01
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY((uint32_t)v.char32_[1] == 0xffffffff);
+
+				v.Clear();
+				n = EA::StdC::Sscanf(EA_CHAR32("1a01_s"), EA_CHAR32("%x_%I32c"), &v.int_[0], &v.char32_[1]);
+				EATEST_VERIFY(n == 2);
+				EATEST_VERIFY(v.int_[0] == 0x1a01);
+				EATEST_VERIFY(v.char32_[1] == 's');
+			}
+		}
+
+		{
+			v.Clear();
+			n = Sscanf(" for (int x = 0; x < 17; ++x)", " for %*[^(](%n%*[^)]%n%c\n", &v.int_[0], &v.int_[1], &v.char8_[0]);
+			EATEST_VERIFY(n == 1);  // %n doesn't count towards the return value, so the expected result is 1 and not 3.
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(v.int_[1] == 28);
+			EATEST_VERIFY(v.char8_[0] == ')');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16(" for (int x = 0; x < 17; ++x)"), EA_CHAR16(" for %*[^(](%n%*[^)]%n%I16c\n"), &v.int_[0], &v.int_[1], &v.char16_[0]);
+			EATEST_VERIFY(n == 1);  // %n doesn't count towards the return value, so the expected result is 1 and not 3.
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(v.int_[1] == 28);
+			EATEST_VERIFY(v.char16_[0] == ')');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32(" for (int x = 0; x < 17; ++x)"), EA_CHAR32(" for %*[^(](%n%*[^)]%n%I32c\n"), &v.int_[0], &v.int_[1], &v.char32_[0]);
+			EATEST_VERIFY(n == 1);  // %n doesn't count towards the return value, so the expected result is 1 and not 3.
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(v.int_[1] == 28);
+			EATEST_VERIFY(v.char32_[0] == ')');
+		}
+
+		// To do: Implement some of these:
+		/*
+		v.Clear();
+		//n = Sscanf(mpStreamPosition, " %p %u %d", &pResult, &nSize, &nAllocationFlags);
+
+		v.Clear();
+		//n = Sscanf(line.c_str(), "IMADDI %[^,], %[^,], %[^,], %d", register1, register2, register3, &immediate1)
+
+		v.Clear();
+		//n = Sscanf(buffer, "%s %[A-Za-z0-9][%d][%d] %s %s", currentState->stateName,currentState->elementType, &currentState->numColumns, &currentState->numRows,currentState->uiType, currentState->globalStateType);
+
+		v.Clear();
+		//n = Sscanf(argv[i], "-i%x", &id);
+
+		v.Clear();
+		//n = Sscanf(argv[i], "-p%s", prefix);
+
+		v.Clear();
+		//n = Sscanf(argv[i], "-o%s", outputPath);
+
+		v.Clear();
+		//n = Sscanf(argv[i], "-h%s", stateFile);
+
+		v.Clear();
+		//n = Sscanf(argv[i], "-r%s", registerFile);
+
+		v.Clear();
+		//n = Sscanf(fp,"%128s",effectName)
+
+		v.Clear();
+		//n = Sscanf(linebuf.c_str(), "%x:%x %xH %*s %31s", &g.mSegment, &g.mOffset, &g.mLength, className))
+
+		v.Clear();
+		//n = Sscanf(linebuf.c_str(), "%x:%x %s %x %*c %s", &seg, &offset, symbolMangled, &rva, source))
+
+		v.Clear();
+		//n = Sscanf(&linebuf[9], " load address is %x", &address))
+
+		v.Clear();
+		//n = Sscanf(dfile,"%[^\n\r]", packageName);
+
+		v.Clear();
+		//n = Sscanf("127 -127 177 255 ff", "%"SCNd8 " %"SCNi8 " %"SCNo8 " %"SCNu8 " %"SCNx8, &d8, &i8, &o8, &u8, &x8);
+
+		v.Clear();
+		//n = Sscanf("32767 -32768 77777 65535 ffff", "%"SCNd16 " %"SCNi16 " %"SCNo16 " %"SCNu16 " %"SCNx16, &d16, &i16, &o16, &u16, &x16);
+
+		v.Clear();
+		//n = Sscanf("2147483647 -2147483648 17777777777 4294967295 ffffffff", "%"SCNd32 " %"SCNi32 " %"SCNo32 " %"SCNu32 " %"SCNx32, &d32, &i32, &o32, &u32, &x32);
+
+		v.Clear();
+		//n = Sscanf("9223372036854775807 -9223372036854775808 777777777777777777777 18446744073709551615 ffffffffffffffff", "%"SCNd64 " %"SCNi64 " %"SCNo64 " %"SCNu64 " %"SCNx64, &d64, &i64, &o64, &u64, &x64);
+
+		v.Clear();
+		//n = Sscanf("2147483647 -2147483648 17777777777 4294967295 ffffffff", "%"SCNdPTR " %"SCNiPTR " %"SCNoPTR " %"SCNuPTR " %"SCNxPTR, &dPtr, &iPtr, &oPtr, &uPtr, &xPtr);
+
+		v.Clear();
+		//n = Sscanf("2147483647 -2147483648 17777777777 18446744073709551615 ffffffffffffffff", "%"SCNdPTR " %"SCNiPTR " %"SCNoPTR " %"SCNuPTR " %"SCNxPTR, &dPtr, &iPtr, &oPtr, &uPtr, &xPtr);
+
+		v.Clear();
+		//n = Sscanf( sValue, EA_CHAR16( "%g,%g,%g,%g" ), &rf.mLeft, &rf.mTop, &rf.mRight, &rf.mBottom ) != 4)
+
+		v.Clear();
+		//n = Sscanf(sctheader+4,"%14lf%14lf%12lf%12lf",&faspectx,&faspecty,&fheight,&fwidth);
+		*/
+	}
+
+	{
+		{
+			v.Clear();
+			n = Sscanf("%", "%%");
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("%"), EA_CHAR16("%%"));
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("%"), EA_CHAR32("%%"));
+			EATEST_VERIFY(n == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0 1 2 3 4 5 6 7 8 9 10 11 a 13 14", "%*d%*i%*o%*u%*x%*X%*e%*E%*f%*g%*G%*s%*[abc]%*c%*p");
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0 1 2 3 4 5 6 7 8 9 10 11 a 13 14"), EA_CHAR16("%*d%*i%*o%*u%*x%*X%*e%*E%*f%*g%*G%*s%*[abc]%*c%*p"));
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0 1 2 3 4 5 6 7 8 9 10 11 a 13 14"), EA_CHAR32("%*d%*i%*o%*u%*x%*X%*e%*E%*f%*g%*G%*s%*[abc]%*c%*p"));
+			EATEST_VERIFY(n == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("123", "%0d", &v.int_[0]);      // We should just ignore the field, as if it was %*d.
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("123"), EA_CHAR16("%0d"), &v.int_[0]);      // We should just ignore the field, as if it was %*d.
+			EATEST_VERIFY(n == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("123"), EA_CHAR32("%0d"), &v.int_[0]);      // We should just ignore the field, as if it was %*d.
+			EATEST_VERIFY(n == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0", "%e %E %f %F %g %G %f %f", &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(v.float_[0] == -0.f);
+			EATEST_VERIFY(v.float_[1] == -0.f);
+			EATEST_VERIFY(v.float_[2] == -0.f);
+			EATEST_VERIFY(v.float_[3] == -0.f);
+			EATEST_VERIFY(v.float_[4] == -0.f);
+			EATEST_VERIFY(v.float_[5] == -0.f);
+			EATEST_VERIFY(v.float_[6] == -0.f);
+			EATEST_VERIFY(v.float_[7] == -0.f);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0"), EA_CHAR16("%e %E %f %F %g %G %f %f"), &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(v.float_[0] == -0.f);
+			EATEST_VERIFY(v.float_[1] == -0.f);
+			EATEST_VERIFY(v.float_[2] == -0.f);
+			EATEST_VERIFY(v.float_[3] == -0.f);
+			EATEST_VERIFY(v.float_[4] == -0.f);
+			EATEST_VERIFY(v.float_[5] == -0.f);
+			EATEST_VERIFY(v.float_[6] == -0.f);
+			EATEST_VERIFY(v.float_[7] == -0.f);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0"), EA_CHAR32("%e %E %f %F %g %G %f %f"), &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(v.float_[0] == -0.f);
+			EATEST_VERIFY(v.float_[1] == -0.f);
+			EATEST_VERIFY(v.float_[2] == -0.f);
+			EATEST_VERIFY(v.float_[3] == -0.f);
+			EATEST_VERIFY(v.float_[4] == -0.f);
+			EATEST_VERIFY(v.float_[5] == -0.f);
+			EATEST_VERIFY(v.float_[6] == -0.f);
+			EATEST_VERIFY(v.float_[7] == -0.f);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("inF -inf nan -Nan INF -inf nan -NaN", "%e %E %f %F %g %G %f %f", &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(IsInfinite(v.float_[0]));
+			EATEST_VERIFY(IsInfinite(v.float_[1]));
+			EATEST_VERIFY(IsNAN(v.float_[2]));
+			EATEST_VERIFY(IsNAN(v.float_[3]));
+			EATEST_VERIFY(IsInfinite(v.float_[4]));
+			EATEST_VERIFY(IsInfinite(v.float_[5]));
+			EATEST_VERIFY(IsNAN(v.float_[6]));
+			EATEST_VERIFY(IsNAN(v.float_[7]));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("inF -inf nan -Nan INF -inf nan -NaN"), EA_CHAR16("%e %E %f %F %g %G %f %f"), &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(IsInfinite(v.float_[0]));
+			EATEST_VERIFY(IsInfinite(v.float_[1]));
+			EATEST_VERIFY(IsNAN(v.float_[2]));
+			EATEST_VERIFY(IsNAN(v.float_[3]));
+			EATEST_VERIFY(IsInfinite(v.float_[4]));
+			EATEST_VERIFY(IsInfinite(v.float_[5]));
+			EATEST_VERIFY(IsNAN(v.float_[6]));
+			EATEST_VERIFY(IsNAN(v.float_[7]));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("inF -inf nan -Nan INF -inf nan -NaN"), EA_CHAR32("%e %E %f %F %g %G %f %f"), &v.float_[0], &v.float_[1], &v.float_[2], &v.float_[3], &v.float_[4], &v.float_[5], &v.float_[6], &v.float_[7]);
+			EATEST_VERIFY(n == 8);
+			EATEST_VERIFY(IsInfinite(v.float_[0]));
+			EATEST_VERIFY(IsInfinite(v.float_[1]));
+			EATEST_VERIFY(IsNAN(v.float_[2]));
+			EATEST_VERIFY(IsNAN(v.float_[3]));
+			EATEST_VERIFY(IsInfinite(v.float_[4]));
+			EATEST_VERIFY(IsInfinite(v.float_[5]));
+			EATEST_VERIFY(IsNAN(v.float_[6]));
+			EATEST_VERIFY(IsNAN(v.float_[7]));
+		}
+
+		// To do: Implement some of these:
+		//n = Sscanf8_("%d", ip);
+		//n = Sscanf8_("%*d");
+		//n = Sscanf8_("%3d", ip);
+		//n = Sscanf8_("%hd", hp);
+		//n = Sscanf8_("%3ld", lp);
+		//n = Sscanf8_("%*3d");
+		//n = Sscanf8_("%d %ld", ip, lp);
+		//n = Sscanf8_("%d%i%o%u%x%X%a%A%e%E%f%F%g%G%s%[abc]%c%p%n%%", ip, ip, uip, uip, uip,  uip, fp, fp, fp, fp, fp, fp, fp, fp, s, s, s, pp, n);
+		//n = Sscanf8_("%*d%*i%*o%*u%*x%*X%*e%*E%*f%*g%*G%*s%*[abc]%*c%*p");
+		//n = Sscanf8_("%*2d%*8s%*3c");
+		//n = Sscanf8_("%*n", n);         // "suppress" "suppression of %n"
+		//n = Sscanf8_("%*hd");           // "together" "suppression with length"
+		//n = Sscanf8_("%le%lE%lf%lg%lG", dp, dp, dp, dp, dp);
+		//n = Sscanf8_("%Le%LE%Lf%Lg%LG", ldp, ldp, ldp, ldp, ldp);
+		//n = Sscanf8_("%d%i%o%u%x%X%e%E%f%g%G%s%[abc]%c%p%n%%", ip, ip, uip, uip, uip, uip, fp, fp, fp, fp, fp, s, s, s, pp, n);
+		//n = Sscanf8_("%2s%3s%4c%5c%6[abc]%7[abc]", ss, us, ss, us, ss, us);
+		//n = Sscanf8_("%[%d]%d", s, ip);       // This is valid. It searches for % and d.
+		//n = Sscanf8_("%[^%d]%d", s, ip);
+		//n = Sscanf8_("%[]%d]%d", s, ip);
+		//n = Sscanf8_("%[^]%d]%d", s, ip);
+		//n = Sscanf8_("%hhd", hhp);
+		//n = Sscanf8_("%lld", llp);
+		//n = Sscanf8_("%jd", jp);
+		//n = Sscanf8_("%zu", zp);
+		//n = Sscanf8_("%td", tp);
+		//n = Sscanf8_("%F", fp);
+		//n = Sscanf8_("%a", fp);
+		//n = Sscanf8_("%A", fp);
+		//n = Sscanf8_("%lc%ls%l[abc]", ls, ls, ls);
+		//n = Sscanf8_("%*d%*i%*o%*u%*x%*X%*a%*A%*e%*E%*f%*F%*g%*G%*s%*[abc]%*c%*p");
+		//n = Sscanf8_("%*2d%*8s%*3c"); 
+		//n = Sscanf8_("%*n", n);       // We should leave the n parameter as-is.
+		//n = Sscanf8_("%*hd");         // We should ignore the h.
+		//n = Sscanf8_("%2d%3i%4o%5u%6x%7X%8a%9A%10e%11E%12f%13F%14g%15G%16s%3[abc]%4c%5p", ip, ip, uip, uip, uip, uip, fp, fp, fp, fp, fp, fp, fp, fp, s, s, s, pp);
+		//n = Sscanf8_("%0d", ip);      // We should just ignore the field, as if it was %*d.
+		//n = Sscanf8_("%3n", n);       // We should just ignore the 3. Possibly assert about it.
+
+		// Modifiers
+		{
+			v.Clear();
+			n = Sscanf("0 1 012 55555 0x4ff 0x5FF x", "%hd%hi%ho%hu%hx%hX%hn", &v.short_[0], &v.short_[1], &v.short_[2], &v.ushort_[3], &v.short_[4], &v.short_[5], &v.short_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.short_[0] == 0);
+			EATEST_VERIFY(v.short_[1] == 1);
+			EATEST_VERIFY(v.short_[2] == 012);
+			EATEST_VERIFY(v.ushort_[3] == 55555);
+			EATEST_VERIFY(v.short_[4] == 0x4ff);
+			EATEST_VERIFY(v.short_[5] == 0x5FF);
+			EATEST_VERIFY(v.short_[6] == 25);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0 1 012 55555 0x4ff 0x5FF x"), EA_CHAR16("%hd%hi%ho%hu%hx%hX%hn"), &v.short_[0], &v.short_[1], &v.short_[2], &v.ushort_[3], &v.short_[4], &v.short_[5], &v.short_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.short_[0] == 0);
+			EATEST_VERIFY(v.short_[1] == 1);
+			EATEST_VERIFY(v.short_[2] == 012);
+			EATEST_VERIFY(v.ushort_[3] == 55555);
+			EATEST_VERIFY(v.short_[4] == 0x4ff);
+			EATEST_VERIFY(v.short_[5] == 0x5FF);
+			EATEST_VERIFY(v.short_[6] == 25);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0 1 012 55555 0x4ff 0x5FF x"), EA_CHAR32("%hd%hi%ho%hu%hx%hX%hn"), &v.short_[0], &v.short_[1], &v.short_[2], &v.ushort_[3], &v.short_[4], &v.short_[5], &v.short_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.short_[0] == 0);
+			EATEST_VERIFY(v.short_[1] == 1);
+			EATEST_VERIFY(v.short_[2] == 012);
+			EATEST_VERIFY(v.ushort_[3] == 55555);
+			EATEST_VERIFY(v.short_[4] == 0x4ff);
+			EATEST_VERIFY(v.short_[5] == 0x5FF);
+			EATEST_VERIFY(v.short_[6] == 25);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0 1 2 3 4 5 x", "%hhd%hhi%hho%hhu%hhx%hhX%hhn", &v.int8_[0], &v.int8_[1], &v.int8_[2], &v.int8_[3], &v.int8_[4], &v.int8_[5], &v.int8_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.int8_[0] == 0);
+			EATEST_VERIFY(v.int8_[1] == 1);
+			EATEST_VERIFY(v.int8_[2] == 2);
+			EATEST_VERIFY(v.int8_[3] == 3);
+			EATEST_VERIFY(v.int8_[4] == 4);
+			EATEST_VERIFY(v.int8_[5] == 5);
+			EATEST_VERIFY(v.int8_[6] == 11);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0 1 2 3 4 5 x"), EA_CHAR16("%hhd%hhi%hho%hhu%hhx%hhX%hhn"), &v.int8_[0], &v.int8_[1], &v.int8_[2], &v.int8_[3], &v.int8_[4], &v.int8_[5], &v.int8_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.int8_[0] == 0);
+			EATEST_VERIFY(v.int8_[1] == 1);
+			EATEST_VERIFY(v.int8_[2] == 2);
+			EATEST_VERIFY(v.int8_[3] == 3);
+			EATEST_VERIFY(v.int8_[4] == 4);
+			EATEST_VERIFY(v.int8_[5] == 5);
+			EATEST_VERIFY(v.int8_[6] == 11);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0 1 2 3 4 5 x"), EA_CHAR32("%hhd%hhi%hho%hhu%hhx%hhX%hhn"), &v.int8_[0], &v.int8_[1], &v.int8_[2], &v.int8_[3], &v.int8_[4], &v.int8_[5], &v.int8_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.int8_[0] == 0);
+			EATEST_VERIFY(v.int8_[1] == 1);
+			EATEST_VERIFY(v.int8_[2] == 2);
+			EATEST_VERIFY(v.int8_[3] == 3);
+			EATEST_VERIFY(v.int8_[4] == 4);
+			EATEST_VERIFY(v.int8_[5] == 5);
+			EATEST_VERIFY(v.int8_[6] == 11);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0 1 2 3 4 5 z", "%ld%li%lo%lu%lx%lX%ln", &v.long_[0], &v.long_[1], &v.long_[2], &v.long_[3], &v.long_[4], &v.long_[5], &v.long_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.long_[0] == 0);
+			EATEST_VERIFY(v.long_[1] == 1);
+			EATEST_VERIFY(v.long_[2] == 2);
+			EATEST_VERIFY(v.long_[3] == 3);
+			EATEST_VERIFY(v.long_[4] == 4);
+			EATEST_VERIFY(v.long_[5] == 5);
+			EATEST_VERIFY(v.long_[6] == 11);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0 1 2 3 4 5 z"), EA_CHAR16("%ld%li%lo%lu%lx%lX%ln"), &v.long_[0], &v.long_[1], &v.long_[2], &v.long_[3], &v.long_[4], &v.long_[5], &v.long_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.long_[0] == 0);
+			EATEST_VERIFY(v.long_[1] == 1);
+			EATEST_VERIFY(v.long_[2] == 2);
+			EATEST_VERIFY(v.long_[3] == 3);
+			EATEST_VERIFY(v.long_[4] == 4);
+			EATEST_VERIFY(v.long_[5] == 5);
+			EATEST_VERIFY(v.long_[6] == 11);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0 1 2 3 4 5 z"), EA_CHAR32("%ld%li%lo%lu%lx%lX%ln"), &v.long_[0], &v.long_[1], &v.long_[2], &v.long_[3], &v.long_[4], &v.long_[5], &v.long_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.long_[0] == 0);
+			EATEST_VERIFY(v.long_[1] == 1);
+			EATEST_VERIFY(v.long_[2] == 2);
+			EATEST_VERIFY(v.long_[3] == 3);
+			EATEST_VERIFY(v.long_[4] == 4);
+			EATEST_VERIFY(v.long_[5] == 5);
+			EATEST_VERIFY(v.long_[6] == 11);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("1e-1\t 1e-2\t 123.456\t 234.567\t 123.456\t 234.567", "%le %lE %lf %lF %lg %lG", &v.double_[0], &v.double_[1], &v.double_[2], &v.double_[3], &v.double_[4], &v.double_[5]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.double_[0] == 1e-1);
+			EATEST_VERIFY(v.double_[1] == 1e-2);
+			EATEST_VERIFY(DoubleEqual(v.double_[2], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[3], 234.567));
+			EATEST_VERIFY(DoubleEqual(v.double_[4], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[5], 234.567));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("1e-1\t 1e-2\t 123.456\t 234.567\t 123.456\t 234.567"), EA_CHAR16("%le %lE %lf %lF %lg %lG"), &v.double_[0], &v.double_[1], &v.double_[2], &v.double_[3], &v.double_[4], &v.double_[5]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.double_[0] == 1e-1);
+			EATEST_VERIFY(v.double_[1] == 1e-2);
+			EATEST_VERIFY(DoubleEqual(v.double_[2], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[3], 234.567));
+			EATEST_VERIFY(DoubleEqual(v.double_[4], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[5], 234.567));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("1e-1\t 1e-2\t 123.456\t 234.567\t 123.456\t 234.567"), EA_CHAR32("%le %lE %lf %lF %lg %lG"), &v.double_[0], &v.double_[1], &v.double_[2], &v.double_[3], &v.double_[4], &v.double_[5]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.double_[0] == 1e-1);
+			EATEST_VERIFY(v.double_[1] == 1e-2);
+			EATEST_VERIFY(DoubleEqual(v.double_[2], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[3], 234.567));
+			EATEST_VERIFY(DoubleEqual(v.double_[4], 123.456));
+			EATEST_VERIFY(DoubleEqual(v.double_[5], 234.567));
+		}
+
+		{
+			// Test conversion of UTF8 sequences to wide strings.
+			// This works under the Standard C library scanf only if it supports UTF8 and you set it to UTF8.
+			v.Clear();
+			n = Sscanf("a\xed\x9f\xbf_ \xed\x9f\xbf", "%I16s %I32s", v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.str16_[0][0] == 'a');
+			EATEST_VERIFY(v.str16_[0][1] == 0xd7ff);
+			EATEST_VERIFY(v.str16_[0][2] == '_');
+			EATEST_VERIFY(v.str16_[0][3] == 0);
+			EATEST_VERIFY(v.str32_[0][0] == 0xd7ff);
+			EATEST_VERIFY(v.str32_[0][1] == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("a\x1234_ \x5678"), EA_CHAR16("%I16s %I32s"), v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.str16_[0][0] == 'a');
+			EATEST_VERIFY(v.str16_[0][1] == 0x1234);
+			EATEST_VERIFY(v.str16_[0][2] == '_');
+			EATEST_VERIFY(v.str16_[0][3] == 0);
+			EATEST_VERIFY(v.str32_[0][0] == 0x5678);
+			EATEST_VERIFY(v.str32_[0][1] == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("a\x1234_ \x5678"), EA_CHAR32("%I16s %I32s"), v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.str16_[0][0] == 'a');
+			EATEST_VERIFY(v.str16_[0][1] == 0x1234);
+			EATEST_VERIFY(v.str16_[0][2] == '_');
+			EATEST_VERIFY(v.str16_[0][3] == 0);
+			EATEST_VERIFY(v.str32_[0][0] == 0x5678);
+			EATEST_VERIFY(v.str32_[0][1] == 0);
+		}
+
+		{ // Test %[] 
+			v.Clear();
+			n = Sscanf("abcdefghij", "%h[ab]%l[cd]%I16[ef]%I32[gh]", v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abcdefghij"), EA_CHAR16("%h[ab]%l[cd]%I16[ef]%I32[gh]"), v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abcdefghij"), EA_CHAR32("%h[ab]%l[cd]%I16[ef]%I32[gh]"), v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+		}
+
+		{ // Test %[^] 
+			v.Clear();
+			n = Sscanf("abcdefghij", "%h[^cd]%l[^ef]%I16[^gh]%I32[^ij]", v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abcdefghij"), EA_CHAR16("%h[^cd]%l[^ef]%I16[^gh]%I32[^ij]"), v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abcdefghij"), EA_CHAR32("%h[^cd]%l[^ef]%I16[^gh]%I32[^ij]"), v.str8_[0], v.strw_[0], v.str16_[0], v.str32_[0]);
+			EATEST_VERIFY(Strcmp(v.str8_[0],  "ab") == 0);
+			EATEST_VERIFY(Strcmp(v.strw_[0],  EA_WCHAR("cd")) == 0);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("ef")) == 0);
+			EATEST_VERIFY(Strcmp(v.str32_[0], EA_CHAR32("gh")) == 0);
+		}
+
+		{
+			// Test conversion of UTF8 sequences to wide chars.
+			// This works under the Standard C library only if you set it to UTF8.
+			// As of this writing, we don't have support for this in our Scanf, and it is a 'to do' item.
+			//v.Clear();
+			//n = Sscanf("\xed\x9f\xbf\xed\x9f\xbf", "%I16c %I32c", &v.char16_[0], &v.char32_[0]);
+			//EATEST_VERIFY(n == 2);
+			//EATEST_VERIFY(v.char16_[0] == 0xd7ff);
+			//EATEST_VERIFY(v.char32_[0] == 0xd7ff);
+
+			//v.Clear();
+			//n = Sscanf(EA_CHAR16("\xed\x9f\xbf\xed\x9f\xbf"), EA_CHAR16("%I16c %I32c"), &v.char16_[0], &v.char32_[0]);
+			//EATEST_VERIFY(n == 2);
+			//EATEST_VERIFY(v.char16_[0] == 0xd7ff);
+			//EATEST_VERIFY(v.char32_[0] == 0xd7ff);
+
+			//v.Clear();
+			//n = Sscanf(EA_CHAR32("\xed\x9f\xbf\xed\x9f\xbf"), EA_CHAR32("%I16c %I32c"), &v.char16_[0], &v.char32_[0]);
+			//EATEST_VERIFY(n == 2);
+			//EATEST_VERIFY(v.char16_[0] == 0xd7ff);
+			//EATEST_VERIFY(v.char32_[0] == 0xd7ff);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-0 -1 012 3 10 20", "%lld%lli%llo%llu%llx%llX%lln", &v.longlong_[0], &v.longlong_[1], &v.longlong_[2], &v.longlong_[3], &v.longlong_[4], &v.longlong_[5], &v.longlong_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.longlong_[0] == -0);
+			EATEST_VERIFY(v.longlong_[1] == -1);
+			EATEST_VERIFY(v.longlong_[2] == 012);
+			EATEST_VERIFY(v.longlong_[3] == 3);
+			EATEST_VERIFY(v.longlong_[4] == 0x10);
+			EATEST_VERIFY(v.longlong_[5] == 0x20);
+			EATEST_VERIFY(v.longlong_[6] == 17);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-0 -1 012 3 10 20"), EA_CHAR16("%lld%lli%llo%llu%llx%llX%lln"), &v.longlong_[0], &v.longlong_[1], &v.longlong_[2], &v.longlong_[3], &v.longlong_[4], &v.longlong_[5], &v.longlong_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.longlong_[0] == -0);
+			EATEST_VERIFY(v.longlong_[1] == -1);
+			EATEST_VERIFY(v.longlong_[2] == 012);
+			EATEST_VERIFY(v.longlong_[3] == 3);
+			EATEST_VERIFY(v.longlong_[4] == 0x10);
+			EATEST_VERIFY(v.longlong_[5] == 0x20);
+			EATEST_VERIFY(v.longlong_[6] == 17);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-0 -1 012 3 10 20"), EA_CHAR32("%lld%lli%llo%llu%llx%llX%lln"), &v.longlong_[0], &v.longlong_[1], &v.longlong_[2], &v.longlong_[3], &v.longlong_[4], &v.longlong_[5], &v.longlong_[6]);
+			EATEST_VERIFY(n == 6);
+			EATEST_VERIFY(v.longlong_[0] == -0);
+			EATEST_VERIFY(v.longlong_[1] == -1);
+			EATEST_VERIFY(v.longlong_[2] == 012);
+			EATEST_VERIFY(v.longlong_[3] == 3);
+			EATEST_VERIFY(v.longlong_[4] == 0x10);
+			EATEST_VERIFY(v.longlong_[5] == 0x20);
+			EATEST_VERIFY(v.longlong_[6] == 17);
+		}
+
+		{
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf("", "%d", &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16(""), EA_CHAR16("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32(""), EA_CHAR32("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0x519", "%x", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x519);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0x519"), EA_CHAR16("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x519);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0x519"), EA_CHAR32("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x519);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0x51ag", "%x", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x51a);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0x51ag"), EA_CHAR16("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x51a);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0x51ag"), EA_CHAR32("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0x51a);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-1", "%x", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-1"), EA_CHAR16("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-1"), EA_CHAR32("%x"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("\"%12@", "\"%%%d%%", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 12);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("\"%12@"), EA_CHAR16("\"%%%d%%"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 12);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("\"%12@"), EA_CHAR32("\"%%%d%%"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 12);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("1.1\t2.2", "%f\t%f", &v.float_[0], &v.float_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(FloatEqual(v.float_[0], 1.1f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], 2.2f));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("1.1\t2.2"), EA_CHAR16("%f\t%f"), &v.float_[0], &v.float_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(FloatEqual(v.float_[0], 1.1f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], 2.2f));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("1.1\t2.2"), EA_CHAR32("%f\t%f"), &v.float_[0], &v.float_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(FloatEqual(v.float_[0], 1.1f));
+			EATEST_VERIFY(FloatEqual(v.float_[1], 2.2f));
+		}
+
+		{
+			v.Clear(); // Skip one char then read a string that ends with \n (don't write the \n).
+			n = Sscanf("  Hello World\n", "%*c%[^\n]", v.str8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.str8_[0], " Hello World") == 0);
+
+			v.Clear();
+			#if EASCANF_MS_STYLE_S_FORMAT
+				n = Sscanf(EA_CHAR16("  Hello World\n"), EA_CHAR16("%*c%[^\n]"), v.strw_[0]);
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR(" Hello World")) == 0);
+			#else
+				n = Sscanf(EA_CHAR16("  Hello World\n"), EA_CHAR16("%*c%[^\n]"), v.str8_[0]);
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(Strcmp(v.str8_[0], " Hello World") == 0);
+			#endif
+
+			#if EASCANF_MS_STYLE_S_FORMAT
+				n = Sscanf(EA_CHAR32("  Hello World\n"), EA_CHAR32("%*c%[^\n]"), v.strw_[0]);
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR(" Hello World")) == 0);
+			#else
+				n = Sscanf(EA_CHAR32("  Hello World\n"), EA_CHAR32("%*c%[^\n]"), v.str8_[0]);
+				EATEST_VERIFY(n == 1);
+				EATEST_VERIFY(Strcmp(v.str8_[0], " Hello World") == 0);
+			#endif
+		}
+
+		{
+			v.Clear(); // Skip all the chars in the [] range, then read one char.
+			n = Sscanf("abcefgdh", "%*[a-cb-g]%c", &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == 'h');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abcefgdh"), EA_CHAR16("%*[a-cb-g]%I16c"), &v.char16_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char16_[0] == 'h');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abcefgdh"), EA_CHAR32("%*[a-cb-g]%I32c"), &v.char32_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char32_[0] == 'h');
+		}
+
+		{
+			v.Clear(); // Skip all the chars in the [] range (exercizing d-d), then read one char.
+			n = Sscanf("abcefgdh", "%*[a-cd-de-g]%c", &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == 'h');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abcefgdh"), EA_CHAR16("%*[a-cd-de-g]%I16c"), &v.char16_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char16_[0] == 'h');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abcefgdh"), EA_CHAR32("%*[a-cd-de-g]%I32c"), &v.char32_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char32_[0] == 'h');
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("3:5:7", "%d%n:%n", &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 3);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 2);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("3:5:7"), EA_CHAR16("%d%n:%n"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 3);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 2);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("3:5:7"), EA_CHAR32("%d%n:%n"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 3);
+			EATEST_VERIFY(v.int_[1] == 1);
+			EATEST_VERIFY(v.int_[2] == 2);
+		}
+
+		{
+			v.Clear(); // Skip a char, then report how many chars were read so far.
+			n = Sscanf("5:7", "%*c%n", &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("5:7"), EA_CHAR16("%*c%n"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("5:7"), EA_CHAR32("%*c%n"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-1", "%i", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-1"), EA_CHAR16("%i"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-1"), EA_CHAR32("%i"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0xff", "%i", &v.int_[0]);   // %i differs from %d in that it acts like strtol with a base of 0 (auto-detect) as opposed to a base of 10.
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 255);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0xff"), EA_CHAR16("%i"), &v.int_[0]); // %i differs from %d in that it acts like strtol with a base of 0 (auto-detect) as opposed to a base of 10.
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 255);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0xff"), EA_CHAR32("%i"), &v.int_[0]); // %i differs from %d in that it acts like strtol with a base of 0 (auto-detect) as opposed to a base of 10.
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 255);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("017", "%i", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 017);         // See C99 7.20.1.4 p3 and p5, 6.4.4.1
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("017"), EA_CHAR16("%i"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 017);         // See C99 7.20.1.4 p3 and p5, 6.4.4.1
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("017"), EA_CHAR32("%i"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 017);         // See C99 7.20.1.4 p3 and p5, 6.4.4.1
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-1", "%d", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-1"), EA_CHAR16("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-1"), EA_CHAR32("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("0xff", "%d", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0);          // %d should not treat 0x sequences as hexidecimal.
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0xff"), EA_CHAR16("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0);          // %d should not treat 0x sequences as hexidecimal.
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0xff"), EA_CHAR32("%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == 0);          // %d should not treat 0x sequences as hexidecimal.
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-1", "%o", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-1"), EA_CHAR16("%o"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-1"), EA_CHAR32("%o"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("-1", "%u", &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("-1"), EA_CHAR16("%u"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("-1"), EA_CHAR32("%u"), &v.int_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.int_[0] == -1);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("xyzwa", "%hc%lc%I16c%I32c", &v.char8_[0], &v.wchar_[0], &v.char16_[0], &v.char32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.char8_[0] == 'x') && (v.wchar_[0] == 'y') && (v.char16_[0] == 'z') && (v.char32_[0] == 'w'));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("xyzwa"), EA_CHAR16("%hc%lc%I16c%I32c"), &v.char8_[0], &v.wchar_[0], &v.char16_[0], &v.char32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.char8_[0] == 'x') && (v.wchar_[0] == 'y') && (v.char16_[0] == 'z') && (v.char32_[0] == 'w'));
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("xyzwa"), EA_CHAR16("%hc%lc%I16c%I32c"), &v.char8_[0], &v.wchar_[0], &v.char16_[0], &v.char32_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.char8_[0] == 'x') && (v.wchar_[0] == 'y') && (v.char16_[0] == 'z') && (v.char32_[0] == 'w'));
+		}
+
+		{
+			v.Clear();
+			n = Sscanf(" xyz", "%c", &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == ' ');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16(" xyz"), EA_CHAR16("%I16c"), &v.char16_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char16_[0] == ' ');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32(" xyz"), EA_CHAR32("%I32c"), &v.char32_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char32_[0] == ' ');
+		}
+
+		{
+			v.Clear();
+			v.char8_[0] = (char8_t)(uint8_t)0xdd;
+			n = Sscanf("10:11", "%d:%d%c", &v.int_[0], &v.int_[1], &v.char8_[0]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 10);
+			EATEST_VERIFY(v.int_[1] == 11);
+			EATEST_VERIFY(v.char8_[0] == (char8_t)(uint8_t)0xdd);
+
+			v.Clear();
+			#if EASCANF_MS_STYLE_S_FORMAT
+				v.wchar_[0] = 0xdd;
+				n = Sscanf(EA_CHAR16("10:11"), EA_CHAR16("%d:%d%c"), &v.int_[0], &v.int_[1], &v.wchar_[0]);
+				EATEST_VERIFY(v.wchar_[0] == 0xdd);
+			#else
+				v.char8_[0] = (char8_t)(uint8_t)0xdd;
+				n = Sscanf(EA_CHAR16("10:11"), EA_CHAR16("%d:%d%c"), &v.int_[0], &v.int_[1], &v.char8_[0]);
+				EATEST_VERIFY(v.char8_[0] == (char8_t)(uint8_t)0xdd);
+			#endif
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 10);
+			EATEST_VERIFY(v.int_[1] == 11);
+
+			v.Clear();
+			#if EASCANF_MS_STYLE_S_FORMAT
+				v.wchar_[0] = 0xdd;
+				n = Sscanf(EA_CHAR32("10:11"), EA_CHAR32("%d:%d%c"), &v.int_[0], &v.int_[1], &v.wchar_[0]);
+				EATEST_VERIFY(v.wchar_[0] == 0xdd);
+			#else
+				v.char8_[0] = (char8_t)(uint8_t)0xdd;
+				n = Sscanf(EA_CHAR32("10:11"), EA_CHAR32("%d:%d%c"), &v.int_[0], &v.int_[1], &v.char8_[0]);
+				EATEST_VERIFY(v.char8_[0] == (char8_t)(uint8_t)0xdd);
+			#endif
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 10);
+			EATEST_VERIFY(v.int_[1] == 11);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("abc   def", "%s %n%s", v.str8_[0], &v.int_[0], v.str8_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "abc") == 0);
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(Strcmp(v.str8_[1], "def") == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("abc   def"), EA_CHAR16("%I16s %n%I32s"), v.str16_[0], &v.int_[0], v.str32_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("abc")) == 0);
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(Strcmp(v.str32_[1], EA_CHAR32("def")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("abc   def"), EA_CHAR32("%I16s %n%I32s"), v.str16_[0], &v.int_[0], v.str32_[1]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(Strcmp(v.str16_[0], EA_CHAR16("abc")) == 0);
+			EATEST_VERIFY(v.int_[0] == 6);
+			EATEST_VERIFY(Strcmp(v.str32_[1], EA_CHAR32("def")) == 0);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("1:23", "%d:%d%n", &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 23);
+			EATEST_VERIFY(v.int_[2] == 4);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("1:23"), EA_CHAR16("%d:%d%n"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 23);
+			EATEST_VERIFY(v.int_[2] == 4);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("1:23"), EA_CHAR32("%d:%d%n"), &v.int_[0], &v.int_[1], &v.int_[2]);
+			EATEST_VERIFY(n == 2);
+			EATEST_VERIFY(v.int_[0] == 1);
+			EATEST_VERIFY(v.int_[1] == 23);
+			EATEST_VERIFY(v.int_[2] == 4);
+		}
+
+		{
+			v.Clear();
+			n = Sscanf("_", "_%n%hn%hhn%ln%lln%I8n%I16n%I32n%I64n", &v.int_[0], &v.short_[0], &v.char_[0], &v.long_[0], &v.longlong_[0], &v.int8_[0], &v.int16_[0], &v.int32_[0], &v.int64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0]      == 1);
+			EATEST_VERIFY(v.short_[0]    == 1);
+			EATEST_VERIFY(v.char_[0]     == 1);
+			EATEST_VERIFY(v.long_[0]     == 1);
+			EATEST_VERIFY(v.longlong_[0] == 1);
+			EATEST_VERIFY(v.int8_[0]     == 1);
+			EATEST_VERIFY(v.int32_[0]    == 1);
+			EATEST_VERIFY(v.int64_[0]    == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("_"), EA_CHAR16("_%n%hn%hhn%ln%lln%I8n%I16n%I32n%I64n"), &v.int_[0], &v.short_[0], &v.char_[0], &v.long_[0], &v.longlong_[0], &v.int8_[0], &v.int16_[0], &v.int32_[0], &v.int64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0]      == 1);
+			EATEST_VERIFY(v.short_[0]    == 1);
+			EATEST_VERIFY(v.char_[0]     == 1);
+			EATEST_VERIFY(v.long_[0]     == 1);
+			EATEST_VERIFY(v.longlong_[0] == 1);
+			EATEST_VERIFY(v.int8_[0]     == 1);
+			EATEST_VERIFY(v.int32_[0]    == 1);
+			EATEST_VERIFY(v.int64_[0]    == 1);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("_"), EA_CHAR32("_%n%hn%hhn%ln%lln%I8n%I32n%I32n%I64n"), &v.int_[0], &v.short_[0], &v.char_[0], &v.long_[0], &v.longlong_[0], &v.int8_[0], &v.int32_[0], &v.int32_[0], &v.int64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0]      == 1);
+			EATEST_VERIFY(v.short_[0]    == 1);
+			EATEST_VERIFY(v.char_[0]     == 1);
+			EATEST_VERIFY(v.long_[0]     == 1);
+			EATEST_VERIFY(v.longlong_[0] == 1);
+			EATEST_VERIFY(v.int8_[0]     == 1);
+			EATEST_VERIFY(v.int32_[0]    == 1);
+			EATEST_VERIFY(v.int64_[0]    == 1);
+		}
+	}
+
+	{
+		// Bug report regression.
+		// What was special about this case was that it represented an exponent of 
+		// the power -10 (wouldn't happen with -9 or -11), and the code was mistakenly 
+		// doing a test for >10 where it needed to do >=10.
+
+		float a, b, c;
+		int count = EA::StdC::Sscanf("0.1797734499 0.1797734499 0.1797734499", "%f %f %f", &a, &b, &c);
+
+		EATEST_VERIFY((count == 3) && FloatEqual(a,  1.79773e-01f));
+	}
+
+	{
+		float a;
+
+		EA::StdC::Sscanf( ".750", "%f", &a);
+		EATEST_VERIFY(FloatEqual(a,  .750f));
+
+		EA::StdC::Sscanf(EA_CHAR16(".750"), EA_CHAR16("%f"), &a);
+		EATEST_VERIFY(FloatEqual(a,  .750f));
+	}
+
+	{
+		// Regresssion for Coverity report that mSigStr[-1] could occur (out of bounds). Actually, mSigStr[-1] was never read though a pointer to it was created.
+		float a = 1.f;
+		int count = EA::StdC::Sscanf(".", "%f", &a);    // This input caused mSigStr to be negatively indexed. 
+		EATEST_VERIFY((count == 0) && (a == 1.f));      // VC++ sscanf returns a count of -1 (EOF), whereas glibc sscanf returns 0. The C11 Standard, section 7.21.6.2, example 3, seems to indicate that 0 is the proper return value.
+	}
+
+	return nErrorCount;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfUnusual
+//
+static int TestScanfUnusual()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{ // Test unusual things
+
+		Values v;
+		int    n;
+
+		{
+			n = TestCRTVsscanf("", "");
+			EATEST_VERIFY(n == 0);
+
+			n = TestCRTVsscanf(EA_CHAR16(""), EA_CHAR16(""));
+			EATEST_VERIFY(n == 0);
+
+			n = TestCRTVsscanf(EA_CHAR32(""), EA_CHAR32(""));
+			EATEST_VERIFY(n == 0);
+		}
+
+		{   
+			// testing to see if the 'j' specifier
+			// The test below is OK for both 32 and 64 bit pointer platforms.
+			v.Clear();
+			n = Sscanf("1111", "%jx", &v.uintmax_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.uintmax_[0] == 0x1111);
+		}
+
+		{
+			// testing to see if the 'z' specifier
+			// The test below is OK for both 32 and 64 bit pointer platforms.
+			v.Clear();
+			n = Sscanf("1111", "%zx", &v.size_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.size_[0] == 0x1111);
+		}
+
+		{
+			// testing to see if the 't' specifier
+			// The test below is OK for both 32 and 64 bit pointer platforms.
+			v.Clear();
+			n = Sscanf("1111", "%tx", &v.ptrdiff_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.ptrdiff_[0] == 0x1111);
+		}
+
+		{   // We should ignore the 'l' in front of 'p'
+			// The test below is OK for both 32 and 64 bit pointer platforms.
+			v.Clear();
+			n = Sscanf("0xffffffff", "%lp", &v.voidptr_[0]);    
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0xffffffff"), EA_CHAR16("%lp"), &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0xffffffff"), EA_CHAR32("%lp"), &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+		}
+
+		{   // We should ignore the 'h' in front of 'p'
+			// The test below is OK for both 32 and 64 bit pointer platforms.
+			v.Clear();
+			n = Sscanf("0xffffffff", "%hp", &v.voidptr_[0]);    
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("0xffffffff"), EA_CHAR16("%hp"), &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("0xffffffff"), EA_CHAR32("%hp"), &v.voidptr_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY((uintptr_t)v.voidptr_[0] == 0xffffffff);
+		}
+
+		{   // Make sure we support explicit char8_t strings.
+			v.Clear();
+			n = Sscanf("2.0", "%hs", v.str8_[0]);    
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "2.0") == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%hs"), v.str8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "2.0") == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%hs"), v.str8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "2.0") == 0);
+		}
+
+		{  // Make sure we support explicit wchar_t strings.
+			v.Clear();
+			n = Sscanf("2.0", "%ls", v.strw_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("2.0")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%ls"), v.strw_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("2.0")) == 0);
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%ls"), v.strw_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(Strcmp(v.strw_[0], EA_WCHAR("2.0")) == 0);
+		}
+
+		{   // Make sure we support explicit char8_t chars.
+			v.Clear();
+			n = Sscanf("2.0", "%hc", &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == '2');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%hc"), &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == '2');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%hc"), &v.char8_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.char8_[0] == '2');
+		}
+
+		{   // Make sure we support explicit char16_t chars.
+			v.Clear();
+			n = Sscanf("2.0", "%lc", &v.wchar_[0]);    
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.wchar_[0] == '2');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%lc"), &v.wchar_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.wchar_[0] == '2');
+
+			v.Clear();
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%lc"), &v.wchar_[0]);
+			EATEST_VERIFY(n == 1);
+			EATEST_VERIFY(v.wchar_[0] == '2');
+		}
+
+		EA::Assert::FailureCallback originalCallback = EA::Assert::GetFailureCallback();
+		EA::Assert::SetFailureCallback([](const char* expr, const char* filename, int line, const char* function, const char* msg, va_list args) -> bool { ++Values::assertCount_; return false; });
+
+		// The following are invalid formats. They will result in assertion failures in Sscanf.
+		{
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf("%4", "%*%%d", &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16("%4"), EA_CHAR16("%*%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32("%4"), EA_CHAR32("%*%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf("%4", "%2%%d", &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16("%4"), EA_CHAR16("%2%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32("%4"), EA_CHAR32("%2%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf("%4", "%h%%d", &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16("%4"), EA_CHAR16("%h%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.int_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32("%4"), EA_CHAR32("%h%%d"), &v.int_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.int_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf("abcd", "%[]abcd", v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf(EA_CHAR16("abcd"), EA_CHAR16("%[]abcd"), v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf(EA_CHAR32("abcd"), EA_CHAR32("%[]abcd"), v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf("2.0", "%hf", &v.float_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%hf"), &v.float_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%hf"), &v.float_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		/*{ Currently our Scanf supports %h in front of string formats, but it's not required to by the Standard. We should disable this support.
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf("2.0", "%h[2.]", v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf(EA_CHAR16("2.0"), EA_CHAR16("%h[2.]"), v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+
+			v.Clear();
+			Strcpy(v.str8_[0], "x");
+			n = Sscanf(EA_CHAR32("2.0"), EA_CHAR32("%h[2.]"), v.str8_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(Strcmp(v.str8_[0], "x") == 0);
+		} */
+
+		{
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf("abcd", "%h", &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16("abcd"), EA_CHAR16("%h"), &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32("abcd"), EA_CHAR32("%h"), &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf("abcd", "%l.", &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf(EA_CHAR16("abcd"), EA_CHAR16("%l."), &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.uint64_[0] = 0xdd;
+			n = Sscanf(EA_CHAR32("abcd"), EA_CHAR32("%l."), &v.uint64_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.uint64_[0] == 0xdd);
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		{
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf("2.0", "%zf", &v.float_[0]);
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf("2.0", "%ze", &v.float_[0]);  //Invalid use of 'z' modifier with 'f' field specifier
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf("2.0", "%zg", &v.float_[0]);  //Invalid use of 'z' modifier with 'g' field specifier
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.float_[0] = -1.f;
+			n = Sscanf("2.0", "%za", &v.float_[0]);  //Invalid use of 'z' modifier with 'a' field specifier
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.float_[0] == -1.f);
+			VERIFY_ASSERTCOUNT(1);
+
+			v.Clear();
+			v.char_[0] = 'x';
+			n = Sscanf("y", "%zc", &v.char_[0]);  //Invalid use of 'z' modifier with 'c' field specifier
+			EATEST_VERIFY(n == 0);
+			EATEST_VERIFY(v.char_[0] == 'x');
+			VERIFY_ASSERTCOUNT(1);
+		}
+
+		EA::Assert::SetFailureCallback(originalCallback);
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfVariants
+//
+static int TestScanfVariants()
+{
+	int nErrorCount = 0;
+
+	{ // Test sscanf family variants.
+
+		// To do: Implement some of these:
+		/*
+		EASTDC_API int Cscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, ...);
+		EASTDC_API int Fscanf(FILE* pFile, const char8_t* pFormat, ...);
+		EASTDC_API int Scanf(const char8_t* pFormat, ...);
+		EASTDC_API int Sscanf(const char8_t*  pTextBuffer, const char8_t* pFormat, ...);
+
+		EASTDC_API int Cscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, ...);
+		EASTDC_API int Fscanf(FILE* pFile, const char16_t* pFormat, ...);
+		EASTDC_API int Scanf(const char16_t* pFormat, ...);
+		EASTDC_API int Sscanf(const char16_t* pTextBuffer, const char16_t* pFormat, ...);
+
+		EASTDC_API int Cscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, ...);
+		EASTDC_API int Fscanf(FILE* pFile, const char32_t* pFormat, ...);
+		EASTDC_API int Scanf(const char32_t* pFormat, ...);
+		EASTDC_API int Sscanf(const char32_t* pTextBuffer, const char32_t* pFormat, ...);
+
+
+		EASTDC_API int Vcscanf(ReadFunction8 pReadFunction8, void* pContext, const char8_t* pFormat, va_list arguments);
+		EASTDC_API int Vfscanf(FILE* pFile, const char8_t* pFormat, va_list arguments);
+		EASTDC_API int Vscanf(const char8_t* pFormat, va_list arguments);
+		EASTDC_API int Vsscanf(const char8_t* pTextBuffer, const char8_t* pFormat, va_list arguments);
+
+		EASTDC_API int Vcscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments);
+		EASTDC_API int Vfscanf(FILE* pFile, const char16_t* pFormat, va_list arguments);
+		EASTDC_API int Vscanf(const char16_t* pFormat, va_list arguments);
+		EASTDC_API int Vsscanf(const char16_t* pTextBuffer, const char16_t* pFormat, va_list arguments);
+
+		EASTDC_API int Vcscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, va_list arguments);
+		EASTDC_API int Vfscanf(FILE* pFile, const char32_t* pFormat, va_list arguments);
+		EASTDC_API int Vscanf(const char32_t* pFormat, va_list arguments);
+		EASTDC_API int Vsscanf(const char32_t* pTextBuffer, const char32_t* pFormat, va_list arguments);
+		*/
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfExtensions
+//
+static int TestScanfExtensions()
+{
+	int nErrorCount = 0;
+
+	{ // Test sscanf extensions
+		using namespace EA::StdC;
+
+		int       n;
+		Values    v;
+
+		{
+			n = Sscanf("127 -128 255", "%I8d %I8i %I8u", &v.int8_[0], &v.int8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int8_[0] == INT8_MAX) && (v.int8_[1] == INT8_MIN) && (v.uint8_[2] == UINT8_MAX));
+
+			n = Sscanf(EA_CHAR16("127 -128 255"), EA_CHAR16("%I8d %I8i %I8u"), &v.int8_[0], &v.int8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int8_[0] == INT8_MAX) && (v.int8_[1] == INT8_MIN) && (v.uint8_[2] == UINT8_MAX));
+
+			n = Sscanf(EA_CHAR32("127 -128 255"), EA_CHAR32("%I8d %I8i %I8u"), &v.int8_[0], &v.int8_[1], &v.uint8_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int8_[0] == INT8_MAX) && (v.int8_[1] == INT8_MIN) && (v.uint8_[2] == UINT8_MAX));
+		}
+
+		{
+			n = Sscanf("32767 -32768 65535", "%I16d %I16i %I16u", &v.int16_[0], &v.int16_[1], &v.uint16_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int16_[0] == INT16_MAX));
+			EATEST_VERIFY((v.int16_[1] == INT16_MIN));
+			EATEST_VERIFY((v.uint16_[2] == UINT16_MAX));
+			// The three lines above were added to replace the line below.  The comparisons are identical, but 
+			// the line below fails on iPhone due to an apparent Clang compiler bug.
+			//EATEST_VERIFY((v.int16_[0] == INT16_MAX) && (v.int16_[1] == INT16_MIN) && (v.uint16_[2] == UINT16_MAX));
+
+			n = Sscanf(EA_CHAR16("32767 -32768 65535"), EA_CHAR16("%I16d %I16i %I16u"), &v.int16_[0], &v.int16_[1], &v.uint16_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int16_[0] == INT16_MAX));
+			EATEST_VERIFY((v.int16_[1] == INT16_MIN));
+			EATEST_VERIFY((v.uint16_[2] == UINT16_MAX));
+			// The three lines above were added to replace the line below.  The comparisons are identical, but 
+			// the line below fails on iPhone due to an apparent Clang compiler bug.
+			//EATEST_VERIFY((v.int16_[0] == INT16_MAX) && (v.int16_[1] == INT16_MIN) && (v.uint16_[2] == UINT16_MAX));
+
+			n = Sscanf(EA_CHAR32("32767 -32768 65535"), EA_CHAR32("%I16d %I16i %I16u"), &v.int16_[0], &v.int16_[1], &v.uint16_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int16_[0] == INT16_MAX));
+			EATEST_VERIFY((v.int16_[1] == INT16_MIN));
+			EATEST_VERIFY((v.uint16_[2] == UINT16_MAX));
+			// The three lines above were added to replace the line below.  The comparisons are identical, but 
+			// the line below fails on iPhone due to an apparent Clang compiler bug.
+			//EATEST_VERIFY((v.int16_[0] == INT16_MAX) && (v.int16_[1] == INT16_MIN) && (v.uint16_[2] == UINT16_MAX));
+		}
+
+		{
+			n = Sscanf("2147483647 -2147483648 4294967295", "%I32d %I32i %I32u", &v.int32_[0], &v.int32_[1], &v.uint32_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int32_[0] == INT32_MAX) && (v.int32_[1] == INT32_MIN) && (v.uint32_[2] == UINT32_MAX));
+
+			n = Sscanf(EA_CHAR16("2147483647 -2147483648 4294967295"), EA_CHAR16("%I32d %I32i %I32u"), &v.int32_[0], &v.int32_[1], &v.uint32_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int32_[0] == INT32_MAX) && (v.int32_[1] == INT32_MIN) && (v.uint32_[2] == UINT32_MAX));
+
+			n = Sscanf(EA_CHAR32("2147483647 -2147483648 4294967295"), EA_CHAR32("%I32d %I32i %I32u"), &v.int32_[0], &v.int32_[1], &v.uint32_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int32_[0] == INT32_MAX) && (v.int32_[1] == INT32_MIN) && (v.uint32_[2] == UINT32_MAX));
+		}
+
+		{
+			n = Sscanf("9223372036854775807 -9223372036854775808 18446744073709551615", "%I64d %I64i %I64u", &v.int64_[0], &v.int64_[1], &v.uint64_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int64_[0] == INT64_MAX) && (v.int64_[1] == INT64_MIN) && (v.uint64_[2] == UINT64_MAX));
+
+			n = Sscanf(EA_CHAR16("9223372036854775807 -9223372036854775808 18446744073709551615"), EA_CHAR16("%I64d %I64i %I64u"), &v.int64_[0], &v.int64_[1], &v.uint64_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int64_[0] == INT64_MAX) && (v.int64_[1] == INT64_MIN) && (v.uint64_[2] == UINT64_MAX));
+
+			n = Sscanf(EA_CHAR32("9223372036854775807 -9223372036854775808 18446744073709551615"), EA_CHAR32("%I64d %I64i %I64u"), &v.int64_[0], &v.int64_[1], &v.uint64_[2]);
+			EATEST_VERIFY(n == 3);
+			EATEST_VERIFY((v.int64_[0] == INT64_MAX) && (v.int64_[1] == INT64_MIN) && (v.uint64_[2] == UINT64_MAX));
+		}
+
+		// We don't currently support %I128, though it could be done if needed. 
+		// If somebody actually asks for this then it can be implemented. 
+		// Otherwise it probably would just bloat the library with something nobody wants.
+		// 
+		// int128_t  int128_[4]; // int128_t has constructors and so is not a POD and cannot be part of the Values union.
+		// uint128_t uint128_[4];
+		// 
+		// n = Sscanf(EA_CHAR16("170141183460469231731687303715884105727 -170141183460469231731687303715884105728 340282366920938463463374607431768211455"), EA_CHAR16("%I128d %I128i %I128u"), &int128_[0], &int128_[1], &uint128_[2]);
+		// EATEST_VERIFY(n == 3);
+		// EATEST_VERIFY((int128_[0]  == int128_t("170141183460469231731687303715884105727")) && 
+		//               (int128_[1]  == int128_t("-170141183460469231731687303715884105728")) && 
+		//               (uint128_[2] == uint128_t("340282366920938463463374607431768211455")));
+
+		{   // Test sized %s
+			// We could test %I8s, %I16s, %I32s, %I8c, %I16c, %I32c here, but we already 
+			// test these a lot in the other code here.
+		}
+
+		{   // Test %b (binary)
+			n = Sscanf("11010111 111011000101011 10110110111101100001110001100111 111010011101100000110100011111100111000011100111110011101110011", "%I8b %I16b %I32b %I64b", &v.uint8_[0], &v.uint16_[0], &v.uint32_[0], &v.uint64_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.uint8_[0] == 0xd7) && (v.uint16_[0] == 0x762B) && (v.uint32_[0] == 0xB6F61C67) && (v.uint64_[0] == UINT64_C(0x74EC1A3F3873E773)));
+
+			n = Sscanf(EA_CHAR16("11010111 111011000101011 10110110111101100001110001100111 111010011101100000110100011111100111000011100111110011101110011"), EA_CHAR16("%I8b %I16b %I32b %I64b"), &v.uint8_[0], &v.uint16_[0], &v.uint32_[0], &v.uint64_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.uint8_[0] == 0xd7) && (v.uint16_[0] == 0x762B) && (v.uint32_[0] == 0xB6F61C67) && (v.uint64_[0] == UINT64_C(0x74EC1A3F3873E773)));
+
+			n = Sscanf(EA_CHAR32("11010111 111011000101011 10110110111101100001110001100111 111010011101100000110100011111100111000011100111110011101110011"), EA_CHAR32("%I8b %I16b %I32b %I64b"), &v.uint8_[0], &v.uint16_[0], &v.uint32_[0], &v.uint64_[0]);
+			EATEST_VERIFY(n == 4);
+			EATEST_VERIFY((v.uint8_[0] == 0xd7) && (v.uint16_[0] == 0x762B) && (v.uint32_[0] == 0xB6F61C67) && (v.uint64_[0] == UINT64_C(0x74EC1A3F3873E773)));
+		}
+
+		{   // Test %Ld (same as %lld)
+			// To do.
+		}
+
+		{   // Test %S handling
+			// To do.
+		}
+
+		{   // Test %C handling
+			// To do.
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanfErrors
+//
+static int TestScanfErrors()
+{
+	int nErrorCount = 0;
+
+	{ // Test sscanf error situations
+		// To do: Implement some of these:
+
+		/*
+		// Wrong number of arguments. 
+		// Not possible to test at runtime.
+		// sscanf("%d%d", ip);
+		// sscanf("%d", ip, ip);
+
+		// Various tests of bad argument types.  Some of these are only pedantic warnings.
+		sscanf("%d", lp);          // "format" "bad argument types"
+		sscanf("%d", uip);         // "format" "bad argument types"
+		sscanf("%d", pp);          // "format" "bad argument types"
+		sscanf("%p", ppc);         // "format" "bad argument types"
+		sscanf("%p", ppv);         // "format" "bad argument types"
+		sscanf("%s", n);           // "format" "bad argument types"
+		sscanf("%s", p);           // "format" "bad argument types"
+		sscanf("%p", p);           // "format" "bad argument types"
+		sscanf("%p", sp);          // "format" "bad argument types"
+
+		// Tests for writing into constant values. 
+		sscanf("%d", cip);         // "constant" "%d writing into const"
+		sscanf("%n", cn);          // "constant" "%n writing into const"
+		sscanf("%s", cs);          // "constant" "%s writing into const"
+		sscanf("%p", pcp);         // "constant" "%p writing into const"
+
+		sscanf("");                // "zero-length" "warning for empty format"
+		sscanf("\0");              // "embedded" "warning for embedded NUL"
+		sscanf("%d\0", ip);        // "embedded" "warning for embedded NUL"
+		sscanf("%d\0%d", ip, ip);  // "embedded|too many" "warning for embedded NUL"
+		sscanf(NULL);              // "null" "null format string warning"
+		sscanf("%");               // "trailing" "trailing % warning"
+		sscanf("%d", (int *)0);    // "null" "writing into NULL"
+
+		sscanf("%lla", fp); // "length" "bad use of %ll"
+		sscanf("%llA", fp); // "length" "bad use of %ll"
+		sscanf("%lle", fp); // "length" "bad use of %ll"
+		sscanf("%llE", fp); // "length" "bad use of %ll"
+		sscanf("%llf", fp); // "length" "bad use of %ll"
+		sscanf("%llF", fp); // "length" "bad use of %ll"
+		sscanf("%llg", fp); // "length" "bad use of %ll"
+		sscanf("%llG", fp); // "length" "bad use of %ll"
+		sscanf("%lls", s); // "length" "bad use of %ll"
+		sscanf("%ll[ac]", s); // "length" "bad use of %ll"
+		sscanf("%llc", s); // "length" "bad use of %ll"
+		sscanf("%llp", pp); // "length" "bad use of %ll"
+		sscanf("%jd%ji%jo%ju%jx%jX%jn", jp, jp, ujp, ujp, ujp, ujp, jn); /// "length" "bogus %j warning"
+		sscanf("%ja", fp); // "length" "bad use of %j"
+		sscanf("%jA", fp); // "length" "bad use of %j"
+		sscanf("%je", fp); // "length" "bad use of %j"
+		sscanf("%jE", fp); // "length" "bad use of %j"
+		sscanf("%jf", fp); // "length" "bad use of %j"
+		sscanf("%jF", fp); // "length" "bad use of %j"
+		sscanf("%jg", fp); // "length" "bad use of %j"
+		sscanf("%jG", fp); // "length" "bad use of %j"
+		sscanf("%js", s); // "length" "bad use of %j"
+		sscanf("%j[ac]", s); // "length" "bad use of %j"
+		sscanf("%jc", s); // "length" "bad use of %j"
+		sscanf("%jp", pp); // "length" "bad use of %j"
+		sscanf("%zd%zi%zo%zu%zx%zX%zn", szp, szp, zp, zp, zp, zp, zn);
+		sscanf("%za", fp); // "length" "bad use of %z"
+		sscanf("%zA", fp); // "length" "bad use of %z"
+		sscanf("%ze", fp); // "length" "bad use of %z"
+		sscanf("%zE", fp); // "length" "bad use of %z"
+		sscanf("%zf", fp); // "length" "bad use of %z"
+		sscanf("%zF", fp); // "length" "bad use of %z"
+		sscanf("%zg", fp); // "length" "bad use of %z"
+		sscanf("%zG", fp); // "length" "bad use of %z"
+		sscanf("%zs", s); // "length" "bad use of %z"
+		sscanf("%z[ac]", s); // "length" "bad use of %z"
+		sscanf("%zc", s); // "length" "bad use of %z"
+		sscanf("%zp", pp); // "length" "bad use of %z"
+		sscanf("%td%ti%to%tu%tx%tX%tn", tp, tp, utp, utp, utp, utp, tn);
+		sscanf("%ta", fp); // "length" "bad use of %t"
+		sscanf("%tA", fp); // "length" "bad use of %t"
+		sscanf("%te", fp); // "length" "bad use of %t"
+		sscanf("%tE", fp); // "length" "bad use of %t"
+		sscanf("%tf", fp); // "length" "bad use of %t"
+		sscanf("%tF", fp); // "length" "bad use of %t"
+		sscanf("%tg", fp); // "length" "bad use of %t"
+		sscanf("%tG", fp); // "length" "bad use of %t"
+		sscanf("%ts", s); // "length" "bad use of %t"
+		sscanf("%t[ac]", s); // "length" "bad use of %t"
+		sscanf("%tc", s); // "length" "bad use of %t"
+		sscanf("%tp", pp); // "length" "bad use of %t"
+		sscanf("%La%LA%Le%LE%Lf%LF%Lg%LG", ldp, ldp, ldp, ldp, ldp, ldp, ldp, ldp);
+		sscanf("%Ld", llp); // "does not support" "bad use of L"
+		sscanf("%Li", llp); // "does not support" "bad use of L"
+		sscanf("%Lo", ullp); // "does not support" "bad use of L"
+		sscanf("%Lu", ullp); // "does not support" "bad use of L"
+		sscanf("%Lx", ullp); // "does not support" "bad use of L"
+		sscanf("%LX", ullp); // "does not support" "bad use of L"
+		sscanf("%Ls", s); // "length" "bad use of L"
+		sscanf("%L[ac]", s); // "length" "bad use of L"
+		sscanf("%Lc", s); // "length" "bad use of L"
+		sscanf("%Lp", pp); // "length" "bad use of L"
+		sscanf("%Ln", n); // "length" "bad use of L"
+
+		sscanf("%ha", fp); // "length" "bad use of %h"
+		sscanf("%hA", fp); // "length" "bad use of %h"
+		sscanf("%he", fp); // "length" "bad use of %h"
+		sscanf("%hE", fp); // "length" "bad use of %h"
+		sscanf("%hf", fp); // "length" "bad use of %h"
+		sscanf("%hF", fp); // "length" "bad use of %h"
+		sscanf("%hg", fp); // "length" "bad use of %h"
+		sscanf("%hG", fp); // "length" "bad use of %h"
+		sscanf("%hs", s); // "length" "bad use of %h"
+		sscanf("%h[ac]", s); // "length" "bad use of %h"
+		sscanf("%hc", s); // "length" "bad use of %h"
+		sscanf("%hp", pp); // "length" "bad use of %h"
+		sscanf("%hha", fp); // "length" "bad use of %hh"
+		sscanf("%hhA", fp); // "length" "bad use of %hh"
+		sscanf("%hhe", fp); // "length" "bad use of %hh"
+		sscanf("%hhE", fp); // "length" "bad use of %hh"
+		sscanf("%hhf", fp); // "length" "bad use of %hh"
+		sscanf("%hhF", fp); // "length" "bad use of %hh"
+		sscanf("%hhg", fp); // "length" "bad use of %hh"
+		sscanf("%hhG", fp); // "length" "bad use of %hh"
+		sscanf("%hhs", s); // "length" "bad use of %hh"
+		sscanf("%hh[ac]", s); // "length" "bad use of %hh"
+		sscanf("%hhc", s); // "length" "bad use of %hh"
+		sscanf("%hhp", pp); // "length" "bad use of %hh"
+
+		sscanf("%lp", pp); // "length" "bad use of %l"
+		*/
+	}
+
+	return nErrorCount;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestScanf
+//
+int TestScanf()
+{
+	int nErrorCount = 0;
+
+	EA::UnitTest::Report("TestScanf\n");
+
+	nErrorCount += TestScanfLimits();
+	nErrorCount += TestScanfMisc();
+	nErrorCount += TestScanfUnusual();
+	nErrorCount += TestScanfVariants();
+	nErrorCount += TestScanfExtensions();
+	nErrorCount += TestScanfErrors();
+
+	return nErrorCount;
+}
+
+
+

+ 77 - 0
test/source/TestSingleton.cpp

@@ -0,0 +1,77 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EASingleton.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+
+class Widget
+{
+public:
+	Widget(){}
+	Widget(const Widget&){}
+   ~Widget(){}
+	Widget& operator=(const Widget&) { return *this; }
+};
+
+
+class WidgetS : public EA::StdC::Singleton<WidgetS> // Note that this class inherits from a template of itself.
+{
+	WidgetS(const WidgetS&);            // Used to placate VC++ compiler warning C4625
+	WidgetS& operator=(const WidgetS&); // Used to placate VC++ compiler warning C4626
+};
+
+
+class WidgetSA : public EA::StdC::SingletonAdapter<Widget, true>
+{
+	WidgetSA(const WidgetSA&);            // Used to placate VC++ compiler warning C4625
+	WidgetSA& operator=(const WidgetSA&); // Used to placate VC++ compiler warning C4626
+};
+
+
+
+int TestSingleton()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestSingleton\n");
+
+	// Singleton
+	WidgetS* pWidgetS = WidgetS::GetInstance();
+	EATEST_VERIFY(pWidgetS == NULL);
+
+	// SingletonImplicit
+	Widget* pWidget = WidgetSA::GetInstance();
+	EATEST_VERIFY(pWidget != NULL);
+
+	WidgetSA::DestroyInstance();
+
+	// SingletonExplicit
+	pWidget = WidgetSA::GetInstance();
+	EATEST_VERIFY(pWidget != NULL);
+
+	pWidget = WidgetSA::CreateInstance("SomeName");
+	EATEST_VERIFY(pWidget != NULL);
+
+	pWidget = WidgetSA::GetInstance();
+	EATEST_VERIFY(pWidget != NULL);
+
+	WidgetSA::DestroyInstance();
+
+	return nErrorCount;
+}
+

+ 2784 - 0
test/source/TestSprintf.cpp

@@ -0,0 +1,2784 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EASprintfOrdered.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EADateTime.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EASTL/string.h>
+#include <EASTL/unique_ptr.h>
+#include <EATest/EATest.h>
+#include <float.h>
+#include <string.h>
+#include <stdarg.h>
+
+
+static void TestCRTVsnprintf(char8_t* pDestination, size_t n, const char8_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	EA::StdC::Vsnprintf(pDestination, n, pFormat, vList);
+	va_end(vList);
+}
+
+static void TestCRTVsnprintf(char16_t* pDestination, size_t n, const char16_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	EA::StdC::Vsnprintf(pDestination, n, pFormat, vList);
+	va_end(vList);
+}
+
+static void TestCRTVsnprintf(char32_t* pDestination, size_t n, const char32_t* pFormat, ...)
+{
+	va_list vList;
+	va_start(vList, pFormat);
+	EA::StdC::Vsnprintf(pDestination, n, pFormat, vList);
+	va_end(vList);
+}
+
+
+#if EASTDC_VSNPRINTF8_ENABLED
+	static void TestCRTVsnprintf8(char8_t* pDestination, size_t n, const char8_t* pFormat, ...)
+	{
+		va_list vList;
+		va_start(vList, pFormat);
+		EA::StdC::Vsnprintf8(pDestination, n, pFormat, vList);
+		va_end(vList);
+	}
+
+	static void TestCRTVsnprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, ...)
+	{
+		va_list vList;
+		va_start(vList, pFormat);
+		EA::StdC::Vsnprintf16(pDestination, n, pFormat, vList);
+		va_end(vList);
+	}
+
+	static void TestCRTVsnprintf32(char32_t* pDestination, size_t n, const char32_t* pFormat, ...)
+	{
+		va_list vList;
+		va_start(vList, pFormat);
+		EA::StdC::Vsnprintf32(pDestination, n, pFormat, vList);
+		va_end(vList);
+	}
+#endif
+
+
+static float FloatFromBitRepr(uint32_t bits)
+{
+	union { float f; uint32_t u; } typepun;
+	typepun.u = bits;
+	return typepun.f;
+}
+
+static double DoubleFromBitRepr(uint64_t bits)
+{
+	union { double d; uint64_t u; } typepun;
+	typepun.u = bits;
+	return (double)typepun.d;
+}
+
+static int TestSprintf8(int unused = 0, ...)
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	// int Snprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char8_t sn18[128];
+		Snprintf(sn18, 128, "%5s%-4d%03i", "abc", -12, 3);
+		EATEST_VERIFY(!Strcmp("  abc-12 003", sn18));
+		Snprintf(sn18, 128, "%.2f", 3.1415);
+		EATEST_VERIFY(!Strcmp("3.14", sn18));
+	}
+
+	// int Vsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char8_t sn18[128];
+		TestCRTVsnprintf(sn18, 128, "%5s%-5d%04i", "abc", -12, 3);
+		EATEST_VERIFY(!Strcmp("  abc-12  0003", sn18));
+		TestCRTVsnprintf(sn18, 128, "%.2f", 3.1415);
+		EATEST_VERIFY(!Strcmp("3.14", sn18));
+	}
+
+	#if EASTDC_VSNPRINTF8_ENABLED
+		{
+			char8_t sn18[128];
+			TestCRTVsnprintf8(sn18, 128, "%5s%-5d%04i", "abc", -12, 3);
+			EATEST_VERIFY(!Strcmp("  abc-12  0003", sn18));
+			TestCRTVsnprintf8(sn18, 128, "%.2f", 3.1415);
+			EATEST_VERIFY(!Strcmp("3.14", sn18));
+		}
+	#endif
+
+
+	// int Vscprintf(const char_t* pFormat, va_list arguments);
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		int result = Vscprintf("abc", arguments);       
+		EATEST_VERIFY(result == 3);
+
+		va_end(arguments);
+	}
+
+
+	// template <typename String>
+	// int StringVcprintf(String& s, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		eastl::string8 s8;
+		int result = StringVcprintf(s8, "hello", arguments);
+		EATEST_VERIFY((result == 5) && (s8 == "hello"));
+
+		va_end(arguments);
+	}
+
+
+	// template <typename String> 
+	// int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...)
+	{
+		eastl::string8 s8;
+		int result = StringPrintf(s8, "%s", "hello");
+		EATEST_VERIFY((result == 5) && (s8 == "hello"));
+	}
+
+	{
+		char buffer[128];
+		Sprintf(buffer, "%Lf", 42.0l);
+		EATEST_VERIFY(Strcmp(buffer, "42.000000") == 0);
+	}
+
+	{
+		// Test for parsing of PRI constants in format strings
+		char8_t buffer[128];
+		Sprintf(buffer, "%" PRIxPTR, (intptr_t) 0xDEADBEEF);
+		EATEST_VERIFY(Strcmp(buffer, "deadbeef") == 0);
+	}
+
+	// Sprintf
+	{
+		char8_t buffer[128];
+		const int kHexValue = 0x12;
+
+		Sprintf(buffer, "%.4x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%04x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%4.4x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%04.4x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%4.3x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, " 012") == 0);
+
+		Sprintf(buffer, "%04.3x", kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, " 012") == 0);
+
+		Sprintf(buffer, "%.*x", 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%0*x", 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%*.*x", 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+
+		Sprintf(buffer, "%0*.*x", 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, "0012") == 0);
+	}
+
+
+	{
+		char8_t buffer[128];
+
+		Sprintf(buffer, "decimal negative: \"%d\"\n", -2345);
+		EATEST_VERIFY(Strcmp(buffer, "decimal negative: \"-2345\"\n") == 0);
+
+		Sprintf(buffer, "octal negative: \"%o\"\n", -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "octal negative: \"37777773327\"\n") == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "octal negative: \"1777777777777777773327\"\n") == 0);
+
+		Sprintf(buffer, "hex negative: \"%x\"\n", -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "hex negative: \"fffff6d7\"\n") == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "hex negative: \"fffffffffffff6d7\"\n") == 0);
+
+		Sprintf(buffer, "long decimal number: \"%ld\"\n", -123456L);
+		EATEST_VERIFY(Strcmp(buffer, "long decimal number: \"-123456\"\n") == 0);
+
+		Sprintf(buffer, "long octal negative: \"%lo\"\n", -2345L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "long octal negative: \"37777773327\"\n") == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "long octal negative: \"1777777777777777773327\"\n") == 0);
+
+		Sprintf(buffer, "long unsigned decimal number: \"%lu\"\n", -123456L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "long unsigned decimal number: \"4294843840\"\n") == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, "long unsigned decimal number: \"18446744073709428160\"\n") == 0);
+
+		Sprintf(buffer, "zero-padded LDN: \"%010ld\"\n", -123456L);
+		EATEST_VERIFY(Strcmp(buffer, "zero-padded LDN: \"-000123456\"\n") == 0);
+
+		Sprintf(buffer, "left-adjusted ZLDN: \"%-010ld\"\n", -123456L);
+		EATEST_VERIFY(Strcmp(buffer, "left-adjusted ZLDN: \"-123456   \"\n") == 0);
+
+		Sprintf(buffer, "space-padded LDN: \"%10ld\"\n", -123456L);
+		EATEST_VERIFY(Strcmp(buffer, "space-padded LDN: \"   -123456\"\n") == 0);
+
+		Sprintf(buffer, "left-adjusted SLDN: \"%-10ld\"\n", -123456L);
+		EATEST_VERIFY(Strcmp(buffer, "left-adjusted SLDN: \"-123456   \"\n") == 0);
+	}
+
+
+	{
+		char8_t buffer[1024];
+		char8_t str1[] = "abc de";
+		char8_t str2[] = "abd def ghi jkl mno pqr stu vwz yz.";
+
+		// The C99 standard specifies that leading zeros only put zeroes in front of numerical types. Spaces for others.
+		Sprintf(buffer, "zero-padded string: \"%010s\"\n", str1);
+		EATEST_VERIFY(Strcmp(buffer, "zero-padded string: \"    abc de\"\n") == 0); // VC++ fails this, as it puts zeroes in front.
+
+		Sprintf(buffer, "left-adjusted Z string: \"%-010s\"\n", str1);
+		EATEST_VERIFY(Strcmp(buffer, "left-adjusted Z string: \"abc de    \"\n") == 0);
+
+		Sprintf(buffer, "space-padded string: \"%10s\"\n", str1);
+		EATEST_VERIFY(Strcmp(buffer, "space-padded string: \"    abc de\"\n") == 0);
+
+		Sprintf(buffer, "left-adjusted S string: \"%-10s\"\n", str1);
+		EATEST_VERIFY(Strcmp(buffer, "left-adjusted S string: \"abc de    \"\n") == 0);
+
+		Sprintf(buffer, "limited string: \"%.22s\"\n", str2);
+		EATEST_VERIFY(Strcmp(buffer, "limited string: \"abd def ghi jkl mno pq\"\n") == 0);
+
+		Sprintf(buffer, "null string: \"%s\"\n", (char8_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, "null string: \"(null)\"\n") == 0);
+
+		Sprintf(buffer, "%10s\n", (char8_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, "    (null)\n") == 0);
+
+		Sprintf(buffer, "%-10s\n", (char8_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, "(null)    \n") == 0);
+
+		Sprintf(buffer, "%*s%*s%*s", -1, "one", -20, "two", -30, "three");
+		EATEST_VERIFY(Strcmp(buffer, "onetwo                 three                         ") == 0);
+
+		int i;
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, "x%1000s", " ");
+		EATEST_VERIFY(buffer[0] == 'x');
+		for(i = 0; i < 1000; i++)
+		{
+			if(buffer[1 + i] != ' ')
+				break;
+		}
+		if(i != 1000)
+			EATEST_VERIFY(i == 1000);
+		else
+			EATEST_VERIFY(buffer[1 + 1000] == 0);
+	}
+
+
+	{   // String tests
+		// We accept %hc, %c, %lc, %I8c, %I16c, %I32c (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hC, %C, %lC, %I8C, %I16C, %I32C (regular, wide,    wide, char8_t, char16_t, char32_t)
+		// We accept %hs, %s, %ls, %I8s, %I16s, %I32s (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hS, %S, %lS, %I8s, %I16s, %I32s (regular, wide,    wide, char8_t, char16_t, char32_t)
+
+		char8_t  buffer[32];
+		char8_t  dStr8[2]  = { 'd', 0 };
+		char16_t eStr16[2] = { 'e', 0 };
+		char32_t fStr32[2] = { 'f', 0 };
+
+		Sprintf(buffer, "%hc %c %lc %I8c %I16c %I32c", 'a', 'b',           EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+		EATEST_VERIFY(Strcmp(buffer, "a b c d e f") == 0);
+
+		Sprintf(buffer, "%hC %C %lC %I8C %I16C %I32C", 'a', EA_WCHAR('b'), EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+		EATEST_VERIFY(Strcmp(buffer, "a b c d e f") == 0);
+
+		Sprintf(buffer, "%hs %s %ls %I8s %I16s %I32s", "a", "b",           EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+		EATEST_VERIFY(Strcmp(buffer, "a b c d e f") == 0);
+
+		Sprintf(buffer, "%hS %S %lS %I8S %I16S %I32S", "a", EA_WCHAR("b"), EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+		EATEST_VERIFY(Strcmp(buffer, "a b c d e f") == 0);
+	}
+
+
+	{ // NaN/Inf functionality tests
+		char8_t buffer[256];
+
+		const float     kFloat32PositiveInfinity     = FloatFromBitRepr(UINT32_C(0x7f800000));
+		const float     kFloat32NegativeInfinity     = FloatFromBitRepr(UINT32_C(0xff800000));
+		const double    kFloat64PositiveInfinity     = DoubleFromBitRepr(UINT64_C(0x7ff0000000000000));
+		const double    kFloat64NegativeInfinity     = DoubleFromBitRepr(UINT64_C(0xfff0000000000000));
+
+		const float     kFloat32PositiveNaN     = FloatFromBitRepr(UINT32_C(0x7fffffff));
+		const float     kFloat32NegativeNaN     = FloatFromBitRepr(UINT32_C(0xffffffff));
+		const double    kFloat64PositiveNaN     = DoubleFromBitRepr(UINT64_C(0x7fffffffffffffff));
+		const double    kFloat64NegativeNaN     = DoubleFromBitRepr(UINT64_C(0xffffffffffffffff));
+		
+		Sprintf(buffer, "%e %f %g", kFloat32PositiveInfinity, kFloat32PositiveInfinity, kFloat32PositiveInfinity);
+		EATEST_VERIFY(Strcmp(buffer, "inf inf inf") == 0);
+
+		Sprintf(buffer, "%e %f %g", kFloat32NegativeInfinity, kFloat32NegativeInfinity, kFloat32NegativeInfinity);
+		EATEST_VERIFY(Strcmp(buffer, "-inf -inf -inf") == 0);
+
+		Sprintf(buffer, "%e %f %g", kFloat32PositiveNaN, kFloat32PositiveNaN, kFloat32PositiveNaN);
+		EATEST_VERIFY(Strcmp(buffer, "nan nan nan") == 0);
+
+		// The ARM instruction fcvtds (convert single to double point precision) does not maintain the sign of NaN.
+		// (Float is always promoted to double in variable length arguments)
+		// This seems to work on the Android
+		// To consider: Disable this test altogether, as it seems to be too FPU-specific to spend our time with.
+		#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_POWERPC)
+			Sprintf(buffer, "%e %f %g", kFloat32NegativeNaN, kFloat32NegativeNaN, kFloat32NegativeNaN);
+			EATEST_VERIFY(Strcmp(buffer, "-nan -nan -nan") == 0);
+		#else
+			EA_UNUSED(kFloat32NegativeNaN);
+		#endif
+
+		Sprintf(buffer, "%e %f %g", kFloat64PositiveInfinity, kFloat64PositiveInfinity, kFloat64PositiveInfinity);
+		EATEST_VERIFY(Strcmp(buffer, "inf inf inf") == 0);
+
+		Sprintf(buffer, "%e %f %g", kFloat64NegativeInfinity, kFloat64NegativeInfinity, kFloat64NegativeInfinity);
+		EATEST_VERIFY(Strcmp(buffer, "-inf -inf -inf") == 0);
+
+		Sprintf(buffer, "%e %f %g", kFloat64PositiveNaN, kFloat64PositiveNaN, kFloat64PositiveNaN);
+		EATEST_VERIFY(Strcmp(buffer, "nan nan nan") == 0);
+
+		Sprintf(buffer, "%e %f %g", kFloat64NegativeNaN, kFloat64NegativeNaN, kFloat64NegativeNaN);
+		EATEST_VERIFY(Strcmp(buffer, "-nan -nan -nan") == 0);
+
+		#if !defined(EA_PLATFORM_CAPILANO)
+			// This test should theoretically work on Capilano.  But currently it must be disabled or it
+			// will cause the runtime to assert.  A bug has been logged with MS so hopefully the issue
+			// will be resolved in the future.  We should try to enable the test later if it is resolved.
+			//
+			// Link to issue;
+			//   https://forums.xboxlive.com/questions/48008/is-there-a-bug-in-string-formatting-functions-in-t.html
+			const double kSmallestDoubleNum = DoubleFromBitRepr(0x0000000000000001ull);
+			Sprintf(buffer, "%f", kSmallestDoubleNum);
+			EATEST_VERIFY_F(Strcmp(buffer, "0.000000") == 0, "Result was %s", buffer);
+		#endif
+	}
+
+	{ // Extended functionality tests
+
+		char8_t buffer[256];
+
+		Sprintf(buffer, "%08x %032b", 0xaaaaaaaa, 0xaaaaaaaa);
+		EATEST_VERIFY(Strcmp(buffer, "aaaaaaaa 10101010101010101010101010101010") == 0);
+
+		Sprintf(buffer, "%I8u %I8d %I16u %I16d %I32u %I32d %I64u %I64d", 0xff, 0xff, 0xffff, 0xffff, 0xffffffff, 0xffffffff, UINT64_C(0xffffffffffffffff), UINT64_C(0xffffffffffffffff));
+		EATEST_VERIFY(Strcmp(buffer, "255 -1 65535 -1 4294967295 -1 18446744073709551615 -1") == 0);
+
+		Sprintf(buffer, "%s %10s", NULL, NULL);
+		EATEST_VERIFY(Strcmp(buffer, "(null)     (null)") == 0);
+	}
+
+
+	{
+		char8_t buffer[1024];
+		int i;
+
+		Sprintf(buffer, "e-style >= 1: \"%e\"\n", 12.34);
+		EATEST_VERIFY(Strcmp(buffer, "e-style >= 1: \"1.234000e+01\"\n") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "e-style >= .1: \"%e\"\n", 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, "e-style >= .1: \"1.234000e-01\"\n") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "e-style < .1: \"%e\"\n", 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, "e-style < .1: \"1.234000e-03\"\n") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "e-style big: \"%.60e\"\n", 1e20);
+		EATEST_VERIFY(Strcmp(buffer, "e-style big: \"1.000000000000000000000000000000000000000000000000000000000000e+20\"\n") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "e-style == .1: \"%e\"\n", 0.1);
+		EATEST_VERIFY(Strcmp(buffer, "e-style == .1: \"1.000000e-01\"\n") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "f-style >= 1: \"%f\"\n", 12.34);
+		EATEST_VERIFY(Strcmp(buffer, "f-style >= 1: \"12.340000\"\n") == 0);
+
+		Sprintf(buffer, "f-style >= 1: \"%.3f\"\n", 12.34);
+		EATEST_VERIFY(Strcmp(buffer, "f-style >= 1: \"12.340\"\n") == 0);
+
+		Sprintf(buffer, "f-style >= .1: \"%f\"\n", 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, "f-style >= .1: \"0.123400\"\n") == 0);
+
+		Sprintf(buffer, "f-style < .1: \"%f\"\n", 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, "f-style < .1: \"0.001234\"\n") == 0);
+
+		Sprintf(buffer, "g-style >= 1: \"%.0g\"\n", 1.234);
+		EATEST_VERIFY(Strcmp(buffer, "g-style >= 1: \"1.2\"\n") == 0);//%g takes precision to be 1, even if set to be 0
+
+		Sprintf(buffer, "g-style >= 1: \"%.1g\"\n", 1.234);
+		EATEST_VERIFY(Strcmp(buffer, "g-style >= 1: \"1.2\"\n") == 0);
+
+		Sprintf(buffer, "g-style >= 1: \"%.2g\"\n", 1.234);
+		EATEST_VERIFY(Strcmp(buffer, "g-style >= 1: \"1.23\"\n") == 0);
+
+		Sprintf(buffer, "g-style >= 1: \"%g\"\n", 12.34);
+		EATEST_VERIFY(Strcmp(buffer, "g-style >= 1: \"12.34\"\n") == 0);
+
+		Sprintf(buffer, "g-style >= .1: \"%g\"\n", 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, "g-style >= .1: \"0.1234\"\n") == 0);
+
+		Sprintf(buffer, "g-style < .1: \"%g\"\n", 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, "g-style < .1: \"0.001234\"\n") == 0);
+
+		Sprintf(buffer, "g-style < .1: \"%g\"\n", 0.001234678);
+		EATEST_VERIFY(Strcmp(buffer, "g-style < .1: \"0.001235\"\n") == 0);
+
+		Sprintf(buffer, "g-style big: \"%.60g\"\n", 1e20);
+		EATEST_VERIFY(Strcmp(buffer, "g-style big: \"100000000000000000000\"\n") == 0);
+
+		//Sprintf(buffer, "%#.4g\n", 0.0); // The C99 committee has decided in a defect analysis that this is how it should work.
+		//EATEST_VERIFY(Strcmp(buffer, "0\n") == 0);
+
+		Sprintf(buffer, " %6.5f\n", .099999999860301614);
+		EATEST_VERIFY(Strcmp(buffer, " 0.10000\n") == 0);
+
+		Sprintf(buffer, " %6.5f\n", .1);
+		EATEST_VERIFY(Strcmp(buffer, " 0.10000\n") == 0);
+
+		Sprintf(buffer, "x%5.4fx\n", .5);
+		EATEST_VERIFY(Strcmp(buffer, "x0.5000x\n") == 0);
+
+		Sprintf(buffer, "%#03x\n", 1);
+		EATEST_VERIFY(Strcmp(buffer, "0x1\n") == 0);
+
+
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, "%.300f", 1.0);
+		EATEST_VERIFY((buffer[0] == '1') && (buffer[1] == '.'));
+		for(i = 0; i < 300; i++)
+		{
+			if(buffer[2 + i] != '0')
+				break;
+		}
+		if(i != 300)
+			EATEST_VERIFY(i == 300);
+		else
+			EATEST_VERIFY(buffer[2 + 300] == 0);
+
+
+		double d = static_cast<double>(FLT_MIN);    // We are intentionally using FLT_MIN and not DBL_MIN.
+		d /= 2.0;
+		Sprintf(buffer, "%.17e", d);                // It should be something like 5.87747175411143___e-39 where count and values of the _ digits vary by hardware.
+		buffer[16] = buffer[17] = buffer[18] = '_'; // Replace the uncertain digits with '_' characters, as they are system-dependent.
+
+		EATEST_VERIFY(Strcmp(buffer, "5.87747175411143___e-39") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+
+		Sprintf(buffer, "%15.5e\n", 4.9406564584124654e-307);
+		EATEST_VERIFY(Strcmp(buffer, "   4.94066e-307\n") == 0);
+
+
+		// Exercise bug report on PS3 platform which results in a crash when printing DBL_MAX.
+		i = Sprintf(buffer, "%15.8g", DBL_MAX);
+		EATEST_VERIFY(i > 0);
+
+		i = Sprintf(buffer, "%15.8g", FLT_MAX);
+		EATEST_VERIFY(i > 0);
+	}
+
+
+	{
+		char8_t        buffer[256];
+		const char8_t* pExpected;
+
+		// VC++ sprintf would fail these tests, as the Standard says to print no more 
+		// than 2 unless necessary, yet VC++ sprintf prints 3 digits exponents.
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 0.0, 0.0, 0.0);
+		EATEST_VERIFY(Strcmp(buffer, "|      0.0000|  0.0000e+00|           0|") == 0);
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 1.0, 1.0, 1.0);
+		EATEST_VERIFY(Strcmp(buffer, "|      1.0000|  1.0000e+00|           1|") == 0);
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", -1.0, -1.0, -1.0);
+		EATEST_VERIFY(Strcmp(buffer, "|     -1.0000| -1.0000e+00|          -1|") == 0);
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 100.0, 100.0, 100.0);
+		EATEST_VERIFY(Strcmp(buffer, "|    100.0000|  1.0000e+02|         100|") == 0);
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 1000.0, 1000.0, 1000.0);
+		EATEST_VERIFY(Strcmp(buffer, "|   1000.0000|  1.0000e+03|        1000|") == 0);
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 10000.0, 10000.0, 10000.0);
+		EATEST_VERIFY(Strcmp(buffer, "|  10000.0000|  1.0000e+04|       1e+04|") == 0);
+
+		// %g picks one of %f or %e, though uses precision differently.
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 12346.0, 12346.0, 12346.0);
+		pExpected = "|  12346.0000|  1.2346e+04|   1.235e+04|";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 100000.0, 100000.0, 100000.0);
+		pExpected = "| 100000.0000|  1.0000e+05|       1e+05|";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "|%12.4f|%12.4e|%12.4g|", 123467.0, 123467.0, 123467.0);
+		pExpected = "| 123467.0000|  1.2347e+05|   1.235e+05|";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer);
+	}
+
+
+	{
+		char8_t buffer[256];
+
+		// Verify that snprintf follows the C99 convention of returning the number of characters
+		// required. This is as opposed to the non-standard way that some libraries just return 
+		// -1 if the buffer isn't big enough.
+
+		const int kBuf1Capacity = 20;
+		char8_t   buf1[kBuf1Capacity];
+		int       n1 = Snprintf(buf1, kBuf1Capacity, "%30s", "foo");
+		Sprintf(buffer, "snprintf(\"%%30s\", \"foo\") == %d, \"%.*s\"\n", n1, kBuf1Capacity, buf1);
+		EATEST_VERIFY(Strcmp(buffer, "snprintf(\"%30s\", \"foo\") == 30, \"                   \"\n") == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf2Capacity = 512;
+		char8_t   buf2[kBuf2Capacity];
+		int       n2 = Snprintf(buf2, kBuf2Capacity, "%.1000u", 10);
+		Sprintf(buffer, "snprintf(\"%%.1000u\", 10) == %d\n", n2);
+		EATEST_VERIFY(Strcmp(buffer, "snprintf(\"%.1000u\", 10) == 1000\n") == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf3Capacity = 512;
+		char8_t   buf3[kBuf3Capacity];
+		char8_t*  pString = new char8_t[100000];
+		memset(pString, '_', 100000 * sizeof(char8_t));
+		pString[100000 - 1] = 0;
+		int n3 = Snprintf(buf3, kBuf2Capacity, "%s", pString);
+		Sprintf(buffer, "snprintf(\"%%s\", pString) == %d\n", n3);
+		EATEST_VERIFY(Strcmp(buffer, "snprintf(\"%s\", pString) == 99999\n") == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+		delete[] pString;
+
+		int n4 = Snprintf(NULL, 0, "%s", "abc");
+		Sprintf(buffer, "snprintf(NULL, \"abc\") == %d\n", n4);
+		EATEST_VERIFY(Strcmp(buffer, "snprintf(NULL, \"abc\") == 3\n") == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		int n5 = Snprintf(NULL, 100, "%s", "abc");
+		Sprintf(buffer, "snprintf(NULL, \"abc\") == %d\n", n5);
+		EATEST_VERIFY(Strcmp(buffer, "snprintf(NULL, \"abc\") == 3\n") == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+	}
+
+
+	{
+		char8_t buffer[16][256];
+
+		int n = 0, i, j, k, m;
+
+		for(i = 0; i < 2; i++)
+		{
+			for(j = 0; j < 2; j++)
+			{
+				for(k = 0; k < 2; k++)
+				{
+					for(m = 0; m < 2; m++)
+					{
+						char8_t prefix[7];
+						char8_t format[128];
+
+						Strcpy(prefix, "%");
+						if(i == 0)
+							Strcat(prefix, "-");
+						if(j == 0)
+							Strcat(prefix, "+");
+						if(k == 0)
+							Strcat(prefix, "#");
+						if(m == 0)
+							Strcat(prefix, "0");
+
+						#define DEC -123
+						#define INT 255
+						#define UNS (~0)
+
+						Sprintf(format, "%%5s |%s6d |%s6o |%s6x |%s6X |%s6u |", prefix, prefix, prefix, prefix, prefix);
+						Sprintf(buffer[n], format, prefix, DEC, INT, INT, INT, UNS);
+						n++;
+					}
+				}
+			}
+		}
+
+		EATEST_VERIFY(Strcmp(buffer[ 0], "%-+#0 |-123   |0377   |0xff   |0XFF   |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 1], " %-+# |-123   |0377   |0xff   |0XFF   |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 2], " %-+0 |-123   |377    |ff     |FF     |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 3], "  %-+ |-123   |377    |ff     |FF     |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 4], " %-#0 |-123   |0377   |0xff   |0XFF   |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 5], "  %-# |-123   |0377   |0xff   |0XFF   |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 6], "  %-0 |-123   |377    |ff     |FF     |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 7], "   %- |-123   |377    |ff     |FF     |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 8], " %+#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 9], "  %+# |  -123 |  0377 |  0xff |  0XFF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[10], "  %+0 |-00123 |000377 |0000ff |0000FF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[11], "   %+ |  -123 |   377 |    ff |    FF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[12], "  %#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[13], "   %# |  -123 |  0377 |  0xff |  0XFF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[14], "   %0 |-00123 |000377 |0000ff |0000FF |4294967295 |") == 0);
+		EATEST_VERIFY(Strcmp(buffer[15], "    % |  -123 |   377 |    ff |    FF |4294967295 |") == 0);
+
+	}
+
+
+	{
+		char8_t        buffer[256];
+		const char8_t* pExpected;
+
+		Sprintf(buffer, "%e", 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = "1.234568e+06";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%f", 1234567.8);
+		pExpected = "1234567.800000";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%g", 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = "1.23457e+06";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%g", 123.456);
+		pExpected = "123.456";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%g", 1000000.0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = "1e+06";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%g", 10.0);
+		pExpected = "10";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%g", 0.02);
+		pExpected = "0.02";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%.1f", 0.09523f);
+		pExpected = "0.1";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%.1f", 0.9523f);
+		pExpected = "1.0";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%.1f", -0.9523f);
+		pExpected = "-1.0";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%.1e", 0.9523f);
+		pExpected = "9.5e-01";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		#if EASTDC_NATIVE_FCVT && defined(_MSC_VER)
+			Sprintf(buffer, "%.0e", 0.9523f);
+			pExpected = "1e+00";
+			EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+		#else
+			// Note that this test result is incorrect! It should be "1e+00" and not "1.0e00".
+			// We have a bug and it needs to be fixed. At least the numerical value is still correct.
+			// The problem is that GCC's standard library fcvt and our custom fcvt generate a string
+			// of "10" instead of "1" for the case of calling fcvt(0.9523, 0, ...).
+			Sprintf(buffer, "%.0e", 0.9523f);
+			pExpected = "1.0e+00";
+			EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+		#endif
+	}
+
+
+	{   // Test the ' extension, which cases numbers to be printed with a thousands separator.
+		char8_t        buffer[64];
+		const char8_t* pExpected;
+
+		Sprintf(buffer, "%'u", 123456789);
+		EATEST_VERIFY(Strcmp(buffer, "123,456,789") == 0);
+
+		Sprintf(buffer, "%'d", -123456789);
+		EATEST_VERIFY(Strcmp(buffer, "-123,456,789") == 0);
+
+		Sprintf(buffer, "%'I8u", 123);
+		EATEST_VERIFY(Strcmp(buffer, "123") == 0);
+
+		Sprintf(buffer, "%'I16u", 12345);
+		EATEST_VERIFY(Strcmp(buffer, "12,345") == 0);
+
+		Sprintf(buffer, "%'I16d", -12345);
+		EATEST_VERIFY(Strcmp(buffer, "-12,345") == 0);
+
+		Sprintf(buffer, "%'I32u", 12345678);
+		EATEST_VERIFY(Strcmp(buffer, "12,345,678") == 0);
+
+		Sprintf(buffer, "%'I32d", -12345678);
+		EATEST_VERIFY(Strcmp(buffer, "-12,345,678") == 0);
+
+		Sprintf(buffer, "%20I32d", -12345678);
+		EATEST_VERIFY(Strcmp(buffer, "           -12345678") == 0);
+
+		Sprintf(buffer, "%'20I32d", -12345678); // Verify that the , chars count towards the field width.
+		EATEST_VERIFY(Strcmp(buffer, "         -12,345,678") == 0);
+
+		Sprintf(buffer, "%'I32x", 0x12345678);  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, "12345678") == 0);
+
+		Sprintf(buffer, "%'I64u", UINT64_C(1234999995678));
+		EATEST_VERIFY(Strcmp(buffer, "1,234,999,995,678") == 0);
+
+		Sprintf(buffer, "%'I64d", INT64_C(-1234599999678));
+		EATEST_VERIFY(Strcmp(buffer, "-1,234,599,999,678") == 0);
+
+		Sprintf(buffer, "%'I64x", UINT64_C(0x1234567812345678));  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, "1234567812345678") == 0);
+
+		Sprintf(buffer, "%'f", 123456.234);
+		EATEST_VERIFY(Strcmp(buffer, "123,456.234000") == 0);
+
+		Sprintf(buffer, "%'e", 1234567.8);  // ' has no effect on %e formatting.
+		EATEST_VERIFY(Strcmp(buffer, "1.234568e+06") == 0);
+
+		Sprintf(buffer, "%'g", 1234.54);   // In some cases %g acts like %f.
+		pExpected = "1,234.54";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+
+		Sprintf(buffer, "%'g", 1234567.8);   // In some cases %g acts like %f.
+		pExpected = "1.23457e+06";
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I8s\n   Actual:   %I8s", pExpected, buffer); 
+	}
+
+
+	{
+		char8_t buffer[256];
+
+		Sprintf(buffer, "%hhu", UCHAR_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, "1") == 0); // VC++ fails this, as it doesn't implement the C99 standard %hh modifier.
+
+		Sprintf(buffer, "%hu", USHRT_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, "1") == 0);
+	}
+
+
+	{
+		char8_t buffer[128];
+
+		Sprintf(buffer, "%5.s", "xyz");
+		EATEST_VERIFY(Strcmp(buffer, "     ") == 0);
+
+		Sprintf(buffer, "%5.f", 33.3);
+		EATEST_VERIFY(Strcmp(buffer, "   33") == 0);
+
+		Sprintf(buffer, "%8.e", 33.3e7);
+		EATEST_VERIFY(Strcmp(buffer, "   3e+08") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "%8.E", 33.3e7);
+		EATEST_VERIFY(Strcmp(buffer, "   3E+08") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "%.g", 33.3);
+		EATEST_VERIFY(Strcmp(buffer, "3e+01") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, "%.G", 33.3);
+		EATEST_VERIFY(Strcmp(buffer, "3E+01") == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+	}
+
+
+	{
+		char8_t buffer[128];
+		int     precision;
+		//for %g, precision 0 is not valid, if specified 0, it is taken as 1
+		precision = 0;
+		Sprintf(buffer, "%.*g", precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, "3.3") == 0);
+
+		precision = 0;
+		Sprintf(buffer, "%.*G", precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, "3.3") == 0);
+
+		precision = 0;
+		Sprintf(buffer, "%7.*G", precision, 3.33);
+		EATEST_VERIFY(Strcmp(buffer, "    3.3") == 0);
+
+		precision = 3;
+		Sprintf(buffer, "%04.*o", precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, " 041") == 0);
+
+		precision = 7;
+		Sprintf(buffer, "%09.*u", precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, "  0000033") == 0);
+
+		precision = 3;
+		Sprintf(buffer, "%04.*x", precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, " 021") == 0);
+
+		precision = 3;
+		Sprintf(buffer, "%04.*X", precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, " 021") == 0);
+	}
+
+
+	{   // EAC tests
+
+		char8_t dest1[1024], dest3[1024];
+
+		// Test #1
+		Sprintf(dest1, "Hello.  Test.");
+		EATEST_VERIFY(Strcmp(dest1, "Hello.  Test.") == 0);
+
+		// Test #2
+		Sprintf(dest1,"Hello.\\ \\  a\n\n\tTest.");
+		EATEST_VERIFY(Strcmp(dest1, "Hello.\\ \\  a\n\n\tTest.") == 0);
+
+		// Test #3
+		Sprintf(dest1, "Int. %d %d %d blah%d%d", -1, 1, 0, 392832993, -32903298);
+		EATEST_VERIFY(Strcmp(dest1, "Int. -1 1 0 blah392832993-32903298") == 0);
+
+		// Test #4
+		Sprintf(dest1,"Float.  %f %f %f\n", -0.01f, 10.0f, 0.0f);
+		EATEST_VERIFY(Strcmp(dest1, "Float.  -0.010000 10.000000 0.000000\n") == 0);
+
+		// Test #5
+		Sprintf(dest1, "Str/char8_t: %s %c %c", "test", 'b', 0341);
+		EATEST_VERIFY(Strcmp(dest1, "Str/char8_t: test b \341") == 0);
+
+		// Test #6
+		Sprintf(dest1,"Hex: %x %X",3829,-392);
+		EATEST_VERIFY(Strcmp(dest1, "Hex: ef5 FFFFFE78") == 0);
+
+		// Test #7
+		//for(int32_t i = 0; i < 7; i++)    We'd need to modify the tests below to deal with this.
+		//{
+			Sprintf(dest3, "Precision: %%.%df", 0); 
+			EATEST_VERIFY(Strcmp(dest3, "Precision: %.0f") == 0);
+			Sprintf(dest1, dest3, 34.56f);
+			EATEST_VERIFY(Strcmp(dest1, "Precision: 35") == 0);
+		//}
+
+		// Test #8
+		Sprintf(dest1, "Align. %9s %8d %5s %10.2f", "test", 10293, "testtesttest", 10.8f);
+		EATEST_VERIFY(Strcmp(dest1, "Align.      test    10293 testtesttest      10.80") == 0);
+
+		// Test #9
+		Sprintf(dest1, "Align. %-9s %-8d %-5s %-10.2f", "test", 10293, "testtesttest", 10.8f);
+		EATEST_VERIFY(Strcmp(dest1, "Align. test      10293    testtesttest 10.80     ") == 0);
+
+		// Test #10
+		Sprintf(dest1, "Str: %S", EA_WCHAR("test"));
+		EATEST_VERIFY(Strcmp(dest1, "Str: test") == 0);
+
+		// Test #11
+		Sprintf(dest1, "Str: %9S", EA_WCHAR("test"));
+		EATEST_VERIFY(Strcmp(dest1, "Str:      test") == 0);
+
+		// Test #12
+		Sprintf(dest1, "Str: %-9S", EA_WCHAR("test"));
+		EATEST_VERIFY(Strcmp(dest1, "Str: test     ") == 0);
+
+		// Test #13
+		Sprintf(dest1, "0x%08x", 0x876543);
+		EATEST_VERIFY(Strcmp(dest1, "0x00876543") == 0);
+
+		// Test #14
+		Sprintf(dest1, "%08d", -15);
+		EATEST_VERIFY(Strcmp(dest1, "-0000015") == 0);
+
+		// Test #15
+		Sprintf(dest1, "Int. %i %i %i blah%i%i", -1, 1, 0, 392832993, -32903298);
+		EATEST_VERIFY(Strcmp(dest1, "Int. -1 1 0 blah392832993-32903298") == 0);
+
+		// Test #16
+		Sprintf(dest1, "%p", 0x38e340);
+		EATEST_VERIFY(Strcmp(dest1, "38e340") == 0);
+
+		// Test #17
+		Sprintf(dest1, "%u", -372834);
+		EATEST_VERIFY(Strcmp(dest1, "4294594462") == 0);
+
+		// Test #18
+		int tv1, tv2;
+
+		Sprintf(dest1, "Testing%n%dTests%nabc", &tv1, 4738, &tv2);
+		EATEST_VERIFY(Strcmp(dest1, "Testing4738Testsabc") == 0);
+		EATEST_VERIFY((tv1 == 7) && (tv2 == 16));
+
+		// Test #19
+		Sprintf(dest1, "%o", -372834);
+		EATEST_VERIFY(Strcmp(dest1, "37776447636") == 0);
+
+		// Test #20
+		Sprintf(dest1, "%%%s", "Hello");
+		EATEST_VERIFY(Strcmp(dest1, "%Hello") == 0);
+
+		// Test #21
+		Sprintf(dest1, "%I64d", INT64_C(-12345678901234));
+		EATEST_VERIFY(Strcmp(dest1, "-12345678901234") == 0);
+
+		// Test #22
+		Sprintf(dest1, "%I64u", INT64_C(-12345678901234));
+		EATEST_VERIFY(Strcmp(dest1, "18446731728030650382") == 0);
+
+		// Test #23
+		Sprintf(dest1, "%I64x", INT64_C(0x1234567890123a));
+		EATEST_VERIFY(Strcmp(dest1, "1234567890123a") == 0);
+
+		// Test #24
+		Sprintf(dest1, "%I64X", INT64_C(0x1234567890123A));
+		EATEST_VERIFY(Strcmp(dest1, "1234567890123A") == 0);
+	}
+
+	{ // EAC FPU tests
+
+		const float list1f[]={0.832f,0.00832f,8.32f,0.0f,-0.15f};
+
+		const char8_t* list1[]={"0.832000","0.008320","8.320000","0.000000","-0.150000"};
+
+		const float list2f[]={0.0000000000123f,0.000000000123f,0.00000000123f,0.0000000123f
+								,0.000000123f,0.00000123f,0.0000123f,0.000123f,0.00123f,0.0123f,0.123f,1.23f
+								,12.3f,123.0f,1230.0f,12300.0f,123000.0f,1230000.0f,12300000.0f,123000000.0f,12300000000.0f};
+
+		const char8_t* list2[]={"0.000000","0.000000","0.000000","0.000000"
+								,"0.000000","0.000001","0.000012","0.000123","0.001230","0.012300","0.123000","1.230000"
+								,"12.300000","123.000000","1230.000000","12300.000000","123000.000000","1230000.000000",
+								"12300000.000000","123000000.000000","12300000000.000000"};
+
+		const char8_t* list22D[]={"0.00","0.00","0.00","0.00"
+								,"0.00","0.00","0.00","0.00","0.00","0.01","0.12","1.23"
+								,"12.30","123.00","1230.00","12300.00","123000.00","1230000.00","12300000.00",
+								"123000000.00","12300000000.00"};
+
+		const char8_t* list23DSL[]={"1.230e-11","1.230e-10","1.230e-09","1.230e-08"
+								,"1.230e-07","1.230e-06","1.230e-05","1.230e-04","1.230e-03","1.230e-02","1.230e-01","1.230e+00"
+								,"1.230e+01","1.230e+02","1.230e+03","1.230e+04","1.230e+05","1.230e+06","1.230e+07","1.230e+08"
+								,"1.230e+10"};
+
+		const char8_t* list24DSL[]={"1.230E-11","1.230E-10","1.230E-09","1.230E-08"
+								,"1.230E-07","1.230E-06","1.230E-05","1.230E-04","1.230E-03","1.230E-02","1.230E-01","1.230E+00"
+								,"1.230E+01","1.230E+02","1.230E+03","1.230E+04","1.230E+05","1.230E+06","1.230E+07","1.230E+08"
+								,"1.230E+10"};
+
+		const char8_t* list31DSL[]={"1.23e-11","1.23e-10","1.23e-09","1.23e-08"
+								,"1.23e-07","1.23e-06","1.23e-05","0","0.001","0.012","0.123","1.23"
+								,"12.3","123","1.23e+03","1.23e+04","1.23e+05","1.23e+06","1.23e+07","1.23e+08"
+								,"1.23e+10"};
+
+		const char8_t* list32DSL[]={"1.23E-11","1.23E-10","1.23E-09","1.23E-08"
+								,"1.23E-07","1.23E-06","1.23E-05","0","0.001","0.012","0.123","1.23"
+								,"12.3","123","1.23E+03","1.23E+04","1.23E+05","1.23E+06","1.23E+07","1.23E+08"
+								,"1.23E+10"};
+
+		int32_t list1size = sizeof(list1f)/sizeof(list1f[0]);
+		int32_t list2size = sizeof(list2f)/sizeof(list2f[0]);
+		int32_t i, error;
+		char8_t str[256];
+
+		error = 0;
+		for(i = 0; i < list1size; i++)
+		{
+			Sprintf(str, "%f", list1f[i]);
+			if(Strcmp(str, list1[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(A): %s/%s (Halt if > 2/section A)", str, list1[i]);
+			}
+		}
+
+		EATEST_VERIFY(error <= 2);
+		error = 0;
+		for(i = 0; i < list2size - 4; i++)     // purposely take out the last 4 due to precision of float
+		{
+			Sprintf(str, "%f", list2f[i]);
+			if(Strcmp(str, list2[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(B): %s/%s (Halt if > 3/section B)", str, list2[i]);
+			}
+		}
+
+		EATEST_VERIFY(error <= 3);
+		error = 0;
+		for(i = 0; i < list2size - 2; i++)     // purposely take out the last 2 due to precision of float
+		{
+			Sprintf(str, "%.2f", list2f[i]);
+			if(Strcmp(str, list22D[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(C): %s/%s (Halt if > 3/section C)", str, list22D[i]);
+			}
+		}
+
+		EATEST_VERIFY(error <= 3);
+		error = 0;
+		for(i=0; i < list2size; i++)
+		{
+			Sprintf(str, "%.3e", list2f[i]);
+			if(Strcmp(str, list23DSL[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(D): %s/%s (Halt if > 3/section D)", str, list23DSL[i]);
+			}
+		}
+
+		EATEST_VERIFY(error <= 2);
+		error = 0;
+		for(i = 0; i < list2size; i++)
+		{
+			Sprintf(str,"%.3E", list2f[i]);
+			if(Strcmp(str, list24DSL[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(E): %s/%s (Halt if > 3/section E)", str, list24DSL[i]);
+			}
+		}
+
+		EATEST_VERIFY(error <= 2);
+		error = 0;
+		for(i = 0; i < list2size; i++)
+		{
+			Sprintf(str,"%.3g", list2f[i]);
+			if(Strcmp(str, list31DSL[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(F): %s/%s (Halt if > 0/section F)", str, list23DSL[i]);
+			}
+		}
+
+		EATEST_VERIFY(error == 0);
+		error=0;
+		for (i=0;i<list2size;i++)
+		{
+			Sprintf(str, "%.3G", list2f[i]);
+			if(Strcmp(str, list32DSL[i]) != 0)
+			{
+				error++;
+				EA::UnitTest::Report("\nMismatch(G): %s/%s (Halt if > 0/section G)", str, list24DSL[i]);
+			}
+		}
+		EATEST_VERIFY(error == 0);
+	}
+
+
+	{ // Regression for user-reported bug.
+		const size_t kBufferSize = 2048;
+
+		char format[16];
+		EA::StdC::Snprintf(format, sizeof(format), "%%.%us%%c123", kBufferSize - 1);
+		EATEST_VERIFY(Strcmp(format, "%.2047s%c123") == 0);
+
+		auto unique_buffer = eastl::make_unique<char8_t[]>(kBufferSize);
+		auto unique_expect = eastl::make_unique<char8_t[]>(kBufferSize + 16);
+		auto unique_actual = eastl::make_unique<char8_t[]>(kBufferSize + 16);
+
+		char8_t* buffer = unique_buffer.get();
+		char8_t* expectedOutput = unique_expect.get();
+		char8_t* actualOutput = unique_actual.get();
+
+		memset(buffer, '?', kBufferSize);
+		buffer[kBufferSize - 1] = 0;
+		char c = 'A';
+
+		EA::StdC::Snprintf(actualOutput, kBufferSize + 16, format, buffer, c);
+		memset(expectedOutput, '?', 2047);
+		expectedOutput[2047] = 0;
+		strcat(expectedOutput, "A123");
+		EATEST_VERIFY(Strcmp(actualOutput, expectedOutput) == 0);
+	}
+
+	{
+		static const int kSourceSize = 1024 * 5;
+		static const int kOutputSize = kSourceSize + 100;
+		char16_t value[kSourceSize];
+		char8_t destination[kOutputSize];
+		char8_t comparison[kOutputSize];
+
+		for(int i = 0; i < kSourceSize - 1; ++i)
+		{
+			value[i] = '0' + (i % 10);
+			comparison[i] = '0' + (i % 10);
+		}
+		value[kSourceSize - 1] = 0;
+		comparison[kSourceSize - 1] = 0;
+
+		EA::StdC::Snprintf(destination, kOutputSize, "%I16s", value);
+		EATEST_VERIFY(Strcmp(destination, comparison) == 0);
+
+		EA::StdC::Snprintf(destination, kOutputSize, "%.10I16s", value);
+		EATEST_VERIFY(Strcmp(destination, "0123456789") == 0);
+	}
+
+	/* Copied from rwstdc but not completed.
+	// Compare with Sprintf
+	{
+		char8_t  buffer1[128];
+		char8_t  buffer2[128];
+		char8_t* test = "test %d %5.2f   %s \n \t\t\\\\  %X";
+
+		Sprintf(buffer1, test, -3923, 0.38293, "test", 4568);
+		Snprintf(buffer2, 128, test, -3923, 0.38293, "test", 4568);
+		EATEST_VERIFY(strcmp(buffer1, buffer2) == 0);
+	}
+
+
+	// Compare with standard sprintf
+	{
+		char8_t buffer1[128];
+		char8_t buffer2[128];
+		char*   test = "test %d %5.2f   %s \n \t\t\\\\  %X";
+
+		sprintf(buffer1, test, -3923, 0.38293, "test", 4568);
+		Snprintf(buffer2, 128, test, -3923, 0.38293, "test", 4568);
+		EATEST_VERIFY(strcmp(buffer1, buffer2) == 0);
+	}
+
+
+	{
+		float sum    = 1035.32432f;
+		float ave    = 34.3f;
+		float min    = 343.34f;
+		float max    = 576.3f;
+		char  test[] = "NumberEvents: %5d, TotalDuration: %12.4f ms\nAve: %12.4f ms = %6.2f Hz\nMin: %12.4f ms = %6.2f Hz\nMax: %12.4f ms = %6.2f Hz\n";
+
+		int res = Snprintf(snprintfString, bufferLength, 
+						   "NumberEvents: %5d, TotalDuration: %12.4f ms\nAve: %12.4f ms = %6.2f Hz\nMin: %12.4f ms = %6.2f Hz\nMax: %12.4f ms = %6.2f Hz\n",
+						   bufferLength, sum, ave, 1000.0f/ave, min, 1000.0f/min, max, 1000.0f/max);
+
+		// Full String
+		char testString[384];
+		sprintf(testString, test, 384, sum, ave, 1000.0f/ave, min, 1000.0f/min, max, 1000.0f/max);
+		EATEST_VERIFY(strcmp(snprintfString, testString) == 0);
+
+		// Truncated string.
+		const size_t origStringSize = strlen(snprintfString);
+		for(size_t i = 0; i < origStringSize; i++)
+		{
+			res = Snprintf(snprintfString, i, test, 384, sum, ave, 1000.0f/ave, min, 1000.0f/min, max, 1000.0f/max);
+			EATEST_VERIFY(res == origStringSize);
+		}
+	}
+
+
+	{
+		int   testData = 123;
+		char* test     = "'%d' '%10d' '%-10d'";
+
+		sprintf(expectedString, test, testData, testData, testData);
+		Snprintf(snprintfString, bufferLength, test, testData, testData, testData);
+		EATEST_VERIFY(strcmp(expectedString, snprintfString) == 0);
+	}
+	*/
+
+	return nErrorCount;
+}
+
+
+static int TestSprintf16(int unused = 0, ...)
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	// int Snprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char16_t sn18[128];
+		Snprintf(sn18, 128, EA_CHAR16("%5s%-4d%03i"), EA_WCHAR("abc"), -12, 3);
+		EATEST_VERIFY(!Strcmp(EA_CHAR16("  abc-12 003"), sn18));
+		Snprintf(sn18, 128, EA_CHAR16("%.2f"), 3.1415);
+		EATEST_VERIFY(!Strcmp(EA_CHAR16("3.14"), sn18));
+	}
+
+	// int Vsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char16_t sn18[128];
+		TestCRTVsnprintf(sn18, 128, EA_CHAR16("%5s%-5d%04i"), EA_WCHAR("abc"), -12, 3);
+		EATEST_VERIFY(!Strcmp(EA_CHAR16("  abc-12  0003"), sn18));
+		TestCRTVsnprintf(sn18, 128, EA_CHAR16("%.2f"), 3.1415);
+		EATEST_VERIFY(!Strcmp(EA_CHAR16("3.14"), sn18));
+	}
+
+	#if EASTDC_VSNPRINTF8_ENABLED
+		{
+			char16_t sn18[128];
+			TestCRTVsnprintf16(sn18, 128, EA_CHAR16("%5s%-5d%04i"), EA_WCHAR("abc"), -12, 3);
+			EATEST_VERIFY(!Strcmp(EA_CHAR16("  abc-12  0003"), sn18));
+			TestCRTVsnprintf16(sn18, 128, EA_CHAR16("%.2f"), 3.1415);
+			EATEST_VERIFY(!Strcmp(EA_CHAR16("3.14"), sn18));
+		}
+	#endif
+
+	// int Vscprintf(const char_t* pFormat, va_list arguments);
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		int result = Vscprintf(EA_CHAR16("abc"), arguments);       
+		EATEST_VERIFY(result == 3);
+
+		va_end(arguments);
+	}
+
+	// template <typename String>
+	// int StringVcprintf(String& s, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		eastl::string16 s16;
+		int result = StringVcprintf(s16, EA_CHAR16("hello"), arguments);
+		EATEST_VERIFY((result == 5) && (s16 == EA_CHAR16("hello")));
+
+		va_end(arguments);
+	}
+
+	// template <typename String> 
+	// int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...)
+	{
+		eastl::string16 s16;
+		int result = StringPrintf(s16, EA_CHAR16("%s"), EA_WCHAR("hello"));
+		EATEST_VERIFY((result == 5) && (s16 == EA_CHAR16("hello")));
+	}
+
+	{
+		char16_t buffer[128];
+		const int kHexValue = 0x12;
+
+		Sprintf(buffer, EA_CHAR16("%.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%04x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%4.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%04.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%4.3x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%04.3x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%.*x"), 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%0*x"), 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%*.*x"), 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%0*.*x"), 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0012")) == 0);
+	}
+
+
+	{
+		char16_t buffer[128];
+
+		Sprintf(buffer, EA_CHAR16("decimal negative: \"%d\"\n"), -2345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("decimal negative: \"-2345\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("octal negative: \"%o\"\n"), -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("octal negative: \"37777773327\"\n")) == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("octal negative: \"1777777777777777773327\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("hex negative: \"%x\"\n"), -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("hex negative: \"fffff6d7\"\n")) == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("hex negative: \"fffffffffffff6d7\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("long decimal number: \"%ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("long decimal number: \"-123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("long octal negative: \"%lo\"\n"), -2345L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("long octal negative: \"37777773327\"\n")) == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("long octal negative: \"1777777777777777773327\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("long unsigned decimal number: \"%lu\"\n"), -123456L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("long unsigned decimal number: \"4294843840\"\n")) == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("long unsigned decimal number: \"18446744073709428160\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("zero-padded LDN: \"%010ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("zero-padded LDN: \"-000123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("left-adjusted ZLDN: \"%-010ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("left-adjusted ZLDN: \"-123456   \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("space-padded LDN: \"%10ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("space-padded LDN: \"   -123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("left-adjusted SLDN: \"%-10ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("left-adjusted SLDN: \"-123456   \"\n")) == 0);
+	}
+
+
+	{
+		char16_t  buffer[1024];
+		wchar_t   str1[64]; Strlcpy(str1, EA_WCHAR("abc de"), EAArrayCount(str1)); // Can't do str1[64] = EA_CHAR16("abc de") because some compilers don't support 16 bit string literals.
+		wchar_t   str2[64]; Strlcpy(str2, EA_WCHAR("abd def ghi jkl mno pqr stu vwz yz."), EAArrayCount(str2));
+
+		// The C99 standard specifies that leading zeros only put zeroes in front of numerical types. Spaces for others.
+		Sprintf(buffer, EA_CHAR16("zero-padded string: \"%010s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("zero-padded string: \"    abc de\"\n")) == 0); // VC++ fails this, as it puts zeroes in front.
+
+		Sprintf(buffer, EA_CHAR16("left-adjusted Z string: \"%-010s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("left-adjusted Z string: \"abc de    \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("space-padded string: \"%10s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("space-padded string: \"    abc de\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("left-adjusted S string: \"%-10s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("left-adjusted S string: \"abc de    \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("limited string: \"%.22s\"\n"), str2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("limited string: \"abd def ghi jkl mno pq\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("null string: \"%s\"\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("null string: \"(null)\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%10s\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("    (null)\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%-10s\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("(null)    \n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%*s%*s%*s"), -1, EA_WCHAR("one"), -20, EA_WCHAR("two"), -30, EA_WCHAR("three"));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("onetwo                 three                         ")) == 0);
+
+		int i;
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, EA_CHAR16("x%1000ls"), EA_WCHAR(" "));
+		EATEST_VERIFY(buffer[0] == 'x');
+		for(i = 0; i < 1000; i++)
+		{
+			if(buffer[1 + i] != ' ')
+				break;
+		}
+		if(i != 1000)
+			EATEST_VERIFY(i == 1000);
+		else
+			EATEST_VERIFY(buffer[1 + 1000] == EA_CHAR16('\0'));
+	}
+
+
+	{   // String tests
+		// We accept %hc, %c, %lc, %I8c, %I16c, %I32c (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hC, %C, %lC, %I8C, %I16C, %I32C (regular, wide,    wide, char8_t, char16_t, char32_t)
+		// We accept %hs, %s, %ls, %I8s, %I16s, %I32s (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hS, %S, %lS, %I8s, %I16s, %I32s (regular, wide,    wide, char8_t, char16_t, char32_t)
+
+		char16_t buffer[32];
+		char8_t  dStr8[2]  = { 'd', 0 };
+		char16_t eStr16[2] = { 'e', 0 };
+		char32_t fStr32[2] = { 'f', 0 };
+
+		#if EASPRINTF_MS_STYLE_S_FORMAT // Microsoft style means that the meanings of S and s are reversed for non-char8_t Sprintf.
+			Sprintf(buffer, EA_CHAR16("%hc %c %lc %I8c %I16c %I32c"), 'a', EA_WCHAR('b'), EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hC %C %lC %I8C %I16C %I32C"), 'a', 'b',           EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hs %s %ls %I8s %I16s %I32s"), "a", EA_WCHAR("b"), EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hS %S %lS %I8S %I16S %I32S"), "a", "b",           EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+		#else
+			Sprintf(buffer, EA_CHAR16("%hc %c %lc %I8c %I16c %I32c"), 'a', 'b',           EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hC %C %lC %I8C %I16C %I32C"), 'a', EA_WCHAR('b'), EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f)") == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hs %s %ls %I8s %I16s %I32s"), "a", "b",           EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR16("%hS %S %lS %I8S %I16S %I32S"), "a", EA_WCHAR("b"), EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("a b c d e f")) == 0);
+		#endif
+	}
+
+	{
+		char16_t buffer[1024];
+		int i;
+
+		Sprintf(buffer, EA_CHAR16("e-style >= 1: \"%e\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("e-style >= 1: \"1.234000e+01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR16("e-style >= .1: \"%e\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("e-style >= .1: \"1.234000e-01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR16("e-style < .1: \"%e\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("e-style < .1: \"1.234000e-03\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR16("e-style big: \"%.60e\"\n"), 1e20);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("e-style big: \"1.000000000000000000000000000000000000000000000000000000000000e+20\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR16("e-style == .1: \"%e\"\n"), 0.1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("e-style == .1: \"1.000000e-01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR16("f-style >= 1: \"%f\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("f-style >= 1: \"12.340000\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("f-style >= .1: \"%f\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("f-style >= .1: \"0.123400\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("f-style < .1: \"%f\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("f-style < .1: \"0.001234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("g-style >= 1: \"%g\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("g-style >= 1: \"12.34\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("g-style >= .1: \"%g\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("g-style >= .1: \"0.1234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("g-style < .1: \"%g\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("g-style < .1: \"0.001234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("g-style big: \"%.60g\"\n"), 1e20);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("g-style big: \"100000000000000000000\"\n")) == 0);
+
+		//Sprintf(buffer, EA_CHAR16("%#.4g\n"), 0.0); // The C99 committee has decided in a defect analysis that this is how it should work.
+		//EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16(" %6.5f\n"), .099999999860301614);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 0.10000\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16(" %6.5f\n"), .1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 0.10000\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("x%5.4fx\n"), .5);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("x0.5000x\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%#03x\n"), 1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("0x1\n")) == 0);
+
+
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, EA_CHAR16("%.300f"), 1.0);
+		EATEST_VERIFY((buffer[0] == '1') && (buffer[1] == '.'));
+		for(i = 0; i < 300; i++)
+		{
+			if(buffer[2 + i] != '0')
+				break;
+		}
+		if(i != 300)
+			EATEST_VERIFY(i == 300);
+		else
+			EATEST_VERIFY(buffer[2 + 300] == EA_CHAR16('\0'));
+
+
+		// Operations on FLT_MIN are undefined on Neon
+		double d = static_cast<double>(FLT_MIN);    // We are intentionally using FLT_MIN and not DBL_MIN.
+		d /= 2.0;
+		Sprintf(buffer, EA_CHAR16("%.17e"), d);     // It should be something like 5.87747175411143___e-39 where count and values of the _ digits vary by hardware.
+		buffer[16] = buffer[17] = buffer[18] = '_'; // Replace the uncertain digits with '_' characters, as they are system-dependent.
+		
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("5.87747175411143___e-39")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+
+		Sprintf(buffer, EA_CHAR16("%15.5e\n"), 4.9406564584124654e-307);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("   4.94066e-307\n")) == 0);
+	}
+
+
+	{
+		char16_t        buffer[256];
+		const char16_t* pExpected;
+
+		// VC++ sprintf would fail these tests, as the Standard says to print no more 
+		// than 2 unless necessary, yet VC++ sprintf prints 3 digits exponents.
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 0.0, 0.0, 0.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("|      0.0000|  0.0000e+00|           0|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 1.0, 1.0, 1.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("|      1.0000|  1.0000e+00|           1|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), -1.0, -1.0, -1.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("|     -1.0000| -1.0000e+00|          -1|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 100.0, 100.0, 100.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("|    100.0000|  1.0000e+02|         100|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 1000.0, 1000.0, 1000.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("|   1000.0000|  1.0000e+03|        1000|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 10000.0, 10000.0, 10000.0);
+		EATEST_VERIFY(Strcmp(buffer,EA_CHAR16("|  10000.0000|  1.0000e+04|       1e+04|")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 12346.0, 12346.0, 12346.0);
+		pExpected = EA_CHAR16("|  12346.0000|  1.2346e+04|   1.235e+04|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 100000.0, 100000.0, 100000.0);
+		pExpected = EA_CHAR16("| 100000.0000|  1.0000e+05|       1e+05|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("|%12.4f|%12.4e|%12.4g|"), 123467.0, 123467.0, 123467.0);
+		pExpected = EA_CHAR16("| 123467.0000|  1.2347e+05|   1.235e+05|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+	}
+
+
+	{
+		char16_t buffer[256];
+
+		// Verify that snprintf follows the C99 convention of returning the number of characters
+		// required. This is as opposed to the non-standard way that some libraries just return 
+		// -1 if the buffer isn't big enough.
+
+		const int kBuf1Capacity = 20;
+		wchar_t   buf1[kBuf1Capacity];
+		int       n1 = Snprintf(buf1, kBuf1Capacity, EA_WCHAR("%30I16s"), EA_CHAR16("foo"));
+		Sprintf(buffer, EA_CHAR16("snprintf(\"%%30s\", \"foo\") == %d, \"%.*s\"\n"), n1, kBuf1Capacity, buf1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("snprintf(\"%30s\", \"foo\") == 30, \"                   \"\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf2Capacity = 512;
+		char16_t  buf2[kBuf2Capacity];
+		int       n2 = Snprintf(buf2, kBuf2Capacity, EA_CHAR16("%.1000u"), 10);
+		Sprintf(buffer, EA_CHAR16("snprintf(\"%%.1000u\", 10) == %d\n"), n2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("snprintf(\"%.1000u\", 10) == 1000\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf3Capacity = 512;
+		char16_t  buf3[kBuf3Capacity];
+		char16_t* pString = new char16_t[100000];
+		memset(pString, '_', 100000 * sizeof(char16_t));
+		pString[100000 - 1] = 0;
+		int n3 = Snprintf(buf3, kBuf2Capacity, EA_CHAR16("%I16s"), pString);
+		Sprintf(buffer, EA_CHAR16("snprintf(\"%%s\", pString) == %d\n"), n3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("snprintf(\"%s\", pString) == 99999\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+		delete[] pString;
+
+		int n4 = Snprintf(NULL, 0, EA_CHAR16("%I16s"), EA_CHAR16("abc"));
+		Sprintf(buffer, EA_CHAR16("snprintf(NULL, \"abc\") == %d\n"), n4);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("snprintf(NULL, \"abc\") == 3\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		int n5 = Snprintf(NULL, 100, EA_CHAR16("%I16s"), EA_CHAR16("abc"));
+		Sprintf(buffer, EA_CHAR16("snprintf(NULL, \"abc\") == %d\n"), n5);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("snprintf(NULL, \"abc\") == 3\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+	}
+
+
+	{
+		char16_t buffer[16][256];
+
+		int n = 0, i, j, k, m;
+
+		for(i = 0; i < 2; i++)
+		{
+			for(j = 0; j < 2; j++)
+			{
+				for(k = 0; k < 2; k++)
+				{
+					for(m = 0; m < 2; m++)
+					{
+						wchar_t  prefix[7];
+						char16_t format[128];
+
+						Strcpy(prefix, EA_WCHAR("%"));
+						if(i == 0)
+							Strcat(prefix, EA_WCHAR("-"));
+						if(j == 0)
+							Strcat(prefix, EA_WCHAR("+"));
+						if(k == 0)
+							Strcat(prefix, EA_WCHAR("#"));
+						if(m == 0)
+							Strcat(prefix, EA_WCHAR("0"));
+
+						#define DEC -123
+						#define INT  255
+						#define UNS (~0)
+
+						Sprintf(format, EA_CHAR16("%%5s |%s6d |%s6o |%s6x |%s6X |%s6u |"), prefix, prefix, prefix, prefix, prefix);
+						Sprintf(buffer[n], format, prefix, DEC, INT, INT, INT, UNS);
+						n++;
+					}
+				}
+			}
+		}
+
+		EATEST_VERIFY(Strcmp(buffer[ 0], EA_CHAR16("%-+#0 |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 1], EA_CHAR16(" %-+# |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 2], EA_CHAR16(" %-+0 |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 3], EA_CHAR16("  %-+ |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 4], EA_CHAR16(" %-#0 |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 5], EA_CHAR16("  %-# |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 6], EA_CHAR16("  %-0 |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 7], EA_CHAR16("   %- |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 8], EA_CHAR16(" %+#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 9], EA_CHAR16("  %+# |  -123 |  0377 |  0xff |  0XFF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[10], EA_CHAR16("  %+0 |-00123 |000377 |0000ff |0000FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[11], EA_CHAR16("   %+ |  -123 |   377 |    ff |    FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[12], EA_CHAR16("  %#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[13], EA_CHAR16("   %# |  -123 |  0377 |  0xff |  0XFF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[14], EA_CHAR16("   %0 |-00123 |000377 |0000ff |0000FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[15], EA_CHAR16("    % |  -123 |   377 |    ff |    FF |4294967295 |")) == 0);
+
+	}
+
+
+	{
+		char16_t        buffer[256];
+		const char16_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR16("%e"), 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR16("1.234568e+06"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%f"), 1234567.8);
+		pExpected = EA_CHAR16("1234567.800000");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%g"), 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR16("1.23457e+06");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%g"), 123.456);
+		pExpected = EA_CHAR16("123.456");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%g"), 1000000.0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR16("1e+06"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%g"), 10.0);
+		pExpected = EA_CHAR16("10");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%g"), 0.02);
+		pExpected = EA_CHAR16("0.02");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+	}
+
+
+	{   // Test the ' extension, that prints numbers with a thousands separator.
+		char16_t        buffer[64];
+		const char16_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR16("%'u"), 123456789);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("123,456,789")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'d"), -123456789);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("-123,456,789")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I8u"), 123);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("123")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I16u"), 12345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("12,345")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I16d"), -12345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("-12,345")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I32u"), 12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I32d"), -12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("-12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%20I32d"), -12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("           -12345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'20I32d"), -12345678); // Verify that the , chars count towards the field width.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("         -12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I32x"), 0x12345678);  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("12345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I64u"), UINT64_C(1234999995678));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("1,234,999,995,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I64d"), INT64_C(-1234599999678));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("-1,234,599,999,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'I64x"), UINT64_C(0x1234567812345678));  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("1234567812345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'f"), 123456.234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("123,456.234000")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'e"), 1234567.8);  // ' has no effect on %e formatting.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("1.234568e+06")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%'g"), 1234.54);   // In some cases %g acts like %f.
+		pExpected = EA_CHAR16("1,234.54");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%'g"), 1234567.8);   // In some cases %g acts like %f.
+		pExpected = EA_CHAR16("1.23457e+06");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+	}
+
+	{
+		char16_t buffer[256];
+
+		Sprintf(buffer, EA_CHAR16("%hhu"), UCHAR_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("1")) == 0); // VC++ fails this, as it doesn't implement the C99 standard %hh modifier.
+
+		Sprintf(buffer, EA_CHAR16("%hu"), USHRT_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("1")) == 0);
+	}
+
+
+	{
+		char16_t        buffer[128];
+		const char16_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR16("%5.s"), EA_WCHAR("xyz"));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("     ")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%5.f"), 33.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("   33")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%8.e"), 33.3e7);                 // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("   3e+08")) == 0);
+
+		Sprintf(buffer, EA_CHAR16("%8.E"), 33.3e7);                 // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("   3E+08")) == 0); 
+
+		Sprintf(buffer, EA_CHAR16("%.g"), 33.3);                    // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR16("3e+01"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR16("%.G"), 33.3);                    // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR16("3E+01"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I16s\n   Actual:   %I16s", pExpected, buffer); 
+	}
+
+
+	{
+		char16_t buffer[128];
+		int      precision;
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR16("%.*g"), precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("3")) == 0);
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR16("%.*G"), precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("3")) == 0);
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR16("%7.*G"), precision, 3.33);
+		EATEST_VERIFY(Strcmp(buffer,EA_CHAR16("      3")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR16("%04.*o"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 041")) == 0);
+
+		precision = 7;
+		Sprintf(buffer, EA_CHAR16("%09.*u"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16("  0000033")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR16("%04.*x"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 021")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR16("%04.*X"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR16(" 021")) == 0);
+	}
+
+	{
+		static const int kSourceSize = 1024 * 5;
+		static const int kOutputSize = kSourceSize + 100;
+		char8_t value[kSourceSize];
+		char16_t destination[kOutputSize];
+		char16_t comparison[kOutputSize];
+
+		for(int i = 0; i < kSourceSize - 1; ++i)
+		{
+			value[i] = '0' + (i % 10);
+			comparison[i] = '0' + (i % 10);
+		}
+		value[kSourceSize - 1] = 0;
+		comparison[kSourceSize - 1] = 0;
+
+		EA::StdC::Snprintf(destination, kOutputSize, EA_CHAR16("%I8s"), value);
+		EATEST_VERIFY(Strcmp(destination, comparison) == 0);
+
+		EA::StdC::Snprintf(destination, kOutputSize, EA_CHAR16("%.10I8s"), value);
+		EATEST_VERIFY(Strcmp(destination, EA_CHAR16("0123456789")) == 0);
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestSprintf32(int unused = 0, ...)
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	// int Snprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char32_t sn18[32];
+		Snprintf(sn18, EAArrayCount(sn18), EA_CHAR32("%5s%-4d%03i"), EA_WCHAR("abc"), -12, 3);
+		EATEST_VERIFY(!Strcmp(EA_CHAR32("  abc-12 003"), sn18));
+		Snprintf(sn18, EAArrayCount(sn18), EA_CHAR32("%.2f"), 3.1415);
+		EATEST_VERIFY(!Strcmp(EA_CHAR32("3.14"), sn18));
+	}
+
+	// int Vsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
+	{
+		char32_t sn18[32];
+		TestCRTVsnprintf(sn18, EAArrayCount(sn18), EA_CHAR32("%5s%-5d%04i"), EA_WCHAR("abc"), -12, 3);
+		EATEST_VERIFY(!Strcmp(EA_CHAR32("  abc-12  0003"), sn18));
+		TestCRTVsnprintf(sn18, EAArrayCount(sn18), EA_CHAR32("%.2f"), 3.1415);
+		EATEST_VERIFY(!Strcmp(EA_CHAR32("3.14"), sn18));
+	}
+
+	#if EASTDC_VSNPRINTF8_ENABLED
+		{
+			char32_t sn18[32];
+			TestCRTVsnprintf32(sn18, EAArrayCount(sn18), EA_CHAR32("%5s%-5d%04i"), EA_WCHAR("abc"), -12, 3);
+			EATEST_VERIFY(!Strcmp(EA_CHAR32("  abc-12  0003"), sn18));
+			TestCRTVsnprintf32(sn18, EAArrayCount(sn18), EA_CHAR32("%.2f"), 3.1415);
+			EATEST_VERIFY(!Strcmp(EA_CHAR32("3.14"), sn18));
+		}
+	#endif
+
+	// int Vscprintf(const char_t* pFormat, va_list arguments);
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		int result = Vscprintf(EA_CHAR32("abc"), arguments);       
+		EATEST_VERIFY(result == 3);
+
+		va_end(arguments);
+	}
+
+	// template <typename String>
+	// int StringVcprintf(String& s, const char8_t* EA_RESTRICT pFormat, va_list arguments)
+	{
+		va_list arguments;
+		va_start(arguments, unused);
+
+		eastl::string32 s32;
+		int result = StringVcprintf(s32, EA_CHAR32("hello"), arguments);
+		EATEST_VERIFY((result == 5) && (s32 == EA_CHAR32("hello")));
+
+		va_end(arguments);
+	}
+
+	// template <typename String> 
+	// int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...)
+	{
+		eastl::string32 s32;
+		int result = StringPrintf(s32, EA_CHAR32("%s"), EA_WCHAR("hello"));
+		EATEST_VERIFY((result == 5) && (s32 == EA_CHAR32("hello")));
+	}
+
+	{
+		char32_t buffer[32];
+		const int kHexValue = 0x12;
+
+		Sprintf(buffer, EA_CHAR32("%.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%04x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%4.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%04.4x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%4.3x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%04.3x"), kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%.*x"), 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%0*x"), 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%*.*x"), 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%0*.*x"), 4, 4, kHexValue);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0012")) == 0);
+	}
+
+
+	{
+		char32_t buffer[96];
+
+		Sprintf(buffer, EA_CHAR32("decimal negative: \"%d\"\n"), -2345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("decimal negative: \"-2345\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("octal negative: \"%o\"\n"), -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("octal negative: \"37777773327\"\n")) == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("octal negative: \"1777777777777777773327\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("hex negative: \"%x\"\n"), -2345);
+		if(sizeof(int) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("hex negative: \"fffff6d7\"\n")) == 0);
+		else if(sizeof(int) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("hex negative: \"fffffffffffff6d7\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("long decimal number: \"%ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("long decimal number: \"-123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("long octal negative: \"%lo\"\n"), -2345L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("long octal negative: \"37777773327\"\n")) == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("long octal negative: \"1777777777777777773327\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("long unsigned decimal number: \"%lu\"\n"), -123456L);
+		if(sizeof(long) == (4 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("long unsigned decimal number: \"4294843840\"\n")) == 0);
+		else if(sizeof(long) == (8 + (__FILE__[0] / 100000))) // Trickery here to avoid compiler warnings.
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("long unsigned decimal number: \"18446744073709428160\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("zero-padded LDN: \"%010ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("zero-padded LDN: \"-000123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("left-adjusted ZLDN: \"%-010ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("left-adjusted ZLDN: \"-123456   \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("space-padded LDN: \"%10ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("space-padded LDN: \"   -123456\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("left-adjusted SLDN: \"%-10ld\"\n"), -123456L);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("left-adjusted SLDN: \"-123456   \"\n")) == 0);
+	}
+
+
+	{
+		char32_t  buffer[1024];
+		wchar_t   str1[64]; Strlcpy(str1, EA_WCHAR("abc de"), EAArrayCount(str1)); // Can't do str1[64] = EA_CHAR32("abc de") because some compilers don't support 32 bit string literals.
+		wchar_t   str2[64]; Strlcpy(str2, EA_WCHAR("abd def ghi jkl mno pqr stu vwz yz."), EAArrayCount(str2));
+
+		// The C99 standard specifies that leading zeros only put zeroes in front of numerical types. Spaces for others.
+		Sprintf(buffer, EA_CHAR32("zero-padded string: \"%010s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("zero-padded string: \"    abc de\"\n")) == 0); // VC++ fails this, as it puts zeroes in front.
+
+		Sprintf(buffer, EA_CHAR32("left-adjusted Z string: \"%-010s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("left-adjusted Z string: \"abc de    \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("space-padded string: \"%10s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("space-padded string: \"    abc de\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("left-adjusted S string: \"%-10s\"\n"), str1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("left-adjusted S string: \"abc de    \"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("limited string: \"%.22s\"\n"), str2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("limited string: \"abd def ghi jkl mno pq\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("null string: \"%s\"\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("null string: \"(null)\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%10s\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("    (null)\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%-10s\n"), (wchar_t*)NULL);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("(null)    \n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%*s%*s%*s"), -1, EA_WCHAR("one"), -20, EA_WCHAR("two"), -30, EA_WCHAR("three"));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("onetwo                 three                         ")) == 0);
+
+		int i;
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, EA_CHAR32("x%1000ls"), EA_WCHAR(" "));
+		EATEST_VERIFY(buffer[0] == 'x');
+		for(i = 0; i < 1000; i++)
+		{
+			if(buffer[1 + i] != ' ')
+				break;
+		}
+		if(i != 1000)
+			EATEST_VERIFY(i == 1000);
+		else
+			EATEST_VERIFY(buffer[1 + 1000] == 0);
+	}
+
+
+	{   // String tests
+		// We accept %hc, %c, %lc, %I8c, %I16c, %I32c (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hC, %C, %lC, %I8C, %I16C, %I32C (regular, wide,    wide, char8_t, char16_t, char32_t)
+		// We accept %hs, %s, %ls, %I8s, %I16s, %I32s (regular, regular, wide, char8_t, char16_t, char32_t)
+		// We accept %hS, %S, %lS, %I8s, %I16s, %I32s (regular, wide,    wide, char8_t, char16_t, char32_t)
+
+		char32_t buffer[32];
+		char8_t  dStr8[2]  = { 'd', 0 };
+		char16_t eStr16[2] = { 'e', 0 };
+		char32_t fStr32[2] = { 'f', 0 };
+
+		#if EASPRINTF_MS_STYLE_S_FORMAT // Microsoft style means that the meanings of S and s are reversed for non-char8_t Sprintf.
+			Sprintf(buffer, EA_CHAR32("%hc %c %lc %I8c %I16c %I32c"), 'a', EA_WCHAR('b'), EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hC %C %lC %I8C %I16C %I32C"), 'a', 'b',           EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hs %s %ls %I8s %I16s %I32s"), "a", EA_WCHAR("b"), EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hS %S %lS %I8S %I16S %I32S"), "a", "b",           EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+		#else
+			Sprintf(buffer, EA_CHAR32("%hc %c %lc %I8c %I16c %I32c"), 'a', 'b',           EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hC %C %lC %I8C %I16C %I32C"), 'a', EA_WCHAR('b'), EA_WCHAR('c'), (char8_t)'d', (char16_t)'e', (char32_t)'f');
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f)") == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hs %s %ls %I8s %I16s %I32s"), "a", "b",           EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+
+			Sprintf(buffer, EA_CHAR32("%hS %S %lS %I8S %I16S %I32S"), "a", EA_WCHAR("b"), EA_WCHAR("c"), dStr8,        eStr16,         fStr32);
+			EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("a b c d e f")) == 0);
+		#endif
+	}
+
+	{
+		char32_t buffer[384];
+		int i;
+
+		Sprintf(buffer, EA_CHAR32("e-style >= 1: \"%e\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("e-style >= 1: \"1.234000e+01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("e-style >= .1: \"%e\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("e-style >= .1: \"1.234000e-01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("e-style < .1: \"%e\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("e-style < .1: \"1.234000e-03\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("e-style big: \"%.60e\"\n"), 1e20);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("e-style big: \"1.000000000000000000000000000000000000000000000000000000000000e+20\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("e-style == .1: \"%e\"\n"), 0.1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("e-style == .1: \"1.000000e-01\"\n")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("f-style >= 1: \"%f\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("f-style >= 1: \"12.340000\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("f-style >= .1: \"%f\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("f-style >= .1: \"0.123400\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("f-style < .1: \"%f\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("f-style < .1: \"0.001234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("g-style >= 1: \"%g\"\n"), 12.34);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("g-style >= 1: \"12.34\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("g-style >= .1: \"%g\"\n"), 0.1234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("g-style >= .1: \"0.1234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("g-style < .1: \"%g\"\n"), 0.001234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("g-style < .1: \"0.001234\"\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("g-style big: \"%.60g\"\n"), 1e20);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("g-style big: \"100000000000000000000\"\n")) == 0);
+
+		//Sprintf(buffer, EA_CHAR32("%#.4g\n"), 0.0); // The C99 committee has decided in a defect analysis that this is how it should work.
+		//EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32(" %6.5f\n"), .099999999860301614);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 0.10000\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32(" %6.5f\n"), .1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 0.10000\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("x%5.4fx\n"), .5);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("x0.5000x\n")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%#03x\n"), 1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("0x1\n")) == 0);
+
+
+		memset(buffer, '_', sizeof(buffer));
+		Sprintf(buffer, EA_CHAR32("%.300f"), 1.0);
+		EATEST_VERIFY((buffer[0] == '1') && (buffer[1] == '.'));
+		for(i = 0; i < 300; i++)
+		{
+			if(buffer[2 + i] != '0')
+				break;
+		}
+		if(i != 300)
+			EATEST_VERIFY(i == 300);
+		else
+			EATEST_VERIFY(buffer[2 + 300] == 0);
+
+
+		double d = static_cast<double>(FLT_MIN);    // We are intentionally using FLT_MIN and not DBL_MIN.
+		d /= 2.0;
+		Sprintf(buffer, EA_CHAR32("%.17e"), d);     // It should be something like 5.87747175411143___e-39 where count and values of the _ digits vary by hardware.
+		buffer[16] = buffer[17] = buffer[18] = '_'; // Replace the uncertain digits with '_' characters, as they are system-dependent.
+		
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("5.87747175411143___e-39")) == 0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+
+		Sprintf(buffer, EA_CHAR32("%15.5e\n"), 4.9406564584124654e-307);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("   4.94066e-307\n")) == 0);
+	}
+
+
+	{
+		char32_t        buffer[96];
+		const char32_t* pExpected;
+
+		// VC++ sprintf would fail these tests, as the Standard says to print no more 
+		// than 2 unless necessary, yet VC++ sprintf prints 3 digits exponents.
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 0.0, 0.0, 0.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("|      0.0000|  0.0000e+00|           0|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 1.0, 1.0, 1.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("|      1.0000|  1.0000e+00|           1|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), -1.0, -1.0, -1.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("|     -1.0000| -1.0000e+00|          -1|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 100.0, 100.0, 100.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("|    100.0000|  1.0000e+02|         100|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 1000.0, 1000.0, 1000.0);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("|   1000.0000|  1.0000e+03|        1000|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 10000.0, 10000.0, 10000.0);
+		EATEST_VERIFY(Strcmp(buffer,EA_CHAR32("|  10000.0000|  1.0000e+04|       1e+04|")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 12346.0, 12346.0, 12346.0);
+		pExpected = EA_CHAR32("|  12346.0000|  1.2346e+04|   1.235e+04|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 100000.0, 100000.0, 100000.0);
+		pExpected = EA_CHAR32("| 100000.0000|  1.0000e+05|       1e+05|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("|%12.4f|%12.4e|%12.4g|"), 123467.0, 123467.0, 123467.0);
+		pExpected = EA_CHAR32("| 123467.0000|  1.2347e+05|   1.235e+05|");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+	}
+
+
+	{
+		char32_t buffer[96];
+
+		// Verify that snprintf follows the C99 convention of returning the number of characters
+		// required. This is as opposed to the non-standard way that some libraries just return 
+		// -1 if the buffer isn't big enough.
+
+		const int kBuf1Capacity = 20;
+		wchar_t   buf1[kBuf1Capacity];
+		int       n1 = Snprintf(buf1, kBuf1Capacity, EA_WCHAR("%30I32s"), EA_CHAR32("foo"));
+		Sprintf(buffer, EA_CHAR32("snprintf(\"%%30s\", \"foo\") == %d, \"%.*s\"\n"), n1, kBuf1Capacity, buf1);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("snprintf(\"%30s\", \"foo\") == 30, \"                   \"\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf2Capacity = 512;
+		char32_t  buf2[kBuf2Capacity];
+		int       n2 = Snprintf(buf2, kBuf2Capacity, EA_CHAR32("%.1000u"), 10);
+		Sprintf(buffer, EA_CHAR32("snprintf(\"%%.1000u\", 10) == %d\n"), n2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("snprintf(\"%.1000u\", 10) == 1000\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		const int kBuf3Capacity = 512;
+		char32_t  buf3[kBuf3Capacity];
+		char32_t* pString = new char32_t[100000];
+		memset(pString, '_', 100000 * sizeof(char32_t));
+		pString[100000 - 1] = 0;
+		int n3 = Snprintf(buf3, kBuf2Capacity, EA_CHAR32("%I32s"), pString);
+		Sprintf(buffer, EA_CHAR32("snprintf(\"%%s\", pString) == %d\n"), n3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("snprintf(\"%s\", pString) == 99999\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+		delete[] pString;
+
+		int n4 = Snprintf(NULL, 0, EA_CHAR32("%I32s"), EA_CHAR32("abc"));
+		Sprintf(buffer, EA_CHAR32("snprintf(NULL, \"abc\") == %d\n"), n4);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("snprintf(NULL, \"abc\") == 3\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+
+		int n5 = Snprintf(NULL, 100, EA_CHAR32("%I32s"), EA_CHAR32("abc"));
+		Sprintf(buffer, EA_CHAR32("snprintf(NULL, \"abc\") == %d\n"), n5);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("snprintf(NULL, \"abc\") == 3\n")) == 0); // VC++ fails this, as it's version of snprintf doesn't use C99 standard snprintf return value conventions.
+	}
+
+
+	{
+		char32_t buffer[16][64];
+
+		int n = 0, i, j, k, m;
+
+		for(i = 0; i < 2; i++)
+		{
+			for(j = 0; j < 2; j++)
+			{
+				for(k = 0; k < 2; k++)
+				{
+					for(m = 0; m < 2; m++)
+					{
+						wchar_t  prefix[7];
+						char32_t format[96];
+
+						Strcpy(prefix, EA_WCHAR("%"));
+						if(i == 0)
+							Strcat(prefix, EA_WCHAR("-"));
+						if(j == 0)
+							Strcat(prefix, EA_WCHAR("+"));
+						if(k == 0)
+							Strcat(prefix, EA_WCHAR("#"));
+						if(m == 0)
+							Strcat(prefix, EA_WCHAR("0"));
+
+						#define DEC -123
+						#define INT  255
+						#define UNS (~0)
+
+						Sprintf(format, EA_CHAR32("%%5s |%s6d |%s6o |%s6x |%s6X |%s6u |"), prefix, prefix, prefix, prefix, prefix);
+						Sprintf(buffer[n], format, prefix, DEC, INT, INT, INT, UNS);
+						n++;
+					}
+				}
+			}
+		}
+
+		EATEST_VERIFY(Strcmp(buffer[ 0], EA_CHAR32("%-+#0 |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 1], EA_CHAR32(" %-+# |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 2], EA_CHAR32(" %-+0 |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 3], EA_CHAR32("  %-+ |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 4], EA_CHAR32(" %-#0 |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 5], EA_CHAR32("  %-# |-123   |0377   |0xff   |0XFF   |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 6], EA_CHAR32("  %-0 |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 7], EA_CHAR32("   %- |-123   |377    |ff     |FF     |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 8], EA_CHAR32(" %+#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[ 9], EA_CHAR32("  %+# |  -123 |  0377 |  0xff |  0XFF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[10], EA_CHAR32("  %+0 |-00123 |000377 |0000ff |0000FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[11], EA_CHAR32("   %+ |  -123 |   377 |    ff |    FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[12], EA_CHAR32("  %#0 |-00123 |000377 |0x00ff |0X00FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[13], EA_CHAR32("   %# |  -123 |  0377 |  0xff |  0XFF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[14], EA_CHAR32("   %0 |-00123 |000377 |0000ff |0000FF |4294967295 |")) == 0);
+		EATEST_VERIFY(Strcmp(buffer[15], EA_CHAR32("    % |  -123 |   377 |    ff |    FF |4294967295 |")) == 0);
+
+	}
+
+
+	{
+		char32_t        buffer[256];
+		const char32_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR32("%e"), 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR32("1.234568e+06"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%f"), 1234567.8);
+		pExpected = EA_CHAR32("1234567.800000");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%g"), 1234567.8); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR32("1.23457e+06");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%g"), 123.456);
+		pExpected = EA_CHAR32("123.456");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%g"), 1000000.0); // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR32("1e+06"); 
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%g"), 10.0);
+		pExpected = EA_CHAR32("10");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%g"), 0.02);
+		pExpected = EA_CHAR32("0.02");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+	}
+
+
+	{   // Test the ' extension, which cases numbers to be printed with a thousands separator.
+		char32_t        buffer[64];
+		const char32_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR32("%'u"), 123456789);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("123,456,789")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'d"), -123456789);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("-123,456,789")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I8u"), 123);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("123")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I16u"), 12345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("12,345")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I16d"), -12345);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("-12,345")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I32u"), 12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I32d"), -12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("-12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%20I32d"), -12345678);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("           -12345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'20I32d"), -12345678); // Verify that the , chars count towards the field width.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("         -12,345,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I32x"), 0x12345678);  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("12345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I64u"), UINT64_C(1234999995678));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("1,234,999,995,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I64d"), INT64_C(-1234599999678));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("-1,234,599,999,678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'I64x"), UINT64_C(0x1234567812345678));  // ' has no effect on hex formatting.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("1234567812345678")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'f"), 123456.234);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("123,456.234000")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%'e"), 1234567.8);   // ' has no effect on %e formatting.
+		pExpected = EA_CHAR32("1.234568e+06");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%'g"), 1234.54);     // In some cases %g acts like %f.
+		pExpected = EA_CHAR32("1,234.54");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%'g"), 1234567.8);   // In some cases %g acts like %f.
+		pExpected = EA_CHAR32("1.23457e+06");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+	}
+
+	{
+		char32_t buffer[256];
+
+		Sprintf(buffer, EA_CHAR32("%hhu"), UCHAR_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("1")) == 0); // VC++ fails this, as it doesn't implement the C99 standard %hh modifier.
+
+		Sprintf(buffer, EA_CHAR32("%hu"), USHRT_MAX + 2);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("1")) == 0);
+	}
+
+
+	{
+		char32_t        buffer[128];
+		const char32_t* pExpected;
+
+		Sprintf(buffer, EA_CHAR32("%5.s"), EA_WCHAR("xyz"));
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("     ")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%5.f"), 33.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("   33")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%8.e"), 33.3e7);                 // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("   3e+08")) == 0); 
+
+		Sprintf(buffer, EA_CHAR32("%8.E"), 33.3e7);                 // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("   3E+08")) == 0);
+
+		Sprintf(buffer, EA_CHAR32("%.g"), 33.3);                    // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR32("3e+01");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+
+		Sprintf(buffer, EA_CHAR32("%.G"), 33.3);                    // VC++ sprintf would fail this, as it uses 3 exponent digits, but the Standard says to print no more than 2 unless necessary.
+		pExpected = EA_CHAR32("3E+01");
+		EATEST_VERIFY_F(Strcmp(buffer, pExpected) == 0, "\n   Expected: %I32s\n   Actual:   %I32s", pExpected, buffer); 
+	}
+
+
+	{
+		char32_t buffer[128];
+		int      precision;
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR32("%.*g"), precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("3")) == 0);
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR32("%.*G"), precision, 3.3);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("3")) == 0);
+
+		precision = 0;
+		Sprintf(buffer, EA_CHAR32("%7.*G"), precision, 3.33);
+		EATEST_VERIFY(Strcmp(buffer,EA_CHAR32("      3")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR32("%04.*o"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 041")) == 0);
+
+		precision = 7;
+		Sprintf(buffer, EA_CHAR32("%09.*u"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32("  0000033")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR32("%04.*x"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 021")) == 0);
+
+		precision = 3;
+		Sprintf(buffer, EA_CHAR32("%04.*X"), precision, 33);
+		EATEST_VERIFY(Strcmp(buffer, EA_CHAR32(" 021")) == 0);
+	}
+
+	{
+		static const int kSourceSize = 1024 * 5;
+		static const int kOutputSize = kSourceSize + 100;
+		char8_t value[kSourceSize];
+		char32_t destination[kOutputSize];
+		char32_t comparison[kOutputSize];
+
+		for(int i = 0; i < kSourceSize - 1; ++i)
+		{
+			value[i] = '0' + (i % 10);
+			comparison[i] = '0' + (i % 10);
+		}
+		value[kSourceSize - 1] = 0;
+		comparison[kSourceSize - 1] = 0;
+
+		EA::StdC::Snprintf(destination, kOutputSize, EA_CHAR32("%I8s"), value);
+		EATEST_VERIFY(Strcmp(destination, comparison) == 0);
+
+		EA::StdC::Snprintf(destination, kOutputSize, EA_CHAR32("%.10I8s"), value);
+		EATEST_VERIFY(Strcmp(destination, EA_CHAR32("0123456789")) == 0);
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestDrintf8()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		int result;
+
+		Dprintf("Begin Dprintf (debug output printf) testing...\n");
+
+		// EASTDC_API int Vdprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+		// EASTDC_API int Dprintf(const char8_t* EA_RESTRICT pFormat, ...);
+		eastl::string8 sBuffer;
+		for(eastl_size_t i = 0; i < 1024; i++) // This size should be > than the size of the buffer(s) used in PlatformLogWriter8, though those buffer sizes aren't publicly exposed.
+			sBuffer.push_back('a' + (char8_t)(i % 26));
+
+		EA::UnitTest::Rand rand((uint32_t)EA::StdC::GetTime());
+
+		for(eastl_size_t i = 0; i < 1024; i += rand.RandRange(1, 100))
+		{
+			char formatBuffer[32]; // Something like "%.52s"
+			Sprintf(formatBuffer, "%%.%ds\n", i); // Use \n because some debug output systems might otherwise get overwhelmed.
+			result = Dprintf(formatBuffer, sBuffer.c_str());
+			EATEST_VERIFY(result > 0);
+		}
+
+		#if EASTDC_PRINTF_DEBUG_ENABLED
+			// EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, ...);
+			// EASTDC_API int Printf(const char8_t* EA_RESTRICT pFormat, ...);
+			// EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char8_t* EA_RESTRICT pFormat, va_list arguments);
+			// EASTDC_API int Vprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
+			result = Printf("%s", "Printf test (EASTDC_PRINTF_DEBUG_ENABLED).\n");
+			EATEST_VERIFY(result > 0);
+
+			result = Fprintf(stdout, "%s", "Printf test (EASTDC_PRINTF_DEBUG_ENABLED).\n");
+			EATEST_VERIFY(result > 0);
+		#endif
+
+		Dprintf("End Dprintf (debug output printf) testing.\n");
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestOsprintf8()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		char8_t buffer[32];
+		int     result;
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, "%0:d", (int)0);
+		EATEST_VERIFY((result == 1) && Strcmp(buffer, "0") == 0);
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, "%2:1.0f %3:d %1:c", (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, "1 2 3") == 0);
+
+		// Test 0-based ordering
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, "%1:1.0f %2:d %0:c", (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, "1 2 3") == 0);
+
+		// Test format limit (currently 21 spans)
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, " %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d        ", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+		EATEST_VERIFY((result == 28) && Strcmp(buffer, " 0 1 2 3 4 5 6 7 8 9        " ) == 0);
+
+		// Test format overflow
+		// Tests below are disabled by default because they trigger runtime asserts which break auto-testing.
+		// They can be enabled by running these tests in interactive mode, in which case you'll have to manually
+		// dismiss assertion failures for these.
+		if(EA::UnitTest::GetInteractive())
+		{
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, " %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d %9:d ", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, "%00000000000000000000:f", 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, "%0:000000000000000000f", 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, "%0:.000000000000000000f", 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, "%000000000000000000:000000000000000000.000000000000000000f", 0.f);
+			EATEST_VERIFY(result == -1);
+		}
+
+		// Test OVsnprintf capacity limits
+		memset(buffer, 0, sizeof(buffer));
+		result = OSnprintf(buffer, 0, "%2:1.0f %3:d %1:c", (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && (buffer[0] == 0));
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestOsprintf16()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		char16_t buffer[128];
+		int      result;
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR16("%0:d"), (int)0);
+		EATEST_VERIFY((result == 1) && Strcmp(buffer, EA_CHAR16("0")) == 0);
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR16("%2:1.0f %3:d %1:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, EA_CHAR16("1 2 3")) == 0);
+
+		// Test 0-based ordering
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR16("%1:1.0f %2:d %0:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, EA_CHAR16("1 2 3")) == 0);
+
+		// Test format limit (currently 21 spans)
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR16(" %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d        "), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+		EATEST_VERIFY((result == 28) && Strcmp(buffer, EA_CHAR16(" 0 1 2 3 4 5 6 7 8 9        ")) == 0);
+
+		// Test format overflow
+		// Tests below are disabled by default because they trigger runtime asserts which break auto-testing.
+		// They can be enabled by running these tests in interactive mode, in which case you'll have to manually
+		// dismiss assertion failures for these.
+		if(EA::UnitTest::GetInteractive())
+		{
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR16(" %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d %9:d "), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR16("%00000000000000000000:f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR16("%0:000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR16("%0:.000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR16("%000000000000000000:000000000000000000.000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+		}
+
+		// Test OVsnprintf capacity limits
+		memset(buffer, 0, sizeof(buffer));
+		result = OSnprintf(buffer, 0, EA_CHAR16("%2:1.0f %3:d %1:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && (buffer[0] == 0));
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestOsprintf32()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		char32_t buffer[128];
+		int      result;
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR32("%0:d"), (int)0);
+		EATEST_VERIFY((result == 1) && Strcmp(buffer, EA_CHAR32("0")) == 0);
+
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR32("%2:1.0f %3:d %1:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, EA_CHAR32("1 2 3")) == 0);
+
+		// Test 0-based ordering
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR32("%1:1.0f %2:d %0:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && Strcmp(buffer, EA_CHAR32("1 2 3")) == 0);
+
+		// Test format limit (currently 21 spans)
+		memset(buffer, 0, sizeof(buffer));
+		result = OSprintf(buffer, EA_CHAR32(" %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d        "), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+		EATEST_VERIFY((result == 28) && Strcmp(buffer, EA_CHAR32(" 0 1 2 3 4 5 6 7 8 9        ")) == 0);
+
+		// Test format overflow
+		// Tests below are disabled by default because they trigger runtime asserts which break auto-testing.
+		// They can be enabled by running these tests in interactive mode, in which case you'll have to manually
+		// dismiss assertion failures for these.
+		if(EA::UnitTest::GetInteractive())
+		{
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR32(" %0:d %1:d %2:d %3:d %4:d %5:d %6:d %7:d %8:d %9:d %9:d "), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR32("%00000000000000000000:f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR32("%0:000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR32("%0:.000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+
+			memset(buffer, 0, sizeof(buffer));
+			result = OSprintf(buffer, EA_CHAR32("%000000000000000000:000000000000000000.000000000000000000f"), 0.f);
+			EATEST_VERIFY(result == -1);
+		}
+
+		// Test OVsnprintf capacity limits
+		memset(buffer, 0, sizeof(buffer));
+		result = OSnprintf(buffer, 0, EA_CHAR32("%2:1.0f %3:d %1:c"), (char)'3', (float)1.f, (int)2);
+		EATEST_VERIFY((result == 5) && (buffer[0] == 0));
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestDeprecated
+///////////////////////////////////////////////////////////////////////////////
+
+static int StringWriterOld8(const char8_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8)
+{
+	eastl::string8* pString8 = static_cast<eastl::string8*>(pContext8);
+	pString8->append(pData, (eastl_size_t)nCount);
+	return (int)nCount;
+}
+
+static int StringWriterOld16(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16)
+{
+	eastl::string16* pString16 = static_cast<eastl::string16*>(pContext16);
+	pString16->append(pData, (eastl_size_t)nCount);
+	return (int)nCount;
+}
+
+static int TestDeprecated()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	{
+		eastl::string8  s8;
+		eastl::string16 s16;
+		int             result;
+
+		result = Cprintf(StringWriterOld8,  &s8, "Hello world");
+		EATEST_VERIFY(result == (int)Strlen("Hello world"));
+		EATEST_VERIFY(s8 == "Hello world");
+
+		result = Cprintf(StringWriterOld16, &s16, EA_CHAR16("Hello world"));
+		EATEST_VERIFY(result == (int)Strlen(EA_CHAR16("Hello world")));
+		EATEST_VERIFY(s16 == EA_CHAR16("Hello world"));
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestSprintf
+///////////////////////////////////////////////////////////////////////////////
+
+int TestSprintf()
+{
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestSprintf\n");
+
+	// Regular sprintf
+	nErrorCount += TestSprintf8();
+	nErrorCount += TestSprintf16();
+	nErrorCount += TestSprintf32();
+
+	// Dprintf
+	nErrorCount += TestDrintf8();
+
+	// Ordered sprintf
+	nErrorCount += TestOsprintf8();
+	nErrorCount += TestOsprintf16();
+	nErrorCount += TestOsprintf32();
+
+	// Test deprecated functionality
+	nErrorCount += TestDeprecated();
+
+	return nErrorCount;
+}
+
+

+ 411 - 0
test/source/TestStopwatch.cpp

@@ -0,0 +1,411 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EADateTime.h>
+#include <EATest/EATest.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EAStdC/EAString.h>
+
+#ifdef _MSC_VER
+	#pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#if defined(EA_PLATFORM_WINDOWS)
+	#include <Windows.h>
+#elif defined(EA_PLATFORM_ANDROID)
+	#include <sys/system_properties.h>
+#endif
+
+#ifdef _MSC_VER
+	#pragma warning(pop)
+#endif
+
+// Tests whether an emulator is being used (for Android devices only)
+#ifdef EA_PLATFORM_ANDROID
+	bool IsEmulator()
+	{
+		char buf[PROP_VALUE_MAX];
+		// "goldfish" and "ranchu" are the possible names of the kernel that run on the Android emulator (as of 2018/02/08)
+		int propLen = __system_property_get("ro.hardware", buf);
+		if(propLen != 0 && 
+			(EA::StdC::Strncmp(buf, "goldfish", propLen) == 0 || 
+				EA::StdC::Strncmp(buf, "ranchu", propLen) == 0))
+			return true;
+		return false;
+	}
+#endif
+
+int TestStopwatch()
+{
+	using namespace EA::StdC;
+
+	EA::UnitTest::Report("TestStopwatch\n");
+
+	int       nErrorCount(0);
+	Stopwatch stopwatch(Stopwatch::kUnitsSeconds);
+	Stopwatch stopwatchHelper1(Stopwatch::kUnitsSeconds);
+	Stopwatch stopwatchHelper2(Stopwatch::kUnitsSeconds);
+	uint64_t  nElapsedTime;
+	bool      bResult = true;
+
+	// Call the stopwatch once to make sure the code is loaded, etc.
+	stopwatch.Start();
+	stopwatch.Stop();
+
+	////////////////////////////////////////////////////////////////////////////
+	// Do a simple test of the CPU cycle counting.
+	//
+	// This test will fail on Android when using an Emulator which is what the Build Farm uses
+	#ifdef EA_PLATFORM_ANDROID
+		if(!IsEmulator())
+		{
+	#endif
+			bool bDisableTimingTest = false;
+			#if !EASTDC_VALGRIND_ENABLED
+				bDisableTimingTest = true;
+			#endif
+
+			uint64_t elapsedTimeLimit = 1000;
+
+			EA::UnitTest::SetHighThreadPriority();
+			int nTookTooLongCount(0);
+			for(int i(0); i < 20; i++)
+			{
+				stopwatch.Reset();
+				stopwatch.SetUnits(Stopwatch::kUnitsCycles);
+				stopwatch.Start();
+				stopwatch.Stop();
+				nElapsedTime = stopwatch.GetElapsedTime();
+
+				if( (nElapsedTime > elapsedTimeLimit) && (bDisableTimingTest == false) )
+				{
+					nTookTooLongCount++;
+				}
+			}
+
+			// Until we can get a way to prevent thread context switches during our tests, we allow as much as one to occur during our tests.
+			EATEST_VERIFY(nTookTooLongCount <= 1);
+
+			EA::UnitTest::SetNormalThreadPriority();
+
+	#ifdef EA_PLATFORM_ANDROID
+		}
+	#endif
+	////////////////////////////////////////////////////////////////////////////
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Do a simple test to verify that Stopwatch::GetStopwatchCycle() always 
+	// generates a value greater than before. There is a problem on some 
+	// multi-processing platforms whereby the processors can have different
+	// cpu tick values and the OS moves the process between the given processors,
+	// resulting in one processing giving a different tick count than another.
+	bool bMultiCPUImprecisionPresent = false;
+	
+	{
+		uint64_t       previous = Stopwatch::GetStopwatchCycle();
+		uint64_t       current  = previous;
+		LimitStopwatch limitStopwatch(Stopwatch::kUnitsMilliseconds);
+		limitStopwatch.SetTimeLimit(1000); // 1 second
+
+		while(!limitStopwatch.IsTimeUp() && (current >= previous))
+		{
+			previous = current;
+			current  = Stopwatch::GetStopwatchCycle();
+		}
+
+		if(current < previous) // If the stopwatch appears to have gone backwards...
+		{
+			// We -may- have a multi-processing platform with unsynchronized CPUs.
+			// Many platforms that use CPU-based cycle counting don't have perfectly synchronized
+			// multiple CPUs. Stopwatch doesn't try to account for that, as it's not a problem
+			// that can be solved in a simple fast way. If we aren't dealing with a 
+			// mulitple CPU thing then we may have a true stopwatch rollover situation.
+			// Some platforms have weaker CPU cycle counting systems which roll over
+			// quickly. Such an example is the Playbook platform, and there is little we 
+			// can do about it in the raw GetStopwatchCycle function. Our Stopwatch class
+			// can handle it due to having contextual information.
+
+			// Ideally we would have a good way to tell if this is a multi-CPU thing.
+			// Allow only a tiny amount of imprecision between CPUs.
+			bMultiCPUImprecisionPresent = (current + (current * 100 / 99)) >= previous; // Note by Paul Pedriana: Isn't the math here wrong and the "* 100" shouldn't be there? And even if it was there we still have the problem of that + operation causing current to roll over UINT64_MAX.
+			EATEST_VERIFY(bMultiCPUImprecisionPresent);
+		}
+	}
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Do a test that verifies that the stopwatch will not report a negative 
+	// time and also that each time GetElapsedTime is called, it will 
+	// be >= to the result of the last call to GetElapsedTime.
+	uint64_t nCurrentElapsedTime(0), nPreviousElapsedTime(0);
+	
+	stopwatchHelper1.SetUnits(Stopwatch::kUnitsSeconds);
+	stopwatchHelper1.Restart();
+	stopwatch.SetUnits(Stopwatch::kUnitsCycles);
+	stopwatch.Restart();
+	bResult = true;
+
+	while(bResult && (stopwatchHelper1.GetElapsedTime() < 2))
+	{
+		nCurrentElapsedTime = stopwatch.GetElapsedTime();
+
+		bResult = (((int64_t)nCurrentElapsedTime >= 0) && (nCurrentElapsedTime >= nPreviousElapsedTime));
+		EATEST_VERIFY((int64_t)nCurrentElapsedTime >= 0);
+		EATEST_VERIFY((nCurrentElapsedTime >= nPreviousElapsedTime) || bMultiCPUImprecisionPresent);
+
+		nPreviousElapsedTime = nCurrentElapsedTime;
+
+		// Just idle away some time.
+		char buffer[16];
+		for(int i(0), iEnd(rand() % 10000); i < iEnd; i++)
+			sprintf(buffer, "%d", i); // Just do a little nothing.
+	}
+	////////////////////////////////////////////////////////////////////////////
+
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Do a test whereby we use two stopwatchs (one being EA::StdC::Stopwatch and the other
+	// being a system stopwatch) and see if they yield similar results.
+	if(EA::StdC::GetTimePrecision() < 10000000) // If the GetTime function has precision of at least 10 milliseconds (10000000 ns)...
+	{
+		uint64_t  nStopwatchElapsedTime(0);
+		uint64_t  nClockStartTime(0);
+		uint64_t  nClockEndTime(0);
+		uint64_t  nClockElapsedTime(0);
+		bool      bStopwatchComparisonTestSucceeded(true);
+
+		//EA::UnitTest::Report("A %u second time comparison test will now be run.\n"
+		//         "Press 'esc' to quit this test early.\n", nSecondsForEachTest);
+		//EA::UnitTest::Report("The results are printed as percentages.\n"
+		//         "100.0%% is perfect, while 90%% and 110%% are both bad.\n");
+
+		stopwatchHelper1.SetUnits(Stopwatch::kUnitsSeconds);
+		stopwatchHelper1.Restart();
+		stopwatchHelper2.SetUnits(Stopwatch::kUnitsMilliseconds);
+		stopwatch.Reset();
+		stopwatch.SetUnits(Stopwatch::kUnitsMilliseconds);
+
+		// Now get the current times. We need to somehow prevent a context switch 
+		// here because a context switch bewteen the two stopwatch elapsed time 
+		// calculations will throw off the precision of the comparison.
+		EA::UnitTest::SetHighThreadPriority();
+		stopwatch.Start();
+		nClockStartTime = EA::StdC::GetTime() / 1000000; // Convert from ns to ms.
+		EA::UnitTest::SetNormalThreadPriority();
+		bResult = true;
+
+		while(bResult && (stopwatchHelper1.GetElapsedTime() < 5))
+		{
+			// Sleep for some small random amount of time.
+			EA::UnitTest::ThreadSleepRandom(1000, 2000, false);
+
+			// Now get the current times. We need to somehow prevent a context switch 
+			// here because a context switch bewteen the two stopwatch elapsed time 
+			// calculations will throw off the precision of the comparison.
+			EA::UnitTest::SetHighThreadPriority();
+			nStopwatchElapsedTime = stopwatch.GetElapsedTime();
+			nClockEndTime = EA::StdC::GetTime() / 1000000; // Convert from ns to ms.
+			nClockElapsedTime = nClockEndTime - nClockStartTime;
+			EA::UnitTest::SetNormalThreadPriority();
+
+			//Now compare the results.
+			#if defined(EA_PLATFORM_DESKTOP)
+				const float kAllowedAccuracyMin = 0.96f; // Windows/Unix do multitasking and can delay threads arbitrarily.
+				const float kAllowedAccuracyMax = 1.04f;
+			#else
+				const float kAllowedAccuracyMin = 0.98f;
+				const float kAllowedAccuracyMax = 1.02f;
+			#endif
+
+			const float fAccuracy = (float)nStopwatchElapsedTime / (float)nClockElapsedTime;
+			if((fAccuracy < kAllowedAccuracyMin) || (fAccuracy > kAllowedAccuracyMax))
+			{
+				if(bStopwatchComparisonTestSucceeded)
+				{
+					bStopwatchComparisonTestSucceeded = false;
+					bResult = false;
+					EA::UnitTest::Report("Stopwatch accuracy failure. fAccuracy: %f\n", fAccuracy);
+					EATEST_VERIFY(false);
+				}
+			}
+			//EA::UnitTest::Report("    Clock: %6d ms, Stopwatch: %6d ms. Accuracy: %5.2f%%\n", nClockElapsedTime, nStopwatchElapsedTime, fAccuracy*100.F);
+		}
+	}
+	////////////////////////////////////////////////////////////////////////////
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Test SetElapsedTime functionality.
+	stopwatch.SetUnits(Stopwatch::kUnitsMilliseconds);
+	stopwatch.Restart();
+	// <No time passed since the restart>
+	stopwatch.SetElapsedTime(30000);      // 30 seconds
+	nElapsedTime = stopwatch.GetElapsedTime();
+
+	#if defined(EA_PLATFORM_DESKTOP)
+		const uint64_t kAllowedElapsedTimeMin = 29990;
+		const uint64_t kAllowedElapsedTimeMax = 31000;
+	#else
+		const uint64_t kAllowedElapsedTimeMin = 29990;
+		const uint64_t kAllowedElapsedTimeMax = 30500;
+	#endif
+	EATEST_VERIFY((nElapsedTime >= kAllowedElapsedTimeMin) && (nElapsedTime <= kAllowedElapsedTimeMax));
+
+	stopwatch.SetUnits(Stopwatch::kUnitsMilliseconds);
+	stopwatch.Restart();
+	while(stopwatch.GetElapsedTime() < 3000)
+	{
+		char buffer[32];
+		EA::StdC::Sprintf(buffer, "%I64u", stopwatch.GetElapsedTime()); // Just spin.
+	}
+	stopwatch.SetElapsedTime(30000);      // 30 seconds
+	nElapsedTime = stopwatch.GetElapsedTime();
+	EATEST_VERIFY((nElapsedTime >= kAllowedElapsedTimeMin) && (nElapsedTime <= kAllowedElapsedTimeMax));
+
+	stopwatch.SetUnits(Stopwatch::kUnitsMinutes);
+	stopwatch.Restart();
+	stopwatch.SetElapsedTimeFloat(0.5f); // 30 seconds
+	stopwatch.SetUnits(Stopwatch::kUnitsMilliseconds);
+	nElapsedTime = stopwatch.GetElapsedTime();
+	EATEST_VERIFY((nElapsedTime >= kAllowedElapsedTimeMin) && (nElapsedTime <= kAllowedElapsedTimeMax));
+	////////////////////////////////////////////////////////////////////////////
+
+
+	// This test has been disabled as it's proving to be unreliable due to generating false failures.
+	#if 0
+		////////////////////////////////////////////////////////////////////////////
+		// Test SetTimeLimit functionality.
+		stopwatchHelper1.SetUnits(Stopwatch::kUnitsMilliseconds);
+		stopwatchHelper1.Start();
+
+		LimitStopwatch limitStopwatch(Stopwatch::kUnitsMilliseconds);
+		limitStopwatch.SetTimeLimit(5000); // 5 seconds
+		bResult = true;
+
+		const float lowLimit = -0.5f;   // If the platform is very slow or is pre-empting this thread a lot, we may need to make this value lower,
+		const float highLimit = 5.0f;   // .. and make this value higher.
+
+		while(bResult && !limitStopwatch.IsTimeUp())
+		{
+			const float fTimeRemaining(limitStopwatch.GetTimeRemainingFloat());
+
+			// Verify that when the time is up, the time remaining is close to zero.
+			if(limitStopwatch.IsTimeUp())
+			{
+				if(!((fTimeRemaining >= lowLimit) && (fTimeRemaining < highLimit)))
+				{
+					bResult = false;
+					EA::UnitTest::Report("LimitStopwatch failure. Time remaining: %f\n", fTimeRemaining);
+					EATEST_VERIFY(((fTimeRemaining >= lowLimit) && (fTimeRemaining < highLimit)));
+				}
+			}
+		}
+		nElapsedTime = stopwatchHelper1.GetElapsedTime();
+		EATEST_VERIFY((nElapsedTime >= 5000) || (nElapsedTime <= 5500));
+		////////////////////////////////////////////////////////////////////////////
+	#endif
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Test GetStopwatchCyclesPerUnit / GetCPUCyclesPerUnit
+	//
+	Stopwatch      stopwatchTemp(Stopwatch::kUnitsMilliseconds, true);
+	#if !defined(EASTDC_SWAPPABLE_PROCESS_PLATFORM)
+		const uint64_t nStartStopwatchCycle    = Stopwatch::GetStopwatchCycle();
+		const uint64_t nStartCPUCycle          = Stopwatch::GetCPUCycle();
+	#endif
+	const uint64_t nTestDuration        = 1000; // run test for one second
+	const int nMaxDrift                 = 50;	// a small amount of drift is just noise
+
+	// Loop until the test duration has passed, making sure the float time tracks the integer time.
+	bool bDriftDetectedPrev = false;
+	uint64_t elapsedTime_i = 0;
+	uint64_t elapsedTime_f = 0;
+	while (elapsedTime_i < nTestDuration && elapsedTime_f < nTestDuration)
+	{
+		// measure time and detect drift
+		elapsedTime_i = stopwatchTemp.GetElapsedTime();
+		elapsedTime_f = static_cast<uint64_t>(stopwatchTemp.GetElapsedTimeFloat());
+		int delta = static_cast<int>(elapsedTime_f - elapsedTime_i);
+		bool bDriftDetected = (delta <= -nMaxDrift || nMaxDrift <= delta);
+
+		// fail only once there is significant drift twice in a row.
+		// This avoids false positives due to pre-emption between measurements
+		EATEST_VERIFY(bDriftDetected == false || bDriftDetectedPrev == false);
+		bDriftDetectedPrev = bDriftDetected;
+	}
+	stopwatchTemp.Stop();
+
+	// For platforms where the main process can be swapped out for extended periods of time, 
+	// we disable the test on the buildfarm (via the manifest.xml)
+	#if !defined(EASTDC_SWAPPABLE_PROCESS_PLATFORM)
+		// Make sure the manual GetUnitsPerStopwatchCycle and GetStopwatchCycle yield results that are equivalent to using Start/Stop/GetElapsedTime.
+		uint64_t       nStopwatchCycles        = Stopwatch::GetStopwatchCycle() - nStartStopwatchCycle;
+		const float    fUnitsPerStopwatchCycle = Stopwatch::GetUnitsPerStopwatchCycle(Stopwatch::kUnitsSeconds);
+		const float    fStopwatchTime          = nStopwatchCycles * fUnitsPerStopwatchCycle;
+		const uint64_t nStopwatchTime          = (uint64_t)(int64_t)(fStopwatchTime + 0.49999f); // Add 0.49999f like GetElapsedTime does for integer rounding
+
+		EATEST_VERIFY_F(nStopwatchTime == 1, "TestStopwatch failure: nStopwatchTime: %I64us, expected: 1s", nStopwatchTime);
+	
+	// Make sure the manual GetUnitsPerCPUCycle and GetCPUCycle yield results that are equivalent to using Start/Stop/GetElapsedTime.
+		uint64_t       nCPUCycles        = Stopwatch::GetCPUCycle() - nStartCPUCycle;
+		const float    fUnitsPerCPUCycle = Stopwatch::GetUnitsPerCPUCycle(Stopwatch::kUnitsSeconds);
+		const float    fCPUTime          = nCPUCycles * fUnitsPerCPUCycle;
+		const uint64_t nCPUTime          = (uint64_t)(int64_t)(fCPUTime + 0.49999f);       // Add 0.49999f like GetElapsedTime does for integer rounding
+
+		EATEST_VERIFY_F(nCPUTime == 1, "TestStopwatch failure: nCPUTime: %I64us, expected: 1s", nCPUTime);
+	#endif
+
+	/*{
+		// Regression to exercize the report that PS4 rdtsc is very slow. Conclusion: it is in fact not intrinsically slow and executes similar to XBox One rdtsc. However, something about the user's code may have been causing usage of it to act slowly for them.
+		uint64_t i = 0;
+		uint64_t sum = 0; // We have this only to prevent the GetCPUCycle call from being optimized away.
+		EA::StdC::Stopwatch stopwatchTime(EA::StdC::Stopwatch::kUnitsMilliseconds, true);
+		uint64_t start = EA::StdC::Stopwatch::GetCPUCycle();
+
+		do
+		{
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			sum += EA::StdC::Stopwatch::GetCPUCycle();
+			i += 8;
+		} while(stopwatchTime.GetElapsedTime() < 1000);
+
+		uint64_t stop = EA::StdC::Stopwatch::GetCPUCycle();
+		uint64_t cost = ((stop - start) / i);
+		EA::UnitTest::Report("Stopwatch::GetCPUCycle cost per call: ~%I64u cycles (overestimated due to other stuff we do here). Sum = %I64u (irrelevant).", cost, sum);
+	}*/
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 4304 - 0
test/source/TestString.cpp

@@ -0,0 +1,4304 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EAStdC.h>
+#include <EAStdC/EAString.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EAMemory.h>
+#include <EAStdC/EARandom.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EASTL/string.h>
+#include <EASTL/fixed_string.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+#if defined(_MSC_VER)
+	#pragma warning(push)
+	#pragma warning(disable: 4996) // Function is deprecated.
+#endif
+
+
+static inline bool DoubleEqual(double x1, double x2)
+{
+	double difference = fabs(x1 - x2);
+	double relative   = fabs(difference / x1);
+
+	if (relative < 0.001)
+		return true;
+	else
+	{
+		EA::UnitTest::Report("DoubleEqual Error: %f, %f\n", x1, x2);
+		return false;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CharTraits 
+//
+// Simple template structure to assist with initializing buffers 
+// and knowing the two other character types that aren't the given type
+//
+
+template <typename CharT>
+struct CharTraits
+{};
+
+template <>
+struct CharTraits<char8_t>
+{
+	static char8_t fill_value() { return 0x33; }
+	static void assign(char8_t* buffer, size_t size, char8_t value) { EA::StdC::Memset8(buffer, value, size); }
+	typedef char16_t char1_t;
+	typedef char32_t char2_t;
+};
+
+template <>
+struct CharTraits<char16_t>
+{
+	static char16_t fill_value() { return 0x3344; }
+	static void assign(char16_t* buffer, size_t size, char16_t value) { EA::StdC::Memset16(buffer, value, size); }
+	typedef char8_t char1_t;
+	typedef char32_t char2_t;
+};
+
+template <>
+struct CharTraits<char32_t>
+{
+	static char32_t fill_value() { return 0x33445566; }
+	static void assign(char32_t* buffer, size_t size, char32_t value) { EA::StdC::Memset32(buffer, value, size); }
+	typedef char8_t char1_t;
+	typedef char16_t char2_t;
+};
+
+static const size_t kStrlcpyTestOutSize = 100;
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestPartial 
+//
+// Test the Strlcpy version that supports returning the number of read/written
+// code units.
+//
+template <typename InCharT, typename OutCharT>
+int StrlcpyTestPartial(OutCharT *pOutput, const InCharT* pInput, size_t nOutSize, size_t nInCodeUnits, 
+	size_t nExpectedCodeUnits, bool bWillError)
+{
+	int nErrorCount = 0;
+
+	CharTraits<OutCharT>::assign(pOutput, nOutSize + 1, CharTraits<OutCharT>::fill_value()); // We assume that there is always one extra code unit available
+	size_t nOutCodeUnitsUsed;
+	size_t nInCodeUnitsUsed;
+	bool result = EA::StdC::Strlcpy(pOutput, pInput, nOutSize, nInCodeUnits, nOutCodeUnitsUsed, nInCodeUnitsUsed);
+	if(!result)
+	{
+		EATEST_VERIFY(bWillError);
+		return nErrorCount;
+	}
+	EATEST_VERIFY(!bWillError);
+
+	// If the output has no space, then we can only verify that the fill value trashed
+	if(nOutSize == 0)
+	{
+		EATEST_VERIFY(pOutput[0] == CharTraits<OutCharT>::fill_value());
+		return nErrorCount;
+	}
+
+	// For simple strings, we can do a direct code unit compare.  If the number of code units match,
+	// then assume this is a simple string.
+	if(nInCodeUnitsUsed == nOutCodeUnitsUsed)
+	{
+		for(size_t i = 0; i < nOutCodeUnitsUsed; ++i)
+		{
+			EATEST_VERIFY(static_cast<uint32_t>(pOutput[i]) == static_cast<uint32_t>(pInput[i]));
+		}
+	}
+
+	// Validate null and that fill value wasn't trashed past the end
+	EATEST_VERIFY(pOutput[nOutCodeUnitsUsed] == 0);
+	EATEST_VERIFY(pOutput[nOutCodeUnitsUsed + 1] == CharTraits<OutCharT>::fill_value());
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestWithCount 
+//
+// Test the Strlcpy version supports the number of input code units being passed pInput.
+//
+template <typename InCharT, typename OutCharT>
+int StrlcpyTestWithCount(OutCharT *pOutput, const InCharT* pInput, size_t nOutSize, size_t nInCodeUnits, size_t nExpectedCodeUnits, bool bWillError)
+{
+	int nErrorCount = 0;
+
+	// Invoke the partial version if we have a count
+	if(nInCodeUnits != EA::StdC::kSizeTypeUnset)
+	{
+		nErrorCount += StrlcpyTestPartial(pOutput, pInput, nOutSize, nInCodeUnits, nExpectedCodeUnits, bWillError);
+	}
+
+	CharTraits<OutCharT>::assign(pOutput, nOutSize + 1, CharTraits<OutCharT>::fill_value()); // We assume that there is always one extra code unit available
+	int intResult = EA::StdC::Strlcpy(pOutput, pInput, nOutSize, nInCodeUnits);
+	if(intResult < 0)
+	{
+		EATEST_VERIFY(bWillError);
+		return nErrorCount;
+	}
+	EATEST_VERIFY(!bWillError);
+	EATEST_VERIFY(intResult >= 0 && static_cast<size_t>(intResult) == nExpectedCodeUnits);
+
+	// If the output has no space, then we can only verify that the fill value trashed
+	if(nOutSize == 0)
+	{
+		EATEST_VERIFY(pOutput[0] == CharTraits<OutCharT>::fill_value());
+		return nErrorCount;
+	}
+
+	// Get the actual number of code units supplied
+	if(nInCodeUnits == EA::StdC::kSizeTypeUnset)
+		nInCodeUnits = EA::StdC::Strlen(pInput);
+
+	// The number of output code units doesn't have much to do with the return value when there is
+	// an overrun.
+	size_t outCodeUnits = static_cast<size_t>(intResult);
+	if(outCodeUnits >= nOutSize)
+		outCodeUnits = nOutSize - 1;
+
+	// For simple strings, we can do a direct code unit compare.  If the number of code units match,
+	// then assume this is a simple string.
+	if(nInCodeUnits == nExpectedCodeUnits)
+	{
+		for(size_t i = 0; i < outCodeUnits; ++i)
+		{
+			EATEST_VERIFY(static_cast<uint32_t>(pOutput[i]) == static_cast<uint32_t>(pInput[i]));
+		}
+	}
+
+	// Validate null and that fill value wasn't trashed past the end
+	EATEST_VERIFY(pOutput[outCodeUnits] == 0);
+	EATEST_VERIFY(pOutput[outCodeUnits + 1] == CharTraits<OutCharT>::fill_value());
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestIWithoutCount 
+//
+// Test the Strlcpy version has no input code unit count
+//
+template <typename InCharT, typename OutCharT>
+int StrlcpyTestIWithoutCount(OutCharT *pOutput, const InCharT* pInput, size_t nOutSize, size_t nExpectedCodeUnits, bool bWillError)
+{
+	int nErrorCount = 0;
+
+	CharTraits<OutCharT>::assign(pOutput, nOutSize, CharTraits<OutCharT>::fill_value());
+	int intResult = EA::StdC::Strlcpy(pOutput, pInput, nOutSize);
+	if(bWillError)
+	{
+		EATEST_VERIFY(intResult == -1);
+		return nErrorCount;
+	}
+
+	EATEST_VERIFY(intResult >= 0 && static_cast<size_t>(intResult) == nExpectedCodeUnits);
+	EATEST_VERIFY(static_cast<size_t>(intResult) < nOutSize - 1); // To allow for termination check and overrun check
+
+	size_t nInCodeUnits = EA::StdC::Strlen(pInput);
+
+	if(nInCodeUnits == nExpectedCodeUnits)
+	{
+		for(size_t i = 0; i < nExpectedCodeUnits; ++i)
+		{
+			EATEST_VERIFY(static_cast<uint32_t>(pOutput[i]) == static_cast<uint32_t>(pInput[i]));
+		}
+	}
+	EATEST_VERIFY(pOutput[nExpectedCodeUnits] == 0);
+	EATEST_VERIFY(pOutput[nExpectedCodeUnits + 1] == CharTraits<OutCharT>::fill_value());
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestConversion 
+//
+// Perform all the basic Strlcpy tests.
+//
+template <typename InCharT, typename OutCharT>
+static int StrlcpyTestConversion(OutCharT *output, const InCharT* input, size_t nOutSize, size_t nInCodeUnits, size_t outCodeUnits, bool bWillError)
+{
+	EA_ASSERT(nOutSize <= kStrlcpyTestOutSize);
+	int nErrorCount = 0;
+	OutCharT tempBuff[kStrlcpyTestOutSize+1];
+
+	// Verify that specifying the length works
+	nErrorCount += StrlcpyTestWithCount(output, input, nOutSize, nInCodeUnits, outCodeUnits, bWillError);
+
+	// These tests can only be performed if we aren't working with a short string
+	if(input[nInCodeUnits] == 0)
+	{
+
+		// Verify that not specifying the length works
+		nErrorCount += StrlcpyTestWithCount(tempBuff, input, nOutSize, EA::StdC::kSizeTypeUnset, outCodeUnits, bWillError);
+		EATEST_VERIFY(EA::StdC::Strcmp(output, tempBuff) == 0);
+
+		// Verify that version of the routine that takes no length works
+		nErrorCount += StrlcpyTestIWithoutCount(tempBuff, input, nOutSize, outCodeUnits, bWillError);
+		EATEST_VERIFY(EA::StdC::Strcmp(output, tempBuff) == 0);
+	}
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestConversionCompare 
+//
+// Given two types of strings, convert from one to the other and compare against the expected results
+//
+template <typename InCharT, typename OutCharT>
+static int StrlcpyTestConversionCompare(const OutCharT* pInput, size_t nInCodeUnits, const InCharT* match, size_t matchCodeUnits)
+{
+	EA_ASSERT(nInCodeUnits <= kStrlcpyTestOutSize);
+	int nErrorCount = 0;
+	InCharT tempBuff[kStrlcpyTestOutSize+1];
+
+	nErrorCount += StrlcpyTestConversion(tempBuff, pInput, kStrlcpyTestOutSize, nInCodeUnits, matchCodeUnits, false);
+
+	EATEST_VERIFY(EA::StdC::Strncmp(match, tempBuff, matchCodeUnits) == 0);
+
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestBasics 
+//
+// Perform a basic series of conversion tests to make sure that the edge cases work
+//
+template <typename InCharT, typename OutCharT>
+static int StrlcpyTestBasics(const InCharT* pInput, size_t nInCodeUnits)
+{
+	int nErrorCount = 0;
+	OutCharT tempBuff[kStrlcpyTestOutSize+1];
+
+	// Test including the null in the conversion.
+	nErrorCount += StrlcpyTestWithCount(tempBuff, pInput, kStrlcpyTestOutSize, nInCodeUnits + 1, nInCodeUnits, false);
+
+	// Test not including the last character
+	if(nInCodeUnits > 0)
+	{
+		nErrorCount += StrlcpyTestWithCount(tempBuff, pInput, kStrlcpyTestOutSize, nInCodeUnits - 1, nInCodeUnits - 1, false);
+	}
+
+	// Test including no string at all
+	nErrorCount += StrlcpyTestWithCount(tempBuff, pInput, kStrlcpyTestOutSize, 0, 0, false);
+
+	// Test including a short buffer
+	if (nInCodeUnits > 0)
+	{
+		nErrorCount += StrlcpyTestWithCount(tempBuff, pInput, nInCodeUnits - 1, nInCodeUnits, nInCodeUnits, false);
+	}
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StrlcpyTestBasics 
+//
+// Given an input string of the given type, perform all the conversion tests 
+// between all of the different type combinations.
+//
+template <typename InCharT>
+static int StrlcpyTest(const InCharT* input, size_t nExpectedCodeUnits = EA::StdC::kSizeTypeUnset, bool bWillError = false, size_t nInCodeUnits = EA::StdC::kSizeTypeUnset)
+{
+	typedef typename CharTraits<InCharT>::char1_t char1_t;
+	typedef typename CharTraits<InCharT>::char2_t char2_t;
+
+	int nErrorCount = 0;
+
+	size_t nInActualCodeUnits = EA::StdC::Strlen(input);
+	if (nInCodeUnits == EA::StdC::kSizeTypeUnset)
+		nInCodeUnits = nInActualCodeUnits;
+	else if (nInCodeUnits < nInActualCodeUnits)
+		nInActualCodeUnits = nInCodeUnits;
+	if (nExpectedCodeUnits == EA::StdC::kSizeTypeUnset)
+		nExpectedCodeUnits = nInCodeUnits;
+
+	// PHASE 1: Create a utf8, ucs2 and ucs4 version of the string.
+
+	// Convert to the first type
+	char1_t char1Buff[kStrlcpyTestOutSize+1];
+	nErrorCount += StrlcpyTestConversion(char1Buff, input, kStrlcpyTestOutSize, nInCodeUnits, nExpectedCodeUnits, bWillError);
+
+	// Convert to the second type
+	char2_t char2Buff[kStrlcpyTestOutSize+1];
+	nErrorCount += StrlcpyTestConversion(char2Buff, input, kStrlcpyTestOutSize, nInCodeUnits, nExpectedCodeUnits, bWillError);
+
+	// If errors are expected, we can't continue
+	if(bWillError)
+	{
+		return nErrorCount;
+	}
+
+	// PHASE 2: Perform basic tests 
+	if(nExpectedCodeUnits == nInActualCodeUnits)
+	{
+		nErrorCount += StrlcpyTestBasics<InCharT, char1_t>(input, nInActualCodeUnits);
+		nErrorCount += StrlcpyTestBasics<InCharT, char2_t>(input, nInActualCodeUnits);
+		nErrorCount += StrlcpyTestBasics<char1_t, InCharT>(char1Buff, nInActualCodeUnits);
+		nErrorCount += StrlcpyTestBasics<char2_t, InCharT>(char2Buff, nInActualCodeUnits);
+		nErrorCount += StrlcpyTestBasics<char1_t, char2_t>(char1Buff, nInActualCodeUnits);
+		nErrorCount += StrlcpyTestBasics<char2_t, char1_t>(char2Buff, nInActualCodeUnits);
+	}
+
+	// PHASE 3: Make sure we can convert back to the original (this can fail if there are code points without 1-1 mapping in the string
+	nErrorCount += StrlcpyTestConversionCompare(char1Buff, nExpectedCodeUnits, input, nInActualCodeUnits);
+	nErrorCount += StrlcpyTestConversionCompare(char2Buff, nExpectedCodeUnits, input, nInActualCodeUnits);
+	nErrorCount += StrlcpyTestConversionCompare(char1Buff, nExpectedCodeUnits, char2Buff, nExpectedCodeUnits);
+	nErrorCount += StrlcpyTestConversionCompare(char2Buff, nExpectedCodeUnits, char1Buff, nExpectedCodeUnits);
+	return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestStringCore
+//
+EA_DISABLE_VC_WARNING(6262) // Function uses 'XXXX' bytes of stack space:  exceeds /analyze:stacksize '16384'
+static int TestStringCore()
+{
+	using namespace EA::StdC;
+
+	int    nErrorCount = 0;
+	size_t sizeResult;
+
+	//{ // Trigger crash intentionally.
+	//    EA::StdC::AtofEnglish((char8_t*)NULL); 
+	//}
+
+	{ // Test user report of inconsistency between FtoaEnglish and Snprintf.
+		const char8_t* stringValue = "1.2345";
+		float floatValue = (float)EA::StdC::AtofEnglish(stringValue); 
+		// floatValue = 1.2345000505447387 in FPU register.
+
+		char8_t tmp1[64];
+		FtoaEnglish(floatValue, tmp1, sizeof(tmp1), 16, false);
+		// tmp1 = "1.23450005"
+
+		char8_t tmp2[64];
+		Snprintf(tmp2, sizeof(tmp2), "%.16f", floatValue);
+		// tmp1 = "1.2345000505447388"
+
+		EATEST_VERIFY(Strcmp(tmp1, tmp2) == 0);
+	}
+
+
+	// char8_t*  Strcat(char8_t*  pDestination, const char8_t*  pSource);
+	// char16_t* Strcat(char16_t* pDestination, const char16_t* pSource);
+	// char32_t* Strcat(char32_t* pDestination, const char32_t* pSource);
+	{
+		char8_t s_to[] = "hello\x0           ";
+		const char8_t* s_from = " world";
+
+		EATEST_VERIFY(Strcat(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, "hello world") == 0);
+	}
+	{
+		char16_t s_to[64]; Strlcpy(s_to, EA_CHAR16("hello\x0           "), EAArrayCount(s_to)); // Can't do s_to[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16(" world");
+
+		EATEST_VERIFY(Strcat(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("hello world")) == 0);
+	}
+	{
+		char32_t s_to[64]; Strlcpy(s_to, EA_CHAR32("hello\x0           "), EAArrayCount(s_to)); // Can't do s_to[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32(" world");
+
+		EATEST_VERIFY(Strcat(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("hello world")) == 0);
+	}
+
+
+	// char8_t*  Strncat(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+	// char16_t* Strncat(char16_t* pDestination, const char16_t* pSource, size_t n);
+	// char32_t* Strncat(char32_t* pDestination, const char32_t* pSource, size_t n);
+	{
+		char8_t s_to[] = "0123\x0......";
+		const char8_t* s_from = "456789";
+
+		EATEST_VERIFY(Strncat(s_to, s_from, 5) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, "012345678") == 0);
+		EATEST_VERIFY(s_to[9] == char8_t(0));
+	}
+	{
+		char16_t s_to[24]; Strlcpy(s_to, EA_CHAR16("0123\x0......"), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("456789");
+
+		EATEST_VERIFY(Strncat(s_to, s_from, 5) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("012345678")) == 0);
+		EATEST_VERIFY(s_to[9] == char16_t(0));
+	}
+	{
+		char32_t s_to[24]; Strlcpy(s_to, EA_CHAR32("0123\x0......"), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("456789");
+
+		EATEST_VERIFY(Strncat(s_to, s_from, 5) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("012345678")) == 0);
+		EATEST_VERIFY(s_to[9] == char32_t(0));
+	}
+
+
+	// char8_t*  StringnCat(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+	// char16_t* StringnCat(char16_t* pDestination, const char16_t* pSource, size_t n);
+	// char32_t* StringnCat(char32_t* pDestination, const char32_t* pSource, size_t n);
+	//
+	// The StringnCopy and StringnCat functions appear to be broken. But since we 
+	// are providing them in this library only for backward compatibility, 
+	// we verify that their behaviour is the same as the existing rwstdc package, 
+	// including any broken behaviour.
+	{
+		char8_t s_to[] = "0123\x0......";
+		const char8_t* s_from = "456789";
+
+		EATEST_VERIFY(StringnCat(s_to, s_from, 5) == s_to);
+		//EATEST_VERIFY(Strcmp(s_to, "012345678..") == 0); Disabled while we try to clarify what the expected behaviour is.
+
+		Memset8(s_to, (char8_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCat(s_to, s_from, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+	{
+		char16_t s_to[24]; Strlcpy(s_to, EA_CHAR16("0123\x0......"), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("456789");
+
+		EATEST_VERIFY(StringnCat(s_to, s_from, 5) == s_to);
+		//EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("012345678..")) == 0); Disabled while we try to clarify what the expected behaviour is.
+
+		Memset16(s_to, (char16_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCat(s_to, s_from, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+	{
+		char32_t s_to[24]; Strlcpy(s_to, EA_CHAR32("0123\x0......"), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("456789");
+
+		EATEST_VERIFY(StringnCat(s_to, s_from, 5) == s_to);
+		//EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("012345678..")) == 0); Disabled while we try to clarify what the expected behaviour is.
+
+		Memset32(s_to, (char32_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCat(s_to, s_from, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+
+
+	// size_t Strlcat(char8_t*  pDestination, const char8_t*  pSource, size_t nDestCapacity);
+	// size_t Strlcat(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity);
+	// size_t Strlcat(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity);
+	{
+		char8_t s_to[8] = "0123\x0..";
+		const char8_t* s_from = "456789";
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123") + Strlen("456789"));
+		EATEST_VERIFY(Strcmp(s_to, "0123456") == 0);
+	}
+	{
+		char16_t s_to[8]; Strlcpy(s_to, EA_CHAR16("0123\x0.."), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("456789");
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123") + Strlen("456789"));
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("0123456")) == 0);
+	}
+	{
+		char32_t s_to[8]; Strlcpy(s_to, EA_CHAR32("0123\x0.."), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("456789");
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123") + Strlen("456789"));
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("0123456")) == 0);
+	}
+
+
+	{
+		char8_t s_to[] = "01\x0........";
+		const char8_t* s_from = "23456";
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123456"));
+		EATEST_VERIFY(Strcmp(s_to, "0123456") == 0);
+	}
+	{
+		char16_t s_to[16]; Strlcpy(s_to, EA_CHAR16("01\x0........"), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("23456");
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123456"));
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("0123456")) == 0);
+	}
+	{
+		char32_t s_to[16]; Strlcpy(s_to, EA_CHAR32("01\x0........"), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("23456");
+
+		sizeResult = Strlcat(s_to, s_from, EAArrayCount(s_to));
+		EATEST_VERIFY(sizeResult == Strlen("0123456"));
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("0123456")) == 0);
+	}
+	
+	/// Regression testing for reported wchar_t array problem on 32 bits wchar_t machine.
+	{
+		char16_t array16[60] = { 'a', 0 };
+		char32_t array32[60] = { 'b', 0 };
+
+		sizeResult = Strlcat(array16, array32 , EAArrayCount(array16));
+		EATEST_VERIFY(sizeResult == 2);
+		EATEST_VERIFY((array16[0] == 'a') && (array16[1] == 'b') && (array16[2] == 0));
+	}
+	{
+		char16_t array16[60] = { 'a', 0 };
+		char32_t array32[60] = { 'b', 0 };
+
+		sizeResult = Strlcat(array32, array16, EAArrayCount(array32));
+		EATEST_VERIFY(sizeResult == 2);
+		EATEST_VERIFY((array32[0] == 'b') && (array32[1] == 'a') && (array32[2] == 0));
+	}
+
+
+	/// char8_t*  Strcpy(char8_t*  pDestination, const char8_t*  pSource);
+	/// char16_t* Strcpy(char16_t* pDestination, const char16_t* pSource);
+	/// char32_t* Strcpy(char32_t* pDestination, const char32_t* pSource);
+	{
+		char8_t s_to[] = "0123456789";
+		const char8_t* s_from = "1234567890";
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, "1234567890") == 0);
+	}
+	{
+		char16_t s_to[16]; Strlcpy(s_to, EA_CHAR16("0123456789"), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("1234567890");
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR16("1234567890")) == 0);
+	}
+	{
+		char32_t s_to[16]; Strlcpy(s_to, EA_CHAR32("0123456789"), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("1234567890");
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Strcmp(s_to, EA_CHAR32("1234567890")) == 0);
+	}
+
+
+	// char8_t*  Strncpy(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+	// char16_t* Strncpy(char16_t* pDestination, const char16_t* pSource, size_t n);
+	// char32_t* Strncpy(char32_t* pDestination, const char32_t* pSource, size_t n);
+	{
+		char8_t  s_to[] = "...........................";
+		const char8_t* s_from = "l;kajjsdf;q4w3rrpoiu113<>)(";
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from, Strlen(s_from) * sizeof(char8_t)) == 0);
+
+		Memset8(s_to, (char8_t)'.', Strlen(s_to));
+
+		EATEST_VERIFY(Strncpy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char8_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char8_t)'.' );
+	}
+	{
+		char16_t s_to[32]; Strlcpy(s_to, EA_CHAR16("..........................."), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from, Strlen(s_from) * sizeof(char16_t)) == 0);
+
+		Memset16(s_to, (char16_t)'.', Strlen(s_to));
+
+		EATEST_VERIFY(Strncpy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char16_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char16_t)'.' );
+	}
+	{
+		char32_t s_to[32]; Strlcpy(s_to, EA_CHAR32("..........................."), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		EATEST_VERIFY(Strcpy(s_to, s_from) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from, Strlen(s_from) * sizeof(char32_t)) == 0);
+
+		Memset32(s_to, (char32_t)'.', Strlen(s_to));
+
+		EATEST_VERIFY(Strncpy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char32_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char32_t)'.' );
+	}
+
+
+	// char8_t*  StringnCopy(char8_t*  pDestination, const char8_t*  pSource, size_t n);
+	// char16_t* StringnCopy(char16_t* pDestination, const char16_t* pSource, size_t n);
+	// char32_t* StringnCopy(char32_t* pDestination, const char32_t* pSource, size_t n);
+	{
+		char8_t  s_to[] = "...........................";
+		const char8_t* s_from = "l;kajjsdf;q4w3rrpoiu113<>)(";
+
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char8_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char8_t)'.' );
+
+		// Test copying nothing.
+		Memset8(s_to, (char8_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+	{
+		char16_t s_to[32]; Strlcpy(s_to, EA_CHAR16(".........................."), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char16_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char8_t)'.' );
+
+		// Test copying nothing.
+		Memset16(s_to, (char16_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+	{
+		char32_t s_to[32]; Strlcpy(s_to, EA_CHAR32(".........................."), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 5) == s_to);
+		EATEST_VERIFY(Memcmp(s_to, s_from+14, 5 * sizeof(char32_t)) == 0);
+		EATEST_VERIFY(s_to[5] == (char8_t)'.' );
+
+		// Test copying nothing.
+		Memset32(s_to, (char32_t)'.', Strlen(s_to));
+		EATEST_VERIFY(StringnCopy(s_to, s_from+14, 0) == s_to);
+		EATEST_VERIFY(s_to[0] == '.');
+	}
+
+	// size_t Strlcpy(char8_t*  pDestination, const char8_t*  pSource, size_t nDestCapacity);
+	// size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity);
+	// size_t Strlcpy(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity);
+	// int Strlcpy(char8_t*  pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	// int Strlcpy(char16_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	// int Strlcpy(char8_t*  pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	// int Strlcpy(char32_t* pDestination, const char8_t*  pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	// int Strlcpy(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	// int Strlcpy(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength = (size_t)~0);
+	{
+		nErrorCount += StrlcpyTest("l;kajjsdf;q4w3rrpoiu113____");
+		nErrorCount += StrlcpyTest("Foo!");
+		nErrorCount += StrlcpyTest("a");
+		nErrorCount += StrlcpyTest("");
+		nErrorCount += StrlcpyTest("\x43\x3A\x5C\x45\x6C\x65\x63\x74\x72\x6F\x6E\x69\x63\x20\x41\x72\x74\x73\x5C\xE3\x82\xB6\xEF\xBD\xA5\xE3\x82\xB7\xE3\x83\xA0\xE3\x82\xBA\xEF\xBC\x93", 25);
+
+		const char8_t* kInvalidUTF8StringArray[] = {
+			"\xc2",       // 1 byte of a 2 byte sequence
+			"\xc2\x20",   // 1 byte of a 2 byte sequence
+			"\xe0",       // 1 byte of a 3 byte sequence
+			"\xe0\x20",   // 1 byte of a 3 byte sequence
+			"\xe0",       // 1 byte of a 3 byte sequence
+			"\xf0",       // 1 byte of a 4 byte sequence
+			"\xf0\x20",   // 1 byte of a 4 byte sequence
+			"\xf8",       // 1 byte of a 5 byte sequence
+			"\xf8\x20",   // 1 byte of a 5 byte sequence
+			"\xfc",       // 1 byte of a 6 byte sequence
+			"\xfc\x20",   // 1 byte of a 6 byte sequence
+			"\xfe"        // 1 byte of an invalid sequence
+		};
+
+		for (size_t u = 0; u < EAArrayCount(kInvalidUTF8StringArray); u++)
+		{
+			nErrorCount += StrlcpyTest(kInvalidUTF8StringArray[u], 0, true);
+		}
+	}
+
+	/// size_t Strlen(const char8_t*  pString);
+	/// size_t Strlen(const char16_t* pString);
+	/// size_t Strlen(const char32_t* pString);
+	{
+		// We test many combinations of data and alignments.
+		RandomFast   random;
+		const size_t kBufferSize(4096 + sizeof(void*));
+		uint8_t      buffer[kBufferSize];
+		Stopwatch    stopwatch(Stopwatch::kUnitsCycles, false);
+
+		for(size_t i = 0; i < kBufferSize; ++i)
+		{
+			const uint8_t c = (uint8_t)(random.RandomUint32Uniform() >> 8);
+			buffer[i] = c ? c : (uint8_t)0x20;
+		}
+		buffer[kBufferSize - 1] = 0;
+
+		stopwatch.Start();
+		for(size_t j = 0; j < kBufferSize; ++j)
+		{
+			size_t n = Strlen((char8_t*)buffer + j);
+
+			EATEST_VERIFY(n == ((kBufferSize - 1) - j));
+			//if(n != ((kBufferSize - 1) - j))
+			//    ++j;
+		}
+		stopwatch.Stop();
+		//EA::UnitTest::Report("Time %I64u\n", stopwatch.GetElapsedTime());
+	}
+	{
+		// We test many combinations of data and alignments.
+		RandomFast   random;
+		const size_t kBufferSize(2048 + sizeof(void*));
+		uint16_t     buffer[kBufferSize];
+		Stopwatch    stopwatch(Stopwatch::kUnitsCycles, false);
+
+		for(size_t i = 0; i < kBufferSize; ++i)
+		{
+			const uint16_t c = (uint16_t)(random.RandomUint32Uniform() >> 8);
+			buffer[i] = c ? c : (uint16_t)0x0020;
+		}
+		buffer[kBufferSize - 1] = 0;
+
+		stopwatch.Start();
+		for(size_t j = 0; j < kBufferSize; ++j)
+		{
+			size_t n = Strlen((char16_t*)buffer + j);
+
+			EATEST_VERIFY(n == ((kBufferSize - 1) - j));
+			//if(n != ((kBufferSize - 1) - j))
+			//    ++j;
+		}
+		stopwatch.Stop();
+		//EA::UnitTest::Report("Time %I64u\n", stopwatch.GetElapsedTime());
+	}
+	{
+		// We test many combinations of data and alignments.
+		RandomFast   random;
+		const size_t kBufferSize(2048 + sizeof(void*));
+		uint32_t     buffer[kBufferSize];
+		Stopwatch    stopwatch(Stopwatch::kUnitsCycles, false);
+
+		for(size_t i = 0; i < kBufferSize; ++i)
+		{
+			const uint32_t c = (uint32_t)(random.RandomUint32Uniform() >> 8);
+			buffer[i] = c ? c : (uint32_t)0x0020;
+		}
+		buffer[kBufferSize - 1] = 0;
+
+		stopwatch.Start();
+		for(size_t j = 0; j < kBufferSize; ++j)
+		{
+			size_t n = Strlen((char32_t*)buffer + j);
+
+			EATEST_VERIFY(n == ((kBufferSize - 1) - j));
+			//if(n != ((kBufferSize - 1) - j))
+			//    ++j;
+		}
+		stopwatch.Stop();
+		//EA::UnitTest::Report("Time %I64u\n", stopwatch.GetElapsedTime());
+	}
+
+
+
+	/// size_t StrlenUTF8Decoded(const char8_t* pString);
+	/// size_t StrlenUTF8Encoded(const char16_t* pString);
+	/// size_t StrlenUTF8Encoded(const char32_t* pString);
+	{
+		EATEST_VERIFY(StrlenUTF8Decoded("0123456789") == 10);
+		EATEST_VERIFY(StrlenUTF8Decoded("") == 0);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xc2" "\xa2") == 1);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xd7" "\x90") == 1);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xe0" "\xbc" "\xa0") == 1);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xc2\x80 \xc2\x81 \xdf\xbe \xdf\xbf") == 7);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xe0\xa0\x80 \xe0\xa0\x81 \xef\xbf\xbe \xef\xbf\xbf") == 7);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xf0\x90\x80\x80 \xf0\x90\x80\x81") == 3);
+		EATEST_VERIFY(StrlenUTF8Decoded("\xf4\x8f\xbf\xbe \xf4\x8f\xbf\xbf") == 3);
+	}
+	{
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR16("0123456789")) == 10);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR16("")) == 0);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR16("\x00a0")) == 2);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR16("\x0400")) == 2);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR16("\x0800")) == 3);
+
+		// We have to break up the string into multiple sub-strings because the \x escape sequence has limitations in how it works.
+		eastl::fixed_string<char16_t, 32> s16; s16 = EA_CHAR16("\xffff"); s16 += EA_CHAR16("\xffff"); // We use a string object because some compilers don't support 16 bit string literals, and thus EA_CHAR16 is a function and doesn't just prepend "L" or "u" to the string.
+		EATEST_VERIFY(StrlenUTF8Encoded(s16.c_str()) == 6);
+
+		s16 = EA_CHAR16("\xffff"); s16 += EA_CHAR16("\x0900"); s16 += EA_CHAR16("0"); s16 += EA_CHAR16("\x00a0");
+		EATEST_VERIFY(StrlenUTF8Encoded(s16.c_str()) == 9);
+	}
+	{
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR32("0123456789")) == 10);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR32("")) == 0);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR32("\x00a0")) == 2);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR32("\x0400")) == 2);
+		EATEST_VERIFY(StrlenUTF8Encoded(EA_CHAR32("\x0800")) == 3);
+
+		// We have to break up the string into multiple sub-strings because the \x escape sequence has limitations in how it works.
+		eastl::fixed_string<char32_t, 32> s32; s32 = EA_CHAR32("\xffff"); s32 += EA_CHAR32("\xffff"); // We use a string object because some compilers don't support 32 bit string literals, and thus EA_CHAR32 is a function and doesn't just prepend "L" or "u" to the string.
+		EATEST_VERIFY(StrlenUTF8Encoded(s32.c_str()) == 6);
+
+		s32 = EA_CHAR32("\xffff"); s32 += EA_CHAR32("\x0900"); s32 += EA_CHAR32("0"); s32 += EA_CHAR32("\x00a0");
+		EATEST_VERIFY(StrlenUTF8Encoded(s32.c_str()) == 9);
+	}
+
+
+	/// char8_t*  Strend(const char8_t*  pString);
+	/// char16_t* Strend(const char16_t* pString);
+	/// char32_t* Strend(const char32_t* pString);
+	{
+		const char8_t* pString = "0123456789";
+		EATEST_VERIFY(Strend(pString) == (pString + Strlen(pString)));
+	}
+	{
+		const char16_t* pString = EA_CHAR16("0123456789");
+		EATEST_VERIFY(Strend(pString) == (pString + Strlen(pString)));
+	}
+	{
+		const char32_t* pString = EA_CHAR32("0123456789");
+		EATEST_VERIFY(Strend(pString) == (pString + Strlen(pString)));
+	}
+
+
+	// size_t Strxfrm(char8_t*  pDest, const char8_t*  pSource, size_t n);
+	// size_t Strxfrm(char16_t* pDest, const char16_t* pSource, size_t n);
+	// size_t Strxfrm(char32_t* pDest, const char32_t* pSource, size_t n);
+	{
+		// To do: Make a better test.
+		char8_t  s_to[] = "...........................";
+		const char8_t* s_from = "l;kajjsdf;q4w3rrpoiu113<>)(";
+
+		const size_t n = Strxfrm(s_to, s_from, Strlen(s_from) + 1);
+		EATEST_VERIFY(Strcmp(s_to, s_from) == 0);
+		EATEST_VERIFY(n == Strlen(s_from));
+	}
+	{
+		// To do: Make a better test.
+		char16_t s_to[32]; Strlcpy(s_to, EA_CHAR16("..........................."), EAArrayCount(s_to)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		const char16_t* s_from = EA_CHAR16("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		const size_t n = Strxfrm(s_to, s_from, Strlen(s_from) + 1);
+		EATEST_VERIFY(Strcmp(s_to, s_from) == 0);
+		EATEST_VERIFY(n == Strlen(s_from));
+	}
+	{
+		// To do: Make a better test.
+		char32_t s_to[32]; Strlcpy(s_to, EA_CHAR32("..........................."), EAArrayCount(s_to)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		const char32_t* s_from = EA_CHAR32("l;kajjsdf;q4w3rrpoiu113<>)(");
+
+		const size_t n = Strxfrm(s_to, s_from, Strlen(s_from) + 1);
+		EATEST_VERIFY(Strcmp(s_to, s_from) == 0);
+		EATEST_VERIFY(n == Strlen(s_from));
+	}
+
+
+	// char8_t*  Strdup(const char8_t*  pString);
+	// char16_t* Strdup(const char16_t* pString);
+	// char32_t* Strdup(const char32_t* pString);
+	// void      Strdel(char8_t*  pString);
+	// void      Strdel(char16_t* pString);
+	// void      Strdel(char32_t* pString);
+	{
+		typedef char8_t test_type;
+		test_type  s_from[]  = "...........................";
+		test_type* s_new = NULL;
+
+		EATEST_VERIFY((s_new = Strdup(s_from)) != NULL);
+		EATEST_VERIFY(Strcmp(s_from, s_new) == 0);
+
+		Strdel(s_new);
+	}
+	{
+		typedef char16_t test_type;
+		char16_t   s_from[32]; Strlcpy(s_from, EA_CHAR16("..........................."), EAArrayCount(s_from)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		test_type* s_new = NULL;
+
+		EATEST_VERIFY((s_new = Strdup(s_from)) != NULL);
+		EATEST_VERIFY(Strcmp(s_from, s_new) == 0);
+
+		Strdel(s_new);
+	}
+	{
+		typedef char32_t test_type;
+		char32_t   s_from[32]; Strlcpy(s_from, EA_CHAR32("..........................."), EAArrayCount(s_from)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		test_type* s_new = NULL;
+
+		EATEST_VERIFY((s_new = Strdup(s_from)) != NULL);
+		EATEST_VERIFY(Strcmp(s_from, s_new) == 0);
+
+		Strdel(s_new);
+	}
+
+
+	// char8_t*  Strupr(char8_t*  pString);
+	// char16_t* Strupr(char16_t* pString);
+	// char32_t* Strupr(char32_t* pString);
+	{
+		char8_t s8[] = "hello world";
+
+		EATEST_VERIFY(Strupr(s8) == s8);
+		EATEST_VERIFY(Memcmp(s8, "HELLO WORLD", EAArrayCount(s8)) == 0);
+	}
+	{
+		char16_t s16[16]; Strlcpy(s16, EA_CHAR16("hello world"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strupr(s16) == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("HELLO WORLD"), EAArrayCount(s16)) == 0);
+	}
+	{
+		char32_t s32[32]; Strlcpy(s32, EA_CHAR32("hello world"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strupr(s32) == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("HELLO WORLD"), EAArrayCount(s32)) == 0);
+	}
+
+
+	// char8_t*  Strlwr(char8_t*  pString);
+	// char16_t* Strlwr(char16_t* pString);
+	// char32_t* Strlwr(char32_t* pString);
+	{
+		char8_t s8[] = "HELLO WORLD";
+
+		EATEST_VERIFY(Strlwr(s8) == s8);
+		EATEST_VERIFY(Memcmp(s8, "hello world", EAArrayCount(s8)) == 0);
+	}
+	{
+		char16_t s16[16]; Strlcpy(s16, EA_CHAR16("HELLO WORLD"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strlwr(s16) == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("hello world"), EAArrayCount(s16)) == 0);
+	}
+	{
+		char32_t s32[32]; Strlcpy(s32, EA_CHAR32("HELLO WORLD"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strlwr(s32) == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("hello world"), EAArrayCount(s32)) == 0);
+	}
+
+
+	// char8_t*  Strchr(const char8_t*  pString, int c);
+	// char16_t* Strchr(const char16_t* pString, char16_t c);
+	// char32_t* Strchr(const char32_t* pString, char32_t c);
+	{
+		char8_t s8[] = "012a456789abc2ef";
+
+		EATEST_VERIFY(Strrchr(s8, 'a')   == &s8[0xa]);
+		EATEST_VERIFY(Strrchr(s8, '2')   == &s8[0xd]);
+		EATEST_VERIFY(Strrchr(s8, '0')   == &s8[0x0]);
+		EATEST_VERIFY(Strrchr(s8, '\x0') == &s8[0x10]);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("012a456789abc2ef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strrchr(s16, 'a')   == &s16[0xa]);
+		EATEST_VERIFY(Strrchr(s16, '2')   == &s16[0xd]);
+		EATEST_VERIFY(Strrchr(s16, '0')   == &s16[0x0]);
+		EATEST_VERIFY(Strrchr(s16, '\x0') == &s16[0x10]);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("012a456789abc2ef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strrchr(s32, 'a')   == &s32[0xa]);
+		EATEST_VERIFY(Strrchr(s32, '2')   == &s32[0xd]);
+		EATEST_VERIFY(Strrchr(s32, '0')   == &s32[0x0]);
+		EATEST_VERIFY(Strrchr(s32, '\x0') == &s32[0x10]);
+	}
+
+
+	// size_t Strcspn(const char8_t*  pString1, const char8_t*  pString2);
+	// size_t Strcspn(const char16_t* pString1, const char16_t* pString2);
+	// size_t Strcspn(const char32_t* pString1, const char32_t* pString2);
+	{
+		char8_t s8[] = "0123456789abcdef";
+
+		EATEST_VERIFY(Strcspn(s8, "@fa")    == 0xa);
+		EATEST_VERIFY(Strcspn(s8, "5.a,f")  == 0x5);
+		EATEST_VERIFY(Strcspn(s8, ":/1")    == 0x1);
+		EATEST_VERIFY(Strcspn(s8, ";/\x0!") == 0x10);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("0123456789abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strcspn(s16, EA_CHAR16("@fa"))    == 0xa);
+		EATEST_VERIFY(Strcspn(s16, EA_CHAR16("5.a,f"))  == 0x5);
+		EATEST_VERIFY(Strcspn(s16, EA_CHAR16(":/1"))    == 0x1);
+		EATEST_VERIFY(Strcspn(s16, EA_CHAR16(";/\x0!")) == 0x10);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("0123456789abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strcspn(s32, EA_CHAR32("@fa"))    == 0xa);
+		EATEST_VERIFY(Strcspn(s32, EA_CHAR32("5.a,f"))  == 0x5);
+		EATEST_VERIFY(Strcspn(s32, EA_CHAR32(":/1"))    == 0x1);
+		EATEST_VERIFY(Strcspn(s32, EA_CHAR32(";/\x0!")) == 0x10);
+	}
+
+
+	// char8_t*  Strpbrk(const char8_t*  pString1, const char8_t*  pString2);
+	// char16_t* Strpbrk(const char16_t* pString1, const char16_t* pString2);
+	// char32_t* Strpbrk(const char32_t* pString1, const char32_t* pString2);
+	{
+		char8_t s8[] = "0123456789abcdef";
+
+		EATEST_VERIFY(Strpbrk(s8, "@fa")    == &s8[0xa]);
+		EATEST_VERIFY(Strpbrk(s8, "5.a,f")  == &s8[0x5]);
+		EATEST_VERIFY(Strpbrk(s8, ":/1")    == &s8[0x1]);
+		EATEST_VERIFY(Strpbrk(s8, ";/\x0!") == NULL);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("0123456789abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strpbrk(s16, EA_CHAR16("@fa"))    == &s16[0xa]);
+		EATEST_VERIFY(Strpbrk(s16, EA_CHAR16("5.a,f"))  == &s16[0x5]);
+		EATEST_VERIFY(Strpbrk(s16, EA_CHAR16(":/1"))    == &s16[0x1]);
+		EATEST_VERIFY(Strpbrk(s16, EA_CHAR16(";/\x0!")) == NULL);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("0123456789abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strpbrk(s32, EA_CHAR32("@fa"))    == &s32[0xa]);
+		EATEST_VERIFY(Strpbrk(s32, EA_CHAR32("5.a,f"))  == &s32[0x5]);
+		EATEST_VERIFY(Strpbrk(s32, EA_CHAR32(":/1"))    == &s32[0x1]);
+		EATEST_VERIFY(Strpbrk(s32, EA_CHAR32(";/\x0!")) == NULL);
+	}
+
+
+	// char8_t*  Strrchr(const char8_t*  pString, int      c);
+	// char16_t* Strrchr(const char16_t* pString, char16_t c);
+	// char32_t* Strrchr(const char32_t* pString, char32_t c);
+	{
+		char8_t s8[] = "0123456789abcdef";
+
+		EATEST_VERIFY(Strchr(s8, 'z')   == NULL);
+		EATEST_VERIFY(Strchr(s8, 'a')   == &s8[0xa]);
+		EATEST_VERIFY(Strchr(s8, '0')   == &s8[0x0]);
+		EATEST_VERIFY(Strchr(s8, '\x0') == &s8[0x10]);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("0123456789abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strchr(s16, 'z')   == NULL);
+		EATEST_VERIFY(Strchr(s16, 'a')   == &s16[0xa]);
+		EATEST_VERIFY(Strchr(s16, '0')   == &s16[0x0]);
+		EATEST_VERIFY(Strchr(s16, '\x0') == &s16[0x10]);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("0123456789abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strchr(s32, 'z')   == NULL);
+		EATEST_VERIFY(Strchr(s32, 'a')   == &s32[0xa]);
+		EATEST_VERIFY(Strchr(s32, '0')   == &s32[0x0]);
+		EATEST_VERIFY(Strchr(s32, '\x0') == &s32[0x10]);
+	}
+
+		{
+		char8_t s8[] = "0123456789abcdef";
+
+		EATEST_VERIFY(Strnchr(s8, 'z', EAArrayCount(s8))   == NULL);
+		EATEST_VERIFY(Strnchr(s8, 'a', EAArrayCount(s8))   == &s8[0xa]);
+		EATEST_VERIFY(Strnchr(s8, '5', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '5', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '5', 2) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '5', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '5', 6) == &s8[0x5]);
+		EATEST_VERIFY(Strnchr(s8, '5', 7) == &s8[0x5]);
+		EATEST_VERIFY(Strnchr(s8, '5', 9) == &s8[0x5]);
+		EATEST_VERIFY(Strnchr(s8, '5', EAArrayCount(s8)) == &s8[0x5]);
+		EATEST_VERIFY(Strnchr(s8, '0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '0', 1) == &s8[0x0]);
+		EATEST_VERIFY(Strnchr(s8, '0', 2) == &s8[0x0]);
+		EATEST_VERIFY(Strnchr(s8, '0', 9) == &s8[0x0]);
+		EATEST_VERIFY(Strnchr(s8, '0', EAArrayCount(s8)) == &s8[0x0]);
+		EATEST_VERIFY(Strnchr(s8, 'f', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s8, 'f', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s8, 'f', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s8, 'f', EA::StdC::Strlen(s8)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s8, 'f', EA::StdC::Strlen(s8)) == &s8[0xf]);
+		EATEST_VERIFY(Strnchr(s8, 'f', EA::StdC::Strlen(s8)+1) == &s8[0xf]); //This is the null terminator
+		EATEST_VERIFY(Strnchr(s8, '\0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '\0', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '\0', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '\0', EA::StdC::Strlen(s8)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '\0', EA::StdC::Strlen(s8)) == NULL);
+		EATEST_VERIFY(Strnchr(s8, '\0', EA::StdC::Strlen(s8)+1) == &s8[0x10]); //This is the null terminator
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("0123456789abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strnchr(s16, 'z', EAArrayCount(s16))   == NULL);
+		EATEST_VERIFY(Strnchr(s16, 'a', EAArrayCount(s16))   == &s16[0xa]);
+		EATEST_VERIFY(Strnchr(s16, '5', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '5', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '5', 2) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '5', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '5', 6) == &s16[0x5]);
+		EATEST_VERIFY(Strnchr(s16, '5', 7) == &s16[0x5]);
+		EATEST_VERIFY(Strnchr(s16, '5', 9) == &s16[0x5]);
+		EATEST_VERIFY(Strnchr(s16, '5', EAArrayCount(s16)) == &s16[0x5]);
+		EATEST_VERIFY(Strnchr(s16, '0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '0', 1) == &s16[0x0]);
+		EATEST_VERIFY(Strnchr(s16, '0', 2) == &s16[0x0]);
+		EATEST_VERIFY(Strnchr(s16, '0', 9) == &s16[0x0]);
+		EATEST_VERIFY(Strnchr(s16, '0', EAArrayCount(s16)) == &s16[0x0]);
+		EATEST_VERIFY(Strnchr(s16, 'f', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s16, 'f', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s16, 'f', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s16, 'f', EA::StdC::Strlen(s16)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s16, 'f', EA::StdC::Strlen(s16)) == &s16[0xf]);
+		EATEST_VERIFY(Strnchr(s16, 'f', EA::StdC::Strlen(s16)+1) == &s16[0xf]); //This is the null terminator
+		EATEST_VERIFY(Strnchr(s16, '\0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '\0', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '\0', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '\0', EA::StdC::Strlen(s16)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '\0', EA::StdC::Strlen(s16)) == NULL);
+		EATEST_VERIFY(Strnchr(s16, '\0', EA::StdC::Strlen(s16)+1) == &s16[0x10]); //This is the null terminator
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("0123456789abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strnchr(s32, 'z', EAArrayCount(s32))   == NULL);
+		EATEST_VERIFY(Strnchr(s32, 'a', EAArrayCount(s32))   == &s32[0xa]);
+		EATEST_VERIFY(Strnchr(s32, '5', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '5', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '5', 2) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '5', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '5', 6) == &s32[0x5]);
+		EATEST_VERIFY(Strnchr(s32, '5', 7) == &s32[0x5]);
+		EATEST_VERIFY(Strnchr(s32, '5', 9) == &s32[0x5]);
+		EATEST_VERIFY(Strnchr(s32, '5', EAArrayCount(s32)) == &s32[0x5]);
+		EATEST_VERIFY(Strnchr(s32, '0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '0', 1) == &s32[0x0]);
+		EATEST_VERIFY(Strnchr(s32, '0', 2) == &s32[0x0]);
+		EATEST_VERIFY(Strnchr(s32, '0', 9) == &s32[0x0]);
+		EATEST_VERIFY(Strnchr(s32, '0', EAArrayCount(s32)) == &s32[0x0]);
+		EATEST_VERIFY(Strnchr(s32, 'f', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s32, 'f', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s32, 'f', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s32, 'f', EA::StdC::Strlen(s32)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s32, 'f', EA::StdC::Strlen(s32)) == &s32[0xf]);
+		EATEST_VERIFY(Strnchr(s32, 'f', EA::StdC::Strlen(s32)+1) == &s32[0xf]); //This is the null terminator
+		EATEST_VERIFY(Strnchr(s32, '\0', 0) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '\0', 1) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '\0', 5) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '\0', EA::StdC::Strlen(s32)-1) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '\0', EA::StdC::Strlen(s32)) == NULL);
+		EATEST_VERIFY(Strnchr(s32, '\0', EA::StdC::Strlen(s32)+1) == &s32[0x10]); //This is the null terminator
+	}
+
+	// size_t Strspn(const char8_t*  pString, const char8_t*  pSubString);
+	// size_t Strspn(const char16_t* pString, const char16_t* pSubString);
+	// size_t Strspn(const char32_t* pString, const char32_t* pSubString);
+	{
+		char8_t s8[] = "0123456789abcdef";
+
+		EATEST_VERIFY(Strspn(s8,    "2103") == 4);
+		EATEST_VERIFY(Strspn(s8+10, "badc") == 4);
+		EATEST_VERIFY(Strspn(s8,    ":/1")  == 0);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("0123456789abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strspn(s16,    EA_CHAR16("2103")) == 4);
+		EATEST_VERIFY(Strspn(s16+10, EA_CHAR16("badc")) == 4);
+		EATEST_VERIFY(Strspn(s16,    EA_CHAR16(":/1"))  == 0);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("0123456789abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strspn(s32,    EA_CHAR32("2103")) == 4);
+		EATEST_VERIFY(Strspn(s32+10, EA_CHAR32("badc")) == 4);
+		EATEST_VERIFY(Strspn(s32,    EA_CHAR32(":/1"))  == 0);
+	}
+
+
+	// char8_t*  Strstr(const char8_t*  pString, const char8_t*  pSubString);
+	// char16_t* Strstr(const char16_t* pString, const char16_t* pSubString);
+	// char32_t* Strstr(const char32_t* pString, const char32_t* pSubString);
+	{
+		char8_t s8[] = "012abcdf89abcdef";
+
+		EATEST_VERIFY(Strstr(s8, "")      == &s8[0]);
+		EATEST_VERIFY(Strstr(s8, "012")   == &s8[0]);
+		EATEST_VERIFY(Strstr(s8, "abcde") == &s8[10]);
+		EATEST_VERIFY(Strstr(s8, ":/1")   == NULL);
+		EATEST_VERIFY(Strstr(s8, "abcd")  == &s8[3]);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("012abcdf89abcdef"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strstr(s16, EA_CHAR16(""))      == &s16[0]);
+		EATEST_VERIFY(Strstr(s16, EA_CHAR16("012"))   == &s16[0]);
+		EATEST_VERIFY(Strstr(s16, EA_CHAR16("abcde")) == &s16[10]);
+		EATEST_VERIFY(Strstr(s16, EA_CHAR16(":/1"))   == NULL);
+		EATEST_VERIFY(Strstr(s16, EA_CHAR16("abcd"))  == &s16[3]);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("012abcdf89abcdef"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strstr(s32, EA_CHAR32(""))      == &s32[0]);
+		EATEST_VERIFY(Strstr(s32, EA_CHAR32("012"))   == &s32[0]);
+		EATEST_VERIFY(Strstr(s32, EA_CHAR32("abcde")) == &s32[10]);
+		EATEST_VERIFY(Strstr(s32, EA_CHAR32(":/1"))   == NULL);
+		EATEST_VERIFY(Strstr(s32, EA_CHAR32("abcd"))  == &s32[3]);
+	}
+
+
+	// char8_t*  Stristr(const char8_t*  pString, const char8_t*  pSubString);
+	// char16_t* Stristr(const char16_t* pString, const char16_t* pSubString);
+	// char32_t* Stristr(const char32_t* pString, const char32_t* pSubString);
+	{
+		char8_t s8[] = "012aBcdf89aBcDEf";
+
+		EATEST_VERIFY(Stristr(s8, "012")   == &s8[0]);
+		EATEST_VERIFY(Stristr(s8, "abcde") == &s8[10]);
+		EATEST_VERIFY(Stristr(s8, ":/1")   == NULL);
+		EATEST_VERIFY(Stristr(s8, "abcd")  == &s8[3]);
+		EATEST_VERIFY(Stristr(s8, "")      == s8);
+	}
+	{
+		char16_t s16[24]; Strlcpy(s16, EA_CHAR16("012aBcdf89aBcDEf"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Stristr(s16, EA_CHAR16("012"))   == &s16[0]);
+		EATEST_VERIFY(Stristr(s16, EA_CHAR16("abcde")) == &s16[10]);
+		EATEST_VERIFY(Stristr(s16, EA_CHAR16(":/1"))   == NULL);
+		EATEST_VERIFY(Stristr(s16, EA_CHAR16("abcd"))  == &s16[3]);
+		EATEST_VERIFY(Stristr(s16, EA_CHAR16(""))      == s16);
+	}
+	{
+		char32_t s32[24]; Strlcpy(s32, EA_CHAR32("012aBcdf89aBcDEf"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Stristr(s32, EA_CHAR32("012"))   == &s32[0]);
+		EATEST_VERIFY(Stristr(s32, EA_CHAR32("abcde")) == &s32[10]);
+		EATEST_VERIFY(Stristr(s32, EA_CHAR32(":/1"))   == NULL);
+		EATEST_VERIFY(Stristr(s32, EA_CHAR32("abcd"))  == &s32[3]);
+		EATEST_VERIFY(Stristr(s32, EA_CHAR32(""))      == s32);
+	}
+
+
+	// char8_t*  Strrstr(const char8_t*  pString, const char8_t*  pSubString);
+	// char16_t* Strrstr(const char16_t* pString, const char16_t* pSubString);
+	// char32_t* Strrstr(const char32_t* pString, const char32_t* pSubString);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Strrstr("Hello", "world");
+		Strrstr(EA_CHAR16("Hello"), EA_CHAR16("world"));
+		Strrstr(EA_CHAR32("Hello"), EA_CHAR32("world"));
+	}
+
+
+	// char8_t*  Strirstr(const char8_t*  pString, const char8_t*  pSubString);
+	// char16_t* Strirstr(const char16_t* pString, const char16_t* pSubString);
+	// char32_t* Strirstr(const char32_t* pString, const char32_t* pSubString);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Strirstr("Hello", "world");
+		Strirstr(EA_CHAR16("Hello"), EA_CHAR16("world"));
+		Strirstr(EA_CHAR32("Hello"), EA_CHAR32("world"));
+	}
+
+
+	// char8_t*  Strtok(char8_t*  pString, const char8_t*  pDelimiters, char8_t**  pContext);
+	// char16_t* Strtok(char16_t* pString, const char16_t* pDelimiters, char16_t** pContext);
+	// char32_t* Strtok(char32_t* pString, const char32_t* pDelimiters, char32_t** pContext);
+	{
+		char8_t  s8[] = ",.:1:2.3,4";
+		char8_t* s8ctx = s8;
+		EATEST_VERIFY(Strtok(s8, ",.:", &s8ctx) == &s8[3]);
+
+		char8_t   p[] = "-abc-=-def";
+		char8_t*  p1;
+		char8_t*  r;
+
+		r = Strtok(p,    "-",  &p1);    // r = "abc", p1 = "=-def", p = "abc\0=-def"
+		EATEST_VERIFY(r != NULL);
+
+		r = Strtok(NULL, "-=", &p1);    // r = "def", p1 = NULL,    p = "abc\0=-def"
+		EATEST_VERIFY(r != NULL);
+
+		r = Strtok(NULL, "=",  &p1);    // r = NULL,  p1 = NULL,    p = "abc\0=-def"
+		EATEST_VERIFY(r == NULL);
+	}
+	{
+		char16_t  s16[16]; Strlcpy(s16, EA_CHAR16(",.:1:2.3,4"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* s16ctx = s16;
+		EATEST_VERIFY(Strtok(s16, EA_CHAR16(",.:"), &s16ctx) == &s16[3]);
+	}
+	{
+		char32_t  s32[32]; Strlcpy(s32, EA_CHAR32(",.:1:2.3,4"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* s32ctx = s32;
+		EATEST_VERIFY(Strtok(s32, EA_CHAR32(",.:"), &s32ctx) == &s32[3]);
+	}
+
+	{
+		// Test bug report by user.
+		char8_t  pStr[]   = { 'a', '=', 'b', ';',  0 };
+		char8_t* pContext = NULL;
+		char8_t* pToken;
+
+		   while((pToken = Strtok(pContext ? NULL : pStr, ";", &pContext)) != NULL)
+			{ EATEST_VERIFY(pToken != NULL); }  // This was looping infinitely.
+	}
+
+
+	// const char8_t*  Strtok2(const char8_t*  pString, const char8_t*  pDelimiters, size_t* pResultLength, bool bFirst);
+	// const char16_t* Strtok2(const char16_t* pString, const char16_t* pDelimiters, size_t* pResultLength, bool bFirst);
+	// const char32_t* Strtok2(const char32_t* pString, const char32_t* pDelimiters, size_t* pResultLength, bool bFirst);
+	{
+		const char8_t* teststr  = "  Hello /// This/is++a test";
+		const char8_t* teststr2 = "  ///  ";
+		const char8_t* teststr3 = "  /// Hello ";
+		const char8_t* delim    = "/ %";
+		const char8_t* token1   = "Hello";
+		const char8_t* token2   = "This";
+		const char8_t* token3   = "is++a";
+		const char8_t* token4   = "test";
+		const char8_t* pCurrent;
+		size_t         n;
+
+		// Test the first string
+		pCurrent = Strtok2(teststr, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token2, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token3, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token4, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the second string
+		pCurrent = Strtok2(teststr2, delim, &n, true);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the third string
+		pCurrent = Strtok2(teststr3, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+	}
+	{
+		const char16_t* teststr  = EA_CHAR16("  Hello /// This/is++a test");
+		const char16_t* teststr2 = EA_CHAR16("  ///  ");
+		const char16_t* teststr3 = EA_CHAR16("  /// Hello ");
+		const char16_t* delim    = EA_CHAR16("/ %");
+		const char16_t* token1   = EA_CHAR16("Hello");
+		const char16_t* token2   = EA_CHAR16("This");
+		const char16_t* token3   = EA_CHAR16("is++a");
+		const char16_t* token4   = EA_CHAR16("test");
+		const char16_t* pCurrent;
+		size_t          n;
+
+		// Test the first string
+		pCurrent = Strtok2(teststr, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token2, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token3, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token4, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the second string
+		pCurrent = Strtok2(teststr2, delim, &n, true);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the third string
+		pCurrent = Strtok2(teststr3, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+	}
+	{
+		const char32_t* teststr  = EA_CHAR32("  Hello /// This/is++a test");
+		const char32_t* teststr2 = EA_CHAR32("  ///  ");
+		const char32_t* teststr3 = EA_CHAR32("  /// Hello ");
+		const char32_t* delim    = EA_CHAR32("/ %");
+		const char32_t* token1   = EA_CHAR32("Hello");
+		const char32_t* token2   = EA_CHAR32("This");
+		const char32_t* token3   = EA_CHAR32("is++a");
+		const char32_t* token4   = EA_CHAR32("test");
+		const char32_t* pCurrent;
+		size_t          n;
+
+		// Test the first string
+		pCurrent = Strtok2(teststr, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token2, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token3, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(Strncmp(token4, pCurrent, 4) == 0);
+		EATEST_VERIFY(n == 4);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the second string
+		pCurrent = Strtok2(teststr2, delim, &n, true);
+		EATEST_VERIFY(pCurrent == NULL);
+
+		// Test the third string
+		pCurrent = Strtok2(teststr3, delim, &n, true);
+		EATEST_VERIFY(Strncmp(token1, pCurrent, 5) == 0);
+		EATEST_VERIFY(n == 5);
+
+		pCurrent = Strtok2(pCurrent, delim, &n, false);
+		EATEST_VERIFY(pCurrent == NULL);
+	}
+
+
+	// char8_t*  Strset(char8_t*  pString, int      c);
+	// char16_t* Strset(char16_t* pString, char16_t c);
+	// char32_t* Strset(char32_t* pString, char32_t c);
+	{
+		char8_t s8[] = ",.:1:2.3,4";
+
+		EATEST_VERIFY(Strset(s8, '^') == s8);
+		EATEST_VERIFY(Memcmp(s8, "^^^^^^^^^^", Strlen(s8)) == 0);
+	}
+	{
+		char16_t s16[16]; Strlcpy(s16, EA_CHAR16(",.:1:2.3,4"), EAArrayCount(s16)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strset(s16, '^') == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("^^^^^^^^^^"), Strlen(s16)) == 0);
+	}
+	{
+		char32_t s32[32]; Strlcpy(s32, EA_CHAR32(",.:1:2.3,4"), EAArrayCount(s32)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strset(s32, '^') == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("^^^^^^^^^^"), Strlen(s32)) == 0);
+	}
+
+
+	// char8_t*  Strnset(char8_t*  pString, int      c, size_t n);
+	// char16_t* Strnset(char16_t* pString, char16_t c, size_t n);
+	// char32_t* Strnset(char32_t* pString, char32_t c, size_t n);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		char8_t buffer8[32] = {};
+		Strnset(buffer8, 'a', 4);
+
+		char16_t buffer16[32] = {};
+		Strnset(buffer16, 'a', 4);
+
+		char32_t buffer32[32] = {};
+		Strnset(buffer32, 'a', 4);
+	}
+
+
+	// char8_t*  Strrev(char8_t*  pString);
+	// char16_t* Strrev(char16_t* pString);
+	// char32_t* Strrev(char32_t* pString);
+	{
+		char8_t s8[32];
+		char8_t sEmpty[1] = { 0 };
+
+		EATEST_VERIFY(Strlen(Strrev(sEmpty)) == 0);
+
+		Strcpy(s8, "abcdefgh");
+		EATEST_VERIFY(Strrev(s8) == s8);
+		EATEST_VERIFY(Memcmp(s8, "hgfedcba", Strlen(s8)) == 0);
+
+		Strcpy(s8, "abcdefg");
+		EATEST_VERIFY(Strrev(s8) == s8);
+		EATEST_VERIFY(Memcmp(s8, "gfedcba", Strlen(s8)) == 0);
+
+		Strcpy(s8, "a");
+		EATEST_VERIFY(Strrev(s8) == s8);
+		EATEST_VERIFY(Memcmp(s8, "a", Strlen(s8)) == 0);
+	}
+	{
+		char16_t s16[32];
+		char16_t sEmpty[1] = { 0 };
+
+		EATEST_VERIFY(Strlen(Strrev(sEmpty)) == 0);
+
+		Strcpy(s16, EA_CHAR16("abcdefgh"));
+		EATEST_VERIFY(Strrev(s16) == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("hgfedcba"), Strlen(s16)) == 0);
+
+		Strcpy(s16, EA_CHAR16("abcdefg"));
+		EATEST_VERIFY(Strrev(s16) == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("gfedcba"), Strlen(s16)) == 0);
+
+		Strcpy(s16, EA_CHAR16("a"));
+		EATEST_VERIFY(Strrev(s16) == s16);
+		EATEST_VERIFY(Memcmp(s16, EA_CHAR16("a"), Strlen(s16)) == 0);
+	}
+	{
+		char32_t s32[32];
+		char32_t sEmpty[1] = { 0 };
+
+		EATEST_VERIFY(Strlen(Strrev(sEmpty)) == 0);
+
+		Strcpy(s32, EA_CHAR32("abcdefgh"));
+		EATEST_VERIFY(Strrev(s32) == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("hgfedcba"), Strlen(s32)) == 0);
+
+		Strcpy(s32, EA_CHAR32("abcdefg"));
+		EATEST_VERIFY(Strrev(s32) == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("gfedcba"), Strlen(s32)) == 0);
+
+		Strcpy(s32, EA_CHAR32("a"));
+		EATEST_VERIFY(Strrev(s32) == s32);
+		EATEST_VERIFY(Memcmp(s32, EA_CHAR32("a"), Strlen(s32)) == 0);
+	}
+
+
+	// int Strcmp(const char8_t*  pString1, const char8_t*  pString2);
+	// int Strcmp(const char16_t* pString1, const char16_t* pString2);
+	// int Strcmp(const char32_t* pString1, const char32_t* pString2);
+	{
+		char8_t buffer1[] = "01234567a"; 
+		char8_t buffer2[] = "01234567b"; 
+		char8_t buffer3[] = "01234567c";
+
+		EATEST_VERIFY(Strcmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Strcmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Strcmp(buffer1, buffer2) <  0);
+	}
+	{
+		char16_t buffer1[12]; Strlcpy(buffer1, EA_CHAR16("01234567a"), EAArrayCount(buffer1)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer2[12]; Strlcpy(buffer2, EA_CHAR16("01234567b"), EAArrayCount(buffer2)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer3[12]; Strlcpy(buffer3, EA_CHAR16("01234567c"), EAArrayCount(buffer3)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strcmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Strcmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Strcmp(buffer1, buffer2) <  0);
+	}
+	{
+		char32_t buffer1[12]; Strlcpy(buffer1, EA_CHAR32("01234567a"), EAArrayCount(buffer1)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer2[12]; Strlcpy(buffer2, EA_CHAR32("01234567b"), EAArrayCount(buffer2)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer3[12]; Strlcpy(buffer3, EA_CHAR32("01234567c"), EAArrayCount(buffer3)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strcmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Strcmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Strcmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Strcmp(buffer1, buffer2) <  0);
+	}
+
+	{
+		// Extended Strcmp char8_t testing for our optimized version.
+		// To do: Use a page-protected heap to test that Strcmp isn't reading beyond a page's boundaries.
+		eastl::string8     s1;
+		eastl::string8     s2;
+		EA::UnitTest::Rand rand(1234);
+
+		for(int i = 0; i < 20; i++)
+		{
+			bool         makeEqualLength = (rand.RandLimit(3) == 0); // A third of the time
+			bool         makeEqualValue  = (rand.RandLimit(2) == 0); // Half of the time
+			eastl_size_t s1Length        = rand.RandLimit(131072);
+
+			s1.resize(s1Length);
+
+			if(makeEqualLength)
+				s2.resize(s1Length);
+			else
+				s2.resize(rand.RandLimit(131072));
+
+			for(eastl_size_t j = 0; j < s1Length; j++)
+			{
+				s1[j] = (char8_t)rand.RandRange(CHAR_MIN, CHAR_MAX + 1);
+
+				if(s1[j] == 0) // We can't have a 0 char, as that's the C string terminator char.
+					s1[j] = 'x';
+			}
+			s2.assign(s1, 0, eastl::min_alt(s1.size(), s2.size()));
+
+			if(!makeEqualValue)
+			{
+				eastl_size_t pos = rand.RandLimit(static_cast<uint32_t>(s2.length()));
+
+				s2[pos] = ~s2[pos]; // Change one character randomly in the string.
+
+				if(s2[pos] == 0) // We can't have a 0 char, as that's the C string terminator char.
+					s2[pos] = 'x';
+			}
+
+			const int eaValue  = Strcmp(s1.c_str(), s2.c_str()); // Our strcmp returns <0, 0, >0.
+			const int stdValue = strcmp(s1.c_str(), s2.c_str()); // Some strcmp versions return -1, 0, +1, so our verify below can't just compare values.
+			EATEST_VERIFY_F(((eaValue < 0) == (stdValue < 0)) && ((eaValue > 0) == (stdValue > 0)), "Strcmp failure for iteration %d.", i);
+		}
+	}
+
+	{
+		// Extended Strcmp char16_t testing for our optimized version.
+		// To do: Use a page-protected heap to test that Strcmp isn't reading beyond a page's boundaries.
+		eastl::string16    s1;
+		eastl::string16    s2;
+		EA::UnitTest::Rand rand(1234);
+
+		for(int i = 0; i < 20; i++)
+		{
+			bool         makeEqualLength = (rand.RandLimit(3) == 0); // A third of the time
+			bool         makeEqualValue  = (rand.RandLimit(2) == 0); // Half of the time
+			eastl_size_t s1Length        = rand.RandLimit(131072);
+
+			s1.resize(s1Length);
+
+			if(makeEqualLength)
+				s2.resize(s1Length);
+			else
+				s2.resize(rand.RandLimit(131072));
+
+			for(eastl_size_t j = 0; j < s1Length; j++)
+			{
+				s1[j] = (char16_t)rand.RandRange(0, 32768); // There is no CHAR16_MIN, so we pick safe portable limits.
+
+				if(s1[j] == 0) // We can't have a 0 char, as that's the C string terminator char.
+					s1[j] = 'x';
+			}
+			s2.assign(s1, 0, eastl::min_alt(s1.size(), s2.size()));
+
+			if(!makeEqualValue)
+			{
+				eastl_size_t pos = rand.RandLimit(static_cast<uint32_t>(s2.length()));
+
+				s2[pos] = ~s2[pos]; // Change one character randomly in the string.
+
+				if(s2[pos] == 0) // We can't have a 0 char, as that's the C string terminator char.
+					s2[pos] = 'x';
+			}
+
+			#if defined(EA_PLATFORM_MICROSOFT) // Not all platforms provide a 16 bit wchar_t and wcscmp to go with it.
+				const int eaValue  = Strcmp(s1.c_str(), s2.c_str()); // Our strcmp returns <0, 0, >0.
+				const int stdValue = wcscmp(reinterpret_cast<const wchar_t*>(s1.c_str()), reinterpret_cast<const wchar_t*>(s2.c_str())); // Some strcmp versions return -1, 0, +1, so our verify below can't just compare values.
+				EATEST_VERIFY_F(((eaValue < 0) == (stdValue < 0)) && ((eaValue > 0) == (stdValue > 0)), "Strcmp failure for iteration %d.", i);
+			#else
+				// To consider: Implement some fallback validation.
+			#endif
+		}
+	}
+
+
+	// int Strncmp(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+	// int Strncmp(const char16_t* pString1, const char16_t* pString2, size_t n);
+	// int Strncmp(const char32_t* pString1, const char32_t* pString2, size_t n);
+	{
+		char8_t buffer1[] = "01234567abc"; 
+		char8_t buffer2[] = "01234567bbc"; 
+		char8_t buffer3[] = "01234567cbc"; 
+
+		EATEST_VERIFY(Strncmp(buffer1, buffer1, 12) == 0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer1, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer3, buffer2, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer3, 12) <  0);
+		EATEST_VERIFY(Strncmp(buffer1, buffer2, 12) <  0);
+	}
+	{
+		char16_t buffer1[16]; Strlcpy(buffer1, EA_CHAR16("01234567abc"), EAArrayCount(buffer1)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer2[16]; Strlcpy(buffer2, EA_CHAR16("01234567bbc"), EAArrayCount(buffer2)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer3[16]; Strlcpy(buffer3, EA_CHAR16("01234567cbc"), EAArrayCount(buffer3)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+ 
+		EATEST_VERIFY(Strncmp(buffer1, buffer1, 12) == 0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer1, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer3, buffer2, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer3, 12) <  0);
+		EATEST_VERIFY(Strncmp(buffer1, buffer2, 12) <  0);
+	}
+	{
+		char32_t buffer1[32]; Strlcpy(buffer1, EA_CHAR32("01234567abc"), EAArrayCount(buffer1)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer2[32]; Strlcpy(buffer2, EA_CHAR32("01234567bbc"), EAArrayCount(buffer2)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer3[32]; Strlcpy(buffer3, EA_CHAR32("01234567cbc"), EAArrayCount(buffer3)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+ 
+		EATEST_VERIFY(Strncmp(buffer1, buffer1, 12) == 0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer1, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer3, buffer2, 12) >  0);
+		EATEST_VERIFY(Strncmp(buffer2, buffer3, 12) <  0);
+		EATEST_VERIFY(Strncmp(buffer1, buffer2, 12) <  0);
+	}
+
+
+	// int Stricmp(const char8_t*  pString1, const char8_t*  pString2);
+	// int Stricmp(const char16_t* pString1, const char16_t* pString2);
+	// int Stricmp(const char32_t* pString1, const char32_t* pString2);
+	{
+		char8_t buffer1[] = "01asdf67A";
+		char8_t buffer2[] = "01aSDf67b";
+		char8_t buffer3[] = "01AsdF67C";
+
+		EATEST_VERIFY(Stricmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Stricmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Stricmp(buffer1, buffer2) <  0);
+	}
+	{
+		char16_t buffer1[16]; Strlcpy(buffer1, EA_CHAR16("01asdf67A"), EAArrayCount(buffer1)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer2[16]; Strlcpy(buffer2, EA_CHAR16("01aSDf67b"), EAArrayCount(buffer2)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer3[16]; Strlcpy(buffer3, EA_CHAR16("01AsdF67C"), EAArrayCount(buffer3)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Stricmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Stricmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Stricmp(buffer1, buffer2) <  0);
+	}
+	{
+		char32_t buffer1[32]; Strlcpy(buffer1, EA_CHAR32("01asdf67A"), EAArrayCount(buffer1)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer2[32]; Strlcpy(buffer2, EA_CHAR32("01aSDf67b"), EAArrayCount(buffer2)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer3[32]; Strlcpy(buffer3, EA_CHAR32("01AsdF67C"), EAArrayCount(buffer3)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Stricmp(buffer1, buffer1) == 0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer1) >  0);
+		EATEST_VERIFY(Stricmp(buffer3, buffer2) >  0);
+		EATEST_VERIFY(Stricmp(buffer2, buffer3) <  0);
+		EATEST_VERIFY(Stricmp(buffer1, buffer2) <  0);
+	}
+
+
+	// int Strnicmp(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+	// int Strnicmp(const char16_t* pString1, const char16_t* pString2, size_t n);
+	// int Strnicmp(const char32_t* pString1, const char32_t* pString2, size_t n);
+	{
+		char8_t buffer1[] = "01asdf67AAsWE";
+		char8_t buffer2[] = "01aSDf67basWe";
+		char8_t buffer3[] = "01AsdF67CaSwe";
+
+		EATEST_VERIFY(Strnicmp(buffer1, buffer1, 13) == 0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer1, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer3, buffer2, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer3, 13) <  0);
+		EATEST_VERIFY(Strnicmp(buffer1, buffer2, 13) <  0);
+	}
+	{
+		char16_t buffer1[24]; Strlcpy(buffer1, EA_CHAR16("01asdf67AAsWE"), EAArrayCount(buffer1)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer2[24]; Strlcpy(buffer2, EA_CHAR16("01aSDf67basWe"), EAArrayCount(buffer2)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t buffer3[24]; Strlcpy(buffer3, EA_CHAR16("01AsdF67CaSwe"), EAArrayCount(buffer3)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+
+		EATEST_VERIFY(Strnicmp(buffer1, buffer1, 13) == 0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer1, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer3, buffer2, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer3, 13) <  0);
+		EATEST_VERIFY(Strnicmp(buffer1, buffer2, 13) <  0);
+	}
+	{
+		char32_t buffer1[24]; Strlcpy(buffer1, EA_CHAR32("01asdf67AAsWE"), EAArrayCount(buffer1)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer2[24]; Strlcpy(buffer2, EA_CHAR32("01aSDf67basWe"), EAArrayCount(buffer2)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t buffer3[24]; Strlcpy(buffer3, EA_CHAR32("01AsdF67CaSwe"), EAArrayCount(buffer3)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+
+		EATEST_VERIFY(Strnicmp(buffer1, buffer1, 13) == 0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer1, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer3, buffer2, 13) >  0);
+		EATEST_VERIFY(Strnicmp(buffer2, buffer3, 13) <  0);
+		EATEST_VERIFY(Strnicmp(buffer1, buffer2, 13) <  0);
+	}
+
+
+	// int StrcmpAlnum(const char8_t*  pString1, const char8_t*  pString2);
+	// int StrcmpAlnum(const char16_t* pString1, const char16_t* pString2);
+	// No 32 bit version because this function is deprecated.
+	{
+		EATEST_VERIFY(StrcmpAlnum("",       "")         == 0);
+		EATEST_VERIFY(StrcmpAlnum("abc",    "abc")      == 0);
+		EATEST_VERIFY(StrcmpAlnum("a",      "b")        < 0);
+		EATEST_VERIFY(StrcmpAlnum("abc",    "abcd")     < 0);
+		EATEST_VERIFY(StrcmpAlnum("abcd",   "abc")      > 0);
+		EATEST_VERIFY(StrcmpAlnum("a",      "")         > 0);
+		EATEST_VERIFY(StrcmpAlnum("",       "1")        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StrcmpAlnum("abc",    "abd")      < 0);
+		EATEST_VERIFY(StrcmpAlnum("a",      "1")        < 0);
+		EATEST_VERIFY(StrcmpAlnum("%",      "1")        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StrcmpAlnum("103",    "12")       > 0);       // Verify that numeric compare is occuring.
+		EATEST_VERIFY(StrcmpAlnum("abc12a", "abc12b")   < 0);
+		EATEST_VERIFY(StrcmpAlnum("abc123", "abc12a")   > 0);
+		EATEST_VERIFY(StrcmpAlnum("abc-2",  "abc-1")    > 0);       // Verify that '-' is not treated as a minus sign.
+		EATEST_VERIFY(StrcmpAlnum("abc1.1", "abc1.02")  < 0);       // Verify that '.' is not treated as a decimal point.
+		EATEST_VERIFY(StrcmpAlnum("44",     "044")      == 0);      // Verify that digit spans are treated as base 10 numbers.
+	}
+	{
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16(""),       EA_CHAR16(""))         == 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc"),    EA_CHAR16("abc"))      == 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("a"),      EA_CHAR16("b"))        < 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc"),    EA_CHAR16("abcd"))     < 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abcd"),   EA_CHAR16("abc"))      > 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("a"),      EA_CHAR16(""))         > 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16(""),       EA_CHAR16("1"))        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc"),    EA_CHAR16("abd"))      < 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("a"),      EA_CHAR16("1"))        < 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("%"),      EA_CHAR16("1"))        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("103"),    EA_CHAR16("12"))       > 0);       // Verify that numeric compare is occuring.
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc12a"), EA_CHAR16("abc12b"))   < 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc123"), EA_CHAR16("abc12a"))   > 0);
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc-2"),  EA_CHAR16("abc-1"))    > 0);       // Verify that '-' is not treated as a minus sign.
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("abc1.1"), EA_CHAR16("abc1.02"))  < 0);       // Verify that '.' is not treated as a decimal point.
+		EATEST_VERIFY(StrcmpAlnum(EA_CHAR16("44"),     EA_CHAR16("044"))      == 0);      // Verify that digit spans are treated as base 10 numbers.
+	}
+
+
+	// int StricmpAlnum(const char8_t*  pString1, const char8_t*  pString2);
+	// int StricmpAlnum(const char16_t* pString1, const char16_t* pString2);
+	// No 32 bit version because this function is deprecated.
+	{
+		EATEST_VERIFY(StricmpAlnum("",       "")         == 0);
+		EATEST_VERIFY(StricmpAlnum("abc",    "abc")      == 0);
+		EATEST_VERIFY(StricmpAlnum("a",      "b")        < 0);
+		EATEST_VERIFY(StricmpAlnum("abc",    "abcd")     < 0);
+		EATEST_VERIFY(StricmpAlnum("abcd",   "abc")      > 0);
+		EATEST_VERIFY(StricmpAlnum("a",      "")         > 0);
+		EATEST_VERIFY(StricmpAlnum("",       "1")        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StricmpAlnum("abc",    "abd")      < 0);
+		EATEST_VERIFY(StricmpAlnum("a",      "1")        < 0);
+		EATEST_VERIFY(StricmpAlnum("%",      "1")        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StricmpAlnum("103",    "12")       > 0);       // Verify that numeric compare is occuring.
+		EATEST_VERIFY(StricmpAlnum("abc12a", "abc12b")   < 0);
+		EATEST_VERIFY(StricmpAlnum("abc123", "abc12a")   > 0);
+		EATEST_VERIFY(StricmpAlnum("abc-2",  "abc-1")    > 0);       // Verify that '-' is not treated as a minus sign.
+		EATEST_VERIFY(StricmpAlnum("abc1.1", "abc1.02")  < 0);       // Verify that '.' is not treated as a decimal point.
+		EATEST_VERIFY(StricmpAlnum("44",     "044")      == 0);      // Verify that digit spans are treated as base 10 numbers.
+	}
+	{
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16(""),       EA_CHAR16(""))         == 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("aBc"),    EA_CHAR16("abc"))      == 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("a"),      EA_CHAR16("b"))        < 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("ABc"),    EA_CHAR16("aBCd"))     < 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("abcd"),   EA_CHAR16("abc"))      > 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("a"),      EA_CHAR16(""))         > 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16(""),       EA_CHAR16("1"))        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("abC"),    EA_CHAR16("aBd"))      < 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("a"),      EA_CHAR16("1"))        < 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("%"),      EA_CHAR16("1"))        < 0);       // Verify numerics evaluate highest.
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("103"),    EA_CHAR16("12"))       > 0);       // Verify that numeric compare is occuring.
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("abc12A"), EA_CHAR16("aBC12b"))   < 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("abc123"), EA_CHAR16("abc12a"))   > 0);
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("ABc-2"),  EA_CHAR16("abc-1"))    > 0);       // Verify that '-' is not treated as a minus sign.
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("abC1.1"), EA_CHAR16("abc1.02"))  < 0);       // Verify that '.' is not treated as a decimal point.
+		EATEST_VERIFY(StricmpAlnum(EA_CHAR16("44"),     EA_CHAR16("044"))      == 0);      // Verify that digit spans are treated as base 10 numbers.
+	}
+
+
+	// int Strcoll(const char8_t*  pString1, const char8_t*  pString2);
+	// int Strcoll(const char16_t* pString1, const char16_t* pString2);
+	// int Strcoll(const char32_t* pString1, const char32_t* pString2);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Strcoll("Hello",  "world");
+		Strcoll(EA_CHAR16("Hello"), EA_CHAR16("world"));
+		Strcoll(EA_CHAR32("Hello"), EA_CHAR32("world"));
+	}
+
+
+	// int Strncoll(const char8_t*  pString1, const char8_t*  pString2, size_t n);
+	// int Strncoll(const char16_t* pString1, const char16_t* pString2, size_t n);
+	// int Strncoll(const char32_t* pString1, const char32_t* pString2, size_t n);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Strncoll("Hello",  "world", 1);
+		Strncoll(EA_CHAR16("Hello"), EA_CHAR16("world"), 1);
+		Strncoll(EA_CHAR32("Hello"), EA_CHAR32("world"), 1);
+	}
+
+
+	// int Stricoll(const char8_t*  pString1, const char8_t*  pString2);
+	// int Stricoll(const char16_t* pString1, const char16_t* pString2);
+	// int Stricoll(const char32_t* pString1, const char32_t* pString2);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Stricoll("Hello",  "world");
+		Stricoll(EA_CHAR16("Hello"), EA_CHAR16("world"));
+		Stricoll(EA_CHAR32("Hello"), EA_CHAR32("world"));
+	}
+
+
+	// int Strnicoll(const char8_t*  pString1, const char8_t*  pString1, size_t n);
+	// int Strnicoll(const char16_t* pString1, const char16_t* pString1, size_t n);
+	// int Strnicoll(const char32_t* pString1, const char32_t* pString1, size_t n);
+	{
+		// To do: Make a real test. For now we merely verify that we can call the function.
+		Strnicoll("Hello", "world", 1);
+		Strnicoll(EA_CHAR16("Hello"), EA_CHAR16("world"), 1);
+		Strnicoll(EA_CHAR32("Hello"), EA_CHAR32("world"), 1);
+	}
+
+	return nErrorCount;
+}
+EA_RESTORE_VC_WARNING()
+
+
+static int TestEcvt()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// These tests all require a buffer of at least 350 in size to be used
+
+	// char8_t*  EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char8_t* buffer);
+	// char16_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char16_t* buffer);
+	// char32_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char32_t* buffer);
+	{
+		char8_t buffer[kEcvtBufMaxSize];
+		int decimalPos, sign;
+
+		EcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+	{
+		char16_t buffer[kEcvtBufMaxSize];
+		int decimalPos, sign;
+
+		EcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+	{
+		char32_t buffer[kEcvtBufMaxSize];
+		int decimalPos, sign;
+
+		EcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+
+
+	// char8_t*  FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char8_t*  buffer);
+	// char16_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char16_t* buffer);
+	// char32_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char32_t* buffer);
+	{
+		char8_t buffer[kFcvtBufMaxSize];
+		int decimalPos, sign;
+
+		FcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+	{
+		char16_t buffer[kFcvtBufMaxSize];
+		int decimalPos, sign;
+
+		FcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+	{
+		char32_t buffer[kFcvtBufMaxSize];
+		int decimalPos, sign;
+
+		FcvtBuf(1.0, 10, &decimalPos, &sign, buffer);
+		EATEST_VERIFY(buffer[0] == '1');
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestItoa()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// char8_t*  I32toa(int32_t nValue, char8_t*  pResult, int nBase);
+	// char16_t* I32toa(int32_t nValue, char16_t* pResult, int nBase);
+	// char32_t* I32toa(int32_t nValue, char32_t* pResult, int nBase);
+	{
+		char8_t sn8[32];
+
+		EATEST_VERIFY(I32toa(INT32_MIN, sn8, 10) != NULL); // Can't express INT32_MIN as a numeric constant.
+		EATEST_VERIFY_F(Strcmp(sn8, "-2147483648") == 0, "I32toa(INT32_MIN, sn8, 10) failed; produced %s instead of -2147483648", sn8);
+		EATEST_VERIFY(I32toa(2147483647, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "2147483647") == 0);
+		EATEST_VERIFY(I32toa(-2000000000, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "-2000000000") == 0);
+		EATEST_VERIFY(I32toa(2000000000, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "2000000000") == 0);
+		EATEST_VERIFY(I32toa(0x77359400, sn8, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "77359400") == 0);
+		EATEST_VERIFY(I32toa(0xeeee, sn8, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "1110111011101110") == 0);
+
+		// Test random values, in order to exercise our optimized base 10 implementation.
+		RandomFast     random;
+		LimitStopwatch timer(Stopwatch::kUnitsSeconds, 2, true);
+
+		while(!timer.IsTimeUp())
+		{
+			int32_t value = (int32_t)random.RandomUint32Uniform();
+			I32toa(value, sn8, 10);
+			int32_t value2 = AtoI32(sn8);
+			EATEST_VERIFY(value == value2);
+		}
+
+		// Test speed of optimized base 10 implementation.
+		Stopwatch stopwatch(Stopwatch::kUnitsCPUCycles, true);
+		for(int i = 0; i < 10000; i++)
+			I32toa((int32_t)random.RandomUint32Uniform(), sn8, 10);
+		EA::UnitTest::ReportVerbosity(2, "I32toa time: %I64u cycles.\n", stopwatch.GetElapsedTime());
+	}
+	{
+		char16_t sn16[32];
+
+		EATEST_VERIFY(I32toa(INT32_MIN, sn16, 10) != NULL); // Can't express INT32_MIN as a numeric constant.
+		EATEST_VERIFY_F(Strcmp(sn16, EA_CHAR16("-2147483648")) == 0, "I32toa(INT32_MIN, sn16, 10) failed; produced %I16s instead of -2147483648", sn16);
+		EATEST_VERIFY(I32toa(2147483647, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("2147483647")) == 0);
+		EATEST_VERIFY(I32toa(-2000000000, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("-2000000000")) == 0);
+		EATEST_VERIFY(I32toa(2000000000, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("2000000000")) == 0);
+		EATEST_VERIFY(I32toa(0x77359400, sn16, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("77359400")) == 0);
+		EATEST_VERIFY(I32toa(0xeeee, sn16, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("1110111011101110")) == 0);
+	}
+	{
+		char32_t sn32[32];
+
+		EATEST_VERIFY(I32toa(INT32_MIN, sn32, 10) != NULL); // Can't express INT32_MIN as a numeric constant.
+		EATEST_VERIFY_F(Strcmp(sn32, EA_CHAR32("-2147483648")) == 0, "I32toa(INT32_MIN, sn32, 10) failed; produced %I16s instead of -2147483648", sn32);
+		EATEST_VERIFY(I32toa(2147483647, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("2147483647")) == 0);
+		EATEST_VERIFY(I32toa(-2000000000, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("-2000000000")) == 0);
+		EATEST_VERIFY(I32toa(2000000000, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("2000000000")) == 0);
+		EATEST_VERIFY(I32toa(0x77359400, sn32, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("77359400")) == 0);
+		EATEST_VERIFY(I32toa(0xeeee, sn32, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("1110111011101110")) == 0);
+	}
+
+
+	// char8_t*  U32toa(uint32_t nValue, char8_t*  pResult, int nBase);
+	// char16_t* U32toa(uint32_t nValue, char16_t* pResult, int nBase);
+	// char32_t* U32toa(uint32_t nValue, char32_t* pResult, int nBase);
+	{
+		char8_t sn8[32];
+
+		EATEST_VERIFY(U32toa(0, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "0") == 0);
+		EATEST_VERIFY(U32toa(4294967295U, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "4294967295") == 0);
+		EATEST_VERIFY(U32toa(UINT32_C(4000000000), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "4000000000") == 0);
+		EATEST_VERIFY(U32toa(2000000000, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "2000000000") == 0);
+		EATEST_VERIFY(U32toa(0x77359400, sn8, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "77359400") == 0);
+		EATEST_VERIFY(U32toa(0xeeee, sn8, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "1110111011101110") == 0);
+
+		// Test random values, in order to exercise our optimized base 10 implementation.
+		RandomFast     random;
+		LimitStopwatch timer(Stopwatch::kUnitsSeconds, 2, true);
+
+		while(!timer.IsTimeUp())
+		{
+			uint32_t value = random.RandomUint32Uniform();
+			U32toa(value, sn8, 10);
+			uint32_t value2 = AtoU32(sn8);
+			EATEST_VERIFY(value == value2);
+		}
+	}
+	{
+		char16_t sn16[32];
+
+		EATEST_VERIFY(U32toa(0, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("0")) == 0);
+		EATEST_VERIFY(U32toa(4294967295U, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("4294967295")) == 0);
+		EATEST_VERIFY(U32toa(UINT32_C(4000000000), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("4000000000")) == 0);
+		EATEST_VERIFY(U32toa(2000000000, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("2000000000")) == 0);
+		EATEST_VERIFY(U32toa(0x77359400, sn16, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("77359400")) == 0);
+		EATEST_VERIFY(U32toa(0xeeee, sn16, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("1110111011101110")) == 0);
+	}
+	{
+		char32_t sn32[32];
+
+		EATEST_VERIFY(U32toa(0, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("0")) == 0);
+		EATEST_VERIFY(U32toa(4294967295U, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("4294967295")) == 0);
+		EATEST_VERIFY(U32toa(UINT32_C(4000000000), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("4000000000")) == 0);
+		EATEST_VERIFY(U32toa(2000000000, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("2000000000")) == 0);
+		EATEST_VERIFY(U32toa(0x77359400, sn32, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("77359400")) == 0);
+		EATEST_VERIFY(U32toa(0xeeee, sn32, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("1110111011101110")) == 0);
+	}
+
+
+	// char8_t*  I64toa(int64_t nValue, char8_t*  pBuffer, int nBase);
+	// char16_t* I64toa(int64_t nValue, char16_t* pBuffer, int nBase);
+	// char32_t* I64toa(int64_t nValue, char32_t* pBuffer, int nBase);
+	{
+		char8_t sn8[128];
+
+		EATEST_VERIFY(I64toa(INT64_MIN, sn8, 10) != NULL); // Can't express INT64_MIN as a numeric constant.
+		EATEST_VERIFY(Strcmp(sn8, "-9223372036854775808") == 0);
+		EATEST_VERIFY(I64toa(INT64_C(9223372036854775807), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "9223372036854775807") == 0);
+		EATEST_VERIFY(I64toa(INT64_C(-20000000000000), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "-20000000000000") == 0);
+		EATEST_VERIFY(I64toa(INT64_C(20000000000000), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "20000000000000") == 0);
+		EATEST_VERIFY(I64toa(INT64_C(0x7735940012345), sn8, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "7735940012345") == 0);
+		EATEST_VERIFY(I64toa(0xeeee, sn8, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "1110111011101110") == 0);
+
+		// Test random values, in order to exercise our optimized base 10 implementation.
+		RandomFast     random;
+		LimitStopwatch timer(Stopwatch::kUnitsSeconds, 2, true);
+
+		while(!timer.IsTimeUp())
+		{
+			int64_t value = (int64_t)(((uint64_t)random.RandomUint32Uniform() << 32) | random.RandomUint32Uniform());
+			I64toa(value, sn8, 10);
+			int64_t value2 = AtoI64(sn8);
+			EATEST_VERIFY(value == value2);
+		}
+	}
+	{
+		char16_t sn16[128];
+
+		EATEST_VERIFY(I64toa(INT64_MIN, sn16, 10) != NULL); // Can't express INT64_MIN as a numeric constant.
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("-9223372036854775808")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(9223372036854775807), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("9223372036854775807")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(-20000000000000), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("-20000000000000")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(20000000000000), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("20000000000000")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(0x7735940012345), sn16, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("7735940012345")) == 0);
+		EATEST_VERIFY(I64toa(0xeeee, sn16, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("1110111011101110")) == 0);
+	}
+	{
+		char32_t sn32[128];
+
+		EATEST_VERIFY(I64toa(INT64_MIN, sn32, 10) != NULL); // Can't express INT64_MIN as a numeric constant.
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("-9223372036854775808")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(9223372036854775807), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("9223372036854775807")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(-20000000000000), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("-20000000000000")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(20000000000000), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("20000000000000")) == 0);
+		EATEST_VERIFY(I64toa(INT64_C(0x7735940012345), sn32, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("7735940012345")) == 0);
+		EATEST_VERIFY(I64toa(0xeeee, sn32, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("1110111011101110")) == 0);
+	}
+
+
+	// char8_t*  U64toa(uint64_t nValue, char8_t*  pBuffer, int nBase);
+	// char16_t* U64toa(uint64_t nValue, char16_t* pBuffer, int nBase);
+	// char32_t* U64toa(uint64_t nValue, char32_t* pBuffer, int nBase);
+	{
+		char8_t sn8[128];
+
+		EATEST_VERIFY(U64toa(0, sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "0") == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(18446744073709551615), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "18446744073709551615") == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(40000000000000), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "40000000000000") == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(20000000000000), sn8, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "20000000000000") == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(0x7735940012345), sn8, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "7735940012345") == 0);
+		EATEST_VERIFY(U64toa(0xeeee, sn8, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn8, "1110111011101110") == 0);
+
+		// Test random values, in order to exercise our optimized base 10 implementation.
+		RandomFast     random;
+		LimitStopwatch timer(Stopwatch::kUnitsSeconds, 2, true);
+
+		while(!timer.IsTimeUp())
+		{
+			uint64_t value = (((uint64_t)random.RandomUint32Uniform() << 32) | random.RandomUint32Uniform());
+			U64toa(value, sn8, 10);
+			uint64_t value2 = AtoU64(sn8);
+			EATEST_VERIFY(value == value2);
+		}
+	}
+	{
+		char16_t sn16[1216];
+
+		EATEST_VERIFY(U64toa(0, sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("0")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(18446744073709551615), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("18446744073709551615")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(40000000000000), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("40000000000000")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(20000000000000), sn16, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("20000000000000")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(0x7735940012345), sn16, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("7735940012345")) == 0);
+		EATEST_VERIFY(U64toa(0xeeee, sn16, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn16, EA_CHAR16("1110111011101110")) == 0);
+	}
+	{
+		char32_t sn32[1232];
+
+		EATEST_VERIFY(U64toa(0, sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("0")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(18446744073709551615), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("18446744073709551615")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(40000000000000), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("40000000000000")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(20000000000000), sn32, 10) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("20000000000000")) == 0);
+		EATEST_VERIFY(U64toa(UINT64_C(0x7735940012345), sn32, 16) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("7735940012345")) == 0);
+		EATEST_VERIFY(U64toa(0xeeee, sn32, 2) != NULL);
+		EATEST_VERIFY(Strcmp(sn32, EA_CHAR32("1110111011101110")) == 0);
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestStrtod()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// double StrtodEnglish(const char8_t*  pString, char8_t**  ppStringEnd);
+	// double StrtodEnglish(const char16_t* pString, char16_t** ppStringEnd);
+	// double StrtodEnglish(const char32_t* pString, char32_t** ppStringEnd);
+	{
+		const char8_t* p;
+		char8_t* pEnd = NULL;
+
+		p = "";
+		EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		// Need a test here which results in HUGE_VAL
+		//p = "";
+		//EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		p = "-111.111";
+		EATEST_VERIFY(DoubleEqual(-111.111, StrtodEnglish(p, &pEnd)));
+
+		p = "111e111";
+		EATEST_VERIFY(DoubleEqual(111e111, StrtodEnglish(p, &pEnd)));
+
+		p = "-111e-111";
+		EATEST_VERIFY(DoubleEqual(-111e-111, StrtodEnglish(p, &pEnd)));
+
+		p = "137";
+		EATEST_VERIFY(DoubleEqual(137, StrtodEnglish(p, &pEnd)));
+
+		p = "999999";
+		EATEST_VERIFY(DoubleEqual(999999, StrtodEnglish(p, &pEnd)));
+
+		p = "123.456";
+		EATEST_VERIFY(DoubleEqual(123.456, StrtodEnglish(p, &pEnd)));
+
+		p = "123232111.000123";
+		EATEST_VERIFY(DoubleEqual(123232111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = "-888111.000123";
+		EATEST_VERIFY(DoubleEqual(-888111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = "-123.456";
+		EATEST_VERIFY(DoubleEqual(-123.456, StrtodEnglish(p, &pEnd)));
+
+		p = "+999999";
+		EATEST_VERIFY(DoubleEqual(+999999, StrtodEnglish(p, &pEnd)));
+
+		p = "+999999e2";
+		EATEST_VERIFY(DoubleEqual(+999999e2, StrtodEnglish(p, &pEnd)));
+
+		p = "-234.567E-3";
+		EATEST_VERIFY(DoubleEqual(-234.567E-3, StrtodEnglish(p, &pEnd)));
+
+		p = "321654.987e11";
+		EATEST_VERIFY(DoubleEqual(321654.987e11, StrtodEnglish(p, &pEnd)));
+
+		p = "-321654.987E0";
+		EATEST_VERIFY(DoubleEqual(-321654.987E0, StrtodEnglish(p, &pEnd)));
+
+		p = "+321654.987e-0";
+		EATEST_VERIFY(DoubleEqual(+321654.987e-0, StrtodEnglish(p, &pEnd)));
+	}
+	{
+		const char16_t* p;
+		char16_t* pEnd = NULL;
+
+		p = EA_CHAR16("");
+		EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		// Need a test here which results in HUGE_VAL
+		//p = "";
+		//EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		p = EA_CHAR16("-111.111");
+		EATEST_VERIFY(DoubleEqual(-111.111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("111e111");
+		EATEST_VERIFY(DoubleEqual(111e111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("-111e-111");
+		EATEST_VERIFY(DoubleEqual(-111e-111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("137");
+		EATEST_VERIFY(DoubleEqual(137, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("999999");
+		EATEST_VERIFY(DoubleEqual(999999, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("123.456");
+		EATEST_VERIFY(DoubleEqual(123.456, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("123232111.000123");
+		EATEST_VERIFY(DoubleEqual(123232111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("-888111.000123");
+		EATEST_VERIFY(DoubleEqual(-888111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("-123.456");
+		EATEST_VERIFY(DoubleEqual(-123.456, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("+999999");
+		EATEST_VERIFY(DoubleEqual(+999999, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("+999999e2");
+		EATEST_VERIFY(DoubleEqual(+999999e2, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("-234.567E-3");
+		EATEST_VERIFY(DoubleEqual(-234.567E-3, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("321654.987e11");
+		EATEST_VERIFY(DoubleEqual(321654.987e11, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("-321654.987E0");
+		EATEST_VERIFY(DoubleEqual(-321654.987E0, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR16("+321654.987e-0");
+		EATEST_VERIFY(DoubleEqual(+321654.987e-0, StrtodEnglish(p, &pEnd)));
+	}
+	{
+		const char32_t* p;
+		char32_t* pEnd = NULL;
+
+		p = EA_CHAR32("");
+		EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		// Need a test here which results in HUGE_VAL
+		//p = "";
+		//EATEST_VERIFY(0 == StrtodEnglish(p, &pEnd));
+
+		p = EA_CHAR32("-111.111");
+		EATEST_VERIFY(DoubleEqual(-111.111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("111e111");
+		EATEST_VERIFY(DoubleEqual(111e111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("-111e-111");
+		EATEST_VERIFY(DoubleEqual(-111e-111, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("137");
+		EATEST_VERIFY(DoubleEqual(137, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("999999");
+		EATEST_VERIFY(DoubleEqual(999999, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("123.456");
+		EATEST_VERIFY(DoubleEqual(123.456, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("123232111.000123");
+		EATEST_VERIFY(DoubleEqual(123232111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("-888111.000123");
+		EATEST_VERIFY(DoubleEqual(-888111.000123, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("-123.456");
+		EATEST_VERIFY(DoubleEqual(-123.456, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("+999999");
+		EATEST_VERIFY(DoubleEqual(+999999, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("+999999e2");
+		EATEST_VERIFY(DoubleEqual(+999999e2, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("-234.567E-3");
+		EATEST_VERIFY(DoubleEqual(-234.567E-3, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("323254.987e11");
+		EATEST_VERIFY(DoubleEqual(323254.987e11, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("-323254.987E0");
+		EATEST_VERIFY(DoubleEqual(-323254.987E0, StrtodEnglish(p, &pEnd)));
+
+		p = EA_CHAR32("+323254.987e-0");
+		EATEST_VERIFY(DoubleEqual(+323254.987e-0, StrtodEnglish(p, &pEnd)));
+	}
+
+
+	// double Strtod(const char8_t*  pString, char8_t**  ppStringEnd);
+	// double Strtod(const char16_t* pString, char16_t** ppStringEnd);
+	// double Strtod(const char32_t* pString, char32_t** ppStringEnd);
+	{
+		char8_t  sn18[] = "-111.111";
+		char8_t* pEnd = NULL;
+		EATEST_VERIFY(DoubleEqual(-111.111, Strtod(sn18, &pEnd)));
+
+		char8_t sn28[] = "111e111";
+		EATEST_VERIFY(DoubleEqual(111e111, Strtod(sn28, &pEnd)));
+
+		char8_t sn38[] = "-111e-111";
+		EATEST_VERIFY(DoubleEqual(-111e-111, Strtod(sn38, &pEnd)));
+	}
+	{
+		char16_t  sn18[16]; Strlcpy(sn18, EA_CHAR16("-111.111"), EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* pEnd = NULL;
+		EATEST_VERIFY(DoubleEqual(-111.111, Strtod(sn18, &pEnd)));
+
+		char16_t sn28[16]; Strlcpy(sn28, EA_CHAR16("111e111"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(DoubleEqual(111e111, Strtod(sn28, &pEnd)));
+
+		char16_t sn38[16]; Strlcpy(sn38, EA_CHAR16("-111e-111"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(DoubleEqual(-111e-111, Strtod(sn38, &pEnd)));
+	}
+	{
+		char32_t  sn18[32]; Strlcpy(sn18, EA_CHAR32("-111.111"), EAArrayCount(sn18)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* pEnd = NULL;
+		EATEST_VERIFY(DoubleEqual(-111.111, Strtod(sn18, &pEnd)));
+
+		char32_t sn28[32]; Strlcpy(sn28, EA_CHAR32("111e111"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(DoubleEqual(111e111, Strtod(sn28, &pEnd)));
+
+		char32_t sn38[32]; Strlcpy(sn38, EA_CHAR32("-111e-111"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(DoubleEqual(-111e-111, Strtod(sn38, &pEnd)));
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestStrtoi()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// int64_t StrtoI64(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+	// int64_t StrtoI64(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+	// int64_t StrtoI64(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+	{
+		int64_t result;
+
+		char8_t  sn18[] = "-1011101110111011101";
+		char8_t* pEnd = NULL;
+		EATEST_VERIFY(INT64_C(-1011101110111011101) == StrtoI64(sn18, &pEnd, 10));
+
+		char8_t sn28[] = "7fffabcdffffabcd";
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoI64(sn28, &pEnd, 16));
+
+		char8_t sn38[] = "101110011011101110111001010000111111100001100101";
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoI64(sn38, &pEnd, 2));
+
+		char8_t sn1[] = "1";
+		for(int base = 2; base <= 36; base++)
+		{
+			result = StrtoI64(sn1, &pEnd, base);
+			EATEST_VERIFY(result != 0); // Currently just test to make sure it didn't crash.
+		}
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+			char8_t pINT64_MIN_10_[] = "-9223372036854775809"; // One less than valid.
+			char8_t pINT64_MIN_10[]  = "-9223372036854775808";
+			char8_t pINT64_MAX_10[]  = "+9223372036854775807";
+			char8_t pINT64_MAX_10_[] = "+9223372036854775808"; // One more than valid.
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+
+
+			char8_t pINT64_MIN_16_[] = "-8000000000000001";    // One less than valid.
+			char8_t pINT64_MIN_16[]  = "-8000000000000000";
+			char8_t pINT64_MAX_16[]  = "+7fffffffffffffff";
+			char8_t pINT64_MAX_16_[] = "+8000000000000000";    // One more than valid.
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+		}
+	}
+	{
+		int64_t result;
+
+		char16_t  sn18[32]; Strlcpy(sn18, EA_CHAR16("-1011101110111011101"), EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* pEnd = NULL;
+		EATEST_VERIFY(INT64_C(-1011101110111011101) == StrtoI64(sn18, &pEnd, 10));
+
+		char16_t sn28[24]; Strlcpy(sn28, EA_CHAR16("7fffabcdffffabcd"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoI64(sn28, &pEnd, 16));
+
+		char16_t sn38[64]; Strlcpy(sn38, EA_CHAR16("101110011011101110111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoI64(sn38, &pEnd, 2));
+
+		char16_t sn1[2] = { '1', 0 };
+		for(int base = 2; base <= 36; base++)
+		{
+			int64_t conversionResult = StrtoI64(sn1, &pEnd, base);
+			EATEST_VERIFY(conversionResult != 0); // Currently just test to make sure it didn't crash.
+		}
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+			char16_t pINT64_MIN_10_[24]; Strlcpy(pINT64_MIN_10_, "-9223372036854775809", EAArrayCount(pINT64_MIN_10_)); 
+			char16_t pINT64_MIN_10[24];  Strlcpy(pINT64_MIN_10 , "-9223372036854775808", EAArrayCount(pINT64_MIN_10)); 
+			char16_t pINT64_MAX_10[24];  Strlcpy(pINT64_MAX_10 , "+9223372036854775807", EAArrayCount(pINT64_MAX_10)); 
+			char16_t pINT64_MAX_10_[24]; Strlcpy(pINT64_MAX_10_, "+9223372036854775808", EAArrayCount(pINT64_MIN_10_)); 
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+
+
+			char16_t pINT64_MIN_16_[24]; Strlcpy(pINT64_MIN_16_, "-8000000000000001", EAArrayCount(pINT64_MIN_16_)); 
+			char16_t pINT64_MIN_16[24] ; Strlcpy(pINT64_MIN_16 , "-8000000000000000", EAArrayCount(pINT64_MIN_16)); 
+			char16_t pINT64_MAX_16[24] ; Strlcpy(pINT64_MAX_16 , "+7fffffffffffffff", EAArrayCount(pINT64_MAX_16)); 
+			char16_t pINT64_MAX_16_[24]; Strlcpy(pINT64_MAX_16_, "+8000000000000000", EAArrayCount(pINT64_MAX_16_)); 
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+		}
+	}
+	{
+		int64_t result;
+ 
+		char32_t  sn18[32]; Strlcpy(sn18, EA_CHAR32("-1011101110111011101"), EAArrayCount(sn18)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* pEnd = NULL;
+		EATEST_VERIFY(INT64_C(-1011101110111011101) == StrtoI64(sn18, &pEnd, 10));
+
+		char32_t sn28[24]; Strlcpy(sn28, EA_CHAR32("7fffabcdffffabcd"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoI64(sn28, &pEnd, 16));
+
+		char32_t sn38[64]; Strlcpy(sn38, EA_CHAR32("101110011011101110111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoI64(sn38, &pEnd, 2));
+
+		char32_t sn1[2] = { '1', 0 };
+		for(int base = 2; base <= 36; base++)
+		{
+			int64_t conversionResult = StrtoI64(sn1, &pEnd, base);
+			EATEST_VERIFY(conversionResult != 0); // Currently just test to make sure it didn't crash.
+		}
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+			char32_t pINT64_MIN_10_[24]; Strlcpy(pINT64_MIN_10_, "-9223372036854775809", EAArrayCount(pINT64_MIN_10_)); 
+			char32_t pINT64_MIN_10[24];  Strlcpy(pINT64_MIN_10 , "-9223372036854775808", EAArrayCount(pINT64_MIN_10)); 
+			char32_t pINT64_MAX_10[24];  Strlcpy(pINT64_MAX_10 , "+9223372036854775807", EAArrayCount(pINT64_MAX_10)); 
+			char32_t pINT64_MAX_10_[24]; Strlcpy(pINT64_MAX_10_, "+9223372036854775808", EAArrayCount(pINT64_MIN_10_)); 
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+
+
+			char32_t pINT64_MIN_16_[24]; Strlcpy(pINT64_MIN_16_, "-8000000000000001", EAArrayCount(pINT64_MIN_16_)); 
+			char32_t pINT64_MIN_16[24] ; Strlcpy(pINT64_MIN_16 , "-8000000000000000", EAArrayCount(pINT64_MIN_16)); 
+			char32_t pINT64_MAX_16[24] ; Strlcpy(pINT64_MAX_16 , "+7fffffffffffffff", EAArrayCount(pINT64_MAX_16)); 
+			char32_t pINT64_MAX_16_[24]; Strlcpy(pINT64_MAX_16_, "+8000000000000000", EAArrayCount(pINT64_MAX_16_)); 
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI64(pINT64_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT64_MAX) && (errno == ERANGE));
+		}
+	}
+
+
+	// uint64_t StrtoU64(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+	// uint64_t StrtoU64(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+	// uint64_t StrtoU64(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+	{
+		uint64_t result;
+
+		char8_t  sn28[] = "7fffabcdffffabcd";
+		char8_t* pEnd = NULL;
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoU64(sn28, &pEnd, 16));
+
+		char8_t sn38[] = "101110011011101110111001010000111111100001100101";
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoU64(sn38, &pEnd, 2));
+
+		char8_t sn48[] = "eb59a646c232da81";
+		EATEST_VERIFY(UINT64_C(0xeb59a646c232da81) == StrtoU64(sn48, &pEnd, 16));
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+		  //char8_t pUINT64_MIN_10_[] =                   "-1";
+			char8_t pUINT64_MIN_10[]  =                    "0";
+			char8_t pUINT64_MAX_10[]  = "18446744073709551615";
+			char8_t pUINT64_MAX_10_[] = "18446744073709551616";
+
+		  // -1 converting to 18446744073709551615 seems to be what conforming Standard C libraries do. So we allow it too.
+		  //errno = 0;
+		  //result =  StrtoU64(pUINT64_MIN_10_, &pEnd, 10);
+		  //EATEST_VERIFY_F((result == 0) && (errno == ERANGE), "StrtoU64: %s -> %I64u errno: %d", pUINT64_MIN_10_, result, (int)errno);
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MIN_10, &pEnd, 10);
+			EATEST_VERIFY_F((result == 0) && (errno == 0), "StrtoU64: %s -> %I64u errno: %d", pUINT64_MIN_10, result, (int)errno);
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MAX_10, &pEnd, 10);
+			EATEST_VERIFY_F((result == UINT64_MAX) && (errno == 0), "StrtoU64: %s -> %I64u errno: %d", pUINT64_MAX_10, result, (int)errno);
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY_F((result == UINT64_MAX) && (errno == ERANGE), "StrtoU64: %s -> %I64u errno: %d", pUINT64_MAX_10_, result, (int)errno);
+
+
+
+		  //char8_t pUINT64_MIN_16_[] =                "-1";
+			char8_t pUINT64_MIN_16[]  =                 "0";
+			char8_t pUINT64_MAX_16[]  =  "ffffffffffffffff";
+			char8_t pUINT64_MAX_16_[] = "10000000000000000";
+
+		  // -1 converting to 18446744073709551615 seems to be what conforming Standard C libraries do. So we allow it too.
+		  //errno = 0;
+		  //result =  StrtoU64(pUINT64_MIN_16_, &pEnd, 16);
+		  //EATEST_VERIFY((result == 0) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == 0) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == UINT64_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU64(pUINT64_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == UINT64_MAX) && (errno == ERANGE));
+		}
+	}
+	{
+		char16_t  sn28[24]; Strlcpy(sn28, EA_CHAR16("7fffabcdffffabcd"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* pEnd = NULL;
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoU64(sn28, &pEnd, 16));
+
+		char16_t sn38[64]; Strlcpy(sn38, EA_CHAR16("101110011011101110111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoU64(sn38, &pEnd, 2));
+
+		char16_t sn48[24]; Strlcpy(sn48, EA_CHAR16("eb59a646c232da81"), EAArrayCount(sn48)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xeb59a646c232da81) == StrtoU64(sn48, &pEnd, 16));
+	}
+	{
+		char32_t  sn28[24]; Strlcpy(sn28, EA_CHAR32("7fffabcdffffabcd"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* pEnd = NULL;
+		EATEST_VERIFY(UINT64_C(0x7fffabcdffffabcd) == StrtoU64(sn28, &pEnd, 16));
+
+		char32_t sn38[64]; Strlcpy(sn38, EA_CHAR32("101110011011101110111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xB9BBB943F865) == StrtoU64(sn38, &pEnd, 2));
+
+		char32_t sn48[24]; Strlcpy(sn48, EA_CHAR32("eb59a646c232da81"), EAArrayCount(sn48)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT64_C(0xeb59a646c232da81) == StrtoU64(sn48, &pEnd, 16));
+	}
+
+
+	// int32_t StrtoI32(const char8_t*  pString, char8_t**  ppStringEnd, int nBase);
+	// int32_t StrtoI32(const char16_t* pString, char16_t** ppStringEnd, int nBase);
+	// int32_t StrtoI32(const char32_t* pString, char32_t** ppStringEnd, int nBase);
+	{
+		int32_t result;
+
+		char8_t  sn18[] = "-1011101110";
+		char8_t* pEnd = NULL;
+		EATEST_VERIFY(-1011101110 == StrtoI32(sn18, &pEnd, 10));
+
+		char8_t sn28[] = "7fffabcd";
+		EATEST_VERIFY(0x7fffabcd == StrtoI32(sn28, &pEnd, 16));
+
+		char8_t sn38[] = "00111001010000111111100001100101";
+		EATEST_VERIFY(0x3943F865LL == StrtoI32(sn38, &pEnd, 2));
+
+		char8_t sn48[] = "BEEFEEC0DE"; //number greater than INT32_MAX
+		EATEST_VERIFY(INT32_MAX == StrtoI32(sn48, &pEnd, 16));
+
+		char8_t sn58[] = "-BAEBEEC0DE"; //number less than INT32_MIN
+		EATEST_VERIFY(INT32_MIN == StrtoI32(sn58, &pEnd, 16));
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+			char8_t pINT32_MIN_10_[] = "-2147483649";
+			char8_t pINT32_MIN_10[]  = "-2147483648";
+			char8_t pINT32_MAX_10[]  = "+2147483647";
+			char8_t pINT32_MAX_10_[] = "+2147483648";
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MIN_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT32_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MIN_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT32_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MAX_10, &pEnd, 10);
+			EATEST_VERIFY((result == INT32_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY((result == INT32_MAX) && (errno == ERANGE));
+
+
+
+			char8_t pINT32_MIN_16_[] = "-80000001";
+			char8_t pINT32_MIN_16[]  = "-80000000";
+			char8_t pINT32_MAX_16[]  = "+7fffffff";
+			char8_t pINT32_MAX_16_[] = "+80000000";
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MIN_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT32_MIN) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT32_MIN) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == INT32_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoI32(pINT32_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == INT32_MAX) && (errno == ERANGE));
+		}
+	}
+	{
+		char16_t  sn18[16]; Strlcpy(sn18, EA_CHAR16("-1011101110"), EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* pEnd = NULL;
+		EATEST_VERIFY(-1011101110 == StrtoI32(sn18, &pEnd, 10));
+
+		char16_t sn28[12]; Strlcpy(sn28, EA_CHAR16("7fffabcd"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(0x7fffabcd == StrtoI32(sn28, &pEnd, 16));
+
+		char16_t sn38[48]; Strlcpy(sn38, EA_CHAR16("00111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(0x3943F865LL == StrtoI32(sn38, &pEnd, 2));
+
+		char16_t sn48[16]; Strlcpy(sn48, EA_CHAR16("BEEFEEC0DE"), EAArrayCount(sn48)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT32_MAX == StrtoI32(sn48, &pEnd, 16));
+
+		char16_t sn58[16]; Strlcpy(sn58, EA_CHAR16("-BAEBEEC0DE"), EAArrayCount(sn58)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT32_MIN == StrtoI32(sn58, &pEnd, 16));
+	}
+	{
+		char32_t  sn18[32]; Strlcpy(sn18, EA_CHAR32("-1011101110"), EAArrayCount(sn18)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* pEnd = NULL;
+		EATEST_VERIFY(-1011101110 == StrtoI32(sn18, &pEnd, 10));
+
+		char32_t sn28[12]; Strlcpy(sn28, EA_CHAR32("7fffabcd"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(0x7fffabcd == StrtoI32(sn28, &pEnd, 16));
+
+		char32_t sn38[48]; Strlcpy(sn38, EA_CHAR32("00111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(0x3943F865LL == StrtoI32(sn38, &pEnd, 2));
+
+		char32_t sn48[16]; Strlcpy(sn48, EA_CHAR32("BEEFEEC0DE"), EAArrayCount(sn48)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT32_MAX == StrtoI32(sn48, &pEnd, 16));
+
+		char32_t sn58[16]; Strlcpy(sn58, EA_CHAR32("-BAEBEEC0DE"), EAArrayCount(sn58)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT32_MIN == StrtoI32(sn58, &pEnd, 16));
+	}
+
+
+	// uint32_t StrtoU32(const char8_t* pString, char8_t**  ppStringEnd, int nBase);
+	// uint32_t StrtoU32(const char8_t* pString, char16_t** ppStringEnd, int nBase);
+	// uint32_t StrtoU32(const char8_t* pString, char32_t** ppStringEnd, int nBase);
+	{
+		uint32_t result;
+
+		char8_t  sn28[] = "7fffabcd";
+		char8_t* pEnd = NULL;
+
+		EATEST_VERIFY(0x7fffabcd == StrtoU32(sn28, &pEnd, 16));
+
+		char8_t sn38[] = "10111001010000111111100001100101";
+		EATEST_VERIFY(0xB943F865LL == StrtoU32(sn38, &pEnd, 2));
+
+		char8_t sn48[] = "BEEFEEC0DE"; //number greater than UINT32_MAX
+		EATEST_VERIFY(UINT32_MAX == StrtoU32(sn48, &pEnd, 16));
+
+		{
+			// Exercize the ability to read extreme values for various bases.
+		  //char8_t pUINT32_MIN_10_[] =         "-1";
+			char8_t pUINT32_MIN_10[]  =          "0";
+			char8_t pUINT32_MAX_10[]  = "4294967295";
+			char8_t pUINT32_MAX_10_[] = "4294967296";
+
+		  // -1 converting to 18446744073709551615 seems to be what conforming Standard C libraries do. So we allow it too.
+		  //errno = 0;
+		  //result =  StrtoU32(pUINT32_MIN_10_, &pEnd, 10);
+		  //EATEST_VERIFY((result == 0) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MIN_10, &pEnd, 10);
+			EATEST_VERIFY((result == 0) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MAX_10, &pEnd, 10);
+			EATEST_VERIFY((result == UINT32_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MAX_10_, &pEnd, 10);
+			EATEST_VERIFY((result == UINT32_MAX) && (errno == ERANGE));
+
+
+		  //char8_t pUINT32_MIN_16_[] =        "-1";
+			char8_t pUINT32_MIN_16[]  =         "0";
+			char8_t pUINT32_MAX_16[]  =  "ffffffff";
+			char8_t pUINT32_MAX_16_[] = "100000000";
+
+		  // -1 converting to 18446744073709551615 seems to be what conforming Standard C libraries do. So we allow it too.
+		  //errno = 0;
+		  //result =  StrtoU32(pUINT32_MIN_16_, &pEnd, 16);
+		  //EATEST_VERIFY((result == 0) && (errno == ERANGE));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MIN_16, &pEnd, 16);
+			EATEST_VERIFY((result == 0) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MAX_16, &pEnd, 16);
+			EATEST_VERIFY((result == UINT32_MAX) && (errno == 0));
+
+			errno = 0;
+			result =  StrtoU32(pUINT32_MAX_16_, &pEnd, 16);
+			EATEST_VERIFY((result == UINT32_MAX) && (errno == ERANGE));
+		}
+	}
+	{
+		char16_t  sn28[16]; Strlcpy(sn28, EA_CHAR16("7fffabcd"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		char16_t* pEnd = NULL;
+		EATEST_VERIFY(0x7fffabcd == StrtoU32(sn28, &pEnd, 16));
+
+		char16_t sn38[48]; Strlcpy(sn38, EA_CHAR16("10111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(0xB943F865LL == StrtoU32(sn38, &pEnd, 2));
+
+		char16_t sn48[16]; Strlcpy(sn48, EA_CHAR16("BEEFEEC0DE"), EAArrayCount(sn48)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT32_MAX == StrtoU32(sn48, &pEnd, 16));
+	}
+	{
+		char32_t  sn28[32]; Strlcpy(sn28, EA_CHAR32("7fffabcd"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		char32_t* pEnd = NULL;
+		EATEST_VERIFY(0x7fffabcd == StrtoU32(sn28, &pEnd, 16));
+
+		char32_t sn38[48]; Strlcpy(sn38, EA_CHAR32("10111001010000111111100001100101"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(0xB943F865LL == StrtoU32(sn38, &pEnd, 2));
+
+		char32_t sn48[16]; Strlcpy(sn48, EA_CHAR32("BEEFEEC0DE"), EAArrayCount(sn48)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT32_MAX == StrtoU32(sn48, &pEnd, 16));
+	}
+
+
+	// int32_t AtoI32(const char8_t*  pString);
+	// int32_t AtoI32(const char16_t* pString);
+	// int32_t AtoI32(const char32_t* pString);
+	{
+		char8_t sn18[] = "-1011101110";
+		EATEST_VERIFY(-1011101110 == AtoI32(sn18));
+
+		char8_t sn28[] = "2147483647";
+		EATEST_VERIFY(INT32_MAX == AtoI32(sn28));
+
+		char8_t sn38[] = "-2147483648";
+		EATEST_VERIFY(INT32_MIN == AtoI32(sn38));
+	}
+	{
+		char16_t sn18[16]; Strlcpy(sn18, EA_CHAR16("-1011101110"), EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(-1011101110 == AtoI32(sn18));
+
+		char16_t sn28[16]; Strlcpy(sn28, EA_CHAR16("2147483647"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT32_MAX == AtoI32(sn28));
+
+		char16_t sn38[16]; Strlcpy(sn38, EA_CHAR16("-2147483648"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT32_MIN == AtoI32(sn38));
+	}
+	{
+		char32_t sn18[32]; Strlcpy(sn18, EA_CHAR32("-1011101110"), EAArrayCount(sn18)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(-1011101110 == AtoI32(sn18));
+
+		char32_t sn28[32]; Strlcpy(sn28, EA_CHAR32("2147483647"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT32_MAX == AtoI32(sn28));
+
+		char32_t sn38[32]; Strlcpy(sn38, EA_CHAR32("-2147483648"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT32_MIN == AtoI32(sn38));
+	}
+
+
+	// uint32_t AtoU32(const char8_t*  pString);
+	// uint32_t AtoU32(const char16_t* pString);
+	// uint32_t AtoU32(const char32_t* pString);
+	{
+		char8_t sn28[] = "4294967295";
+		EATEST_VERIFY(UINT32_MAX == AtoU32(sn28));
+	}
+	{
+		char16_t sn28[12]; Strlcpy(sn28, EA_CHAR16("4294967295"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT32_MAX == AtoU32(sn28));
+	}
+	{
+		char32_t sn28[12]; Strlcpy(sn28, EA_CHAR32("4294967295"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT32_MAX == AtoU32(sn28));
+	}
+
+
+	// int64_t AtoI64(const char8_t*  pString);
+	// int64_t AtoI64(const char16_t* pString);
+	// int64_t AtoI64(const char32_t* pString);
+	{
+		char8_t sn18[] = "-1011101110111011101";
+		EATEST_VERIFY(-1011101110111011101LL == AtoI64(sn18));
+
+		char8_t sn28[] = "9223372036854775807";
+		EATEST_VERIFY(INT64_MAX == AtoI64(sn28));
+
+		char8_t sn38[] = "-9223372036854775808";
+		EATEST_VERIFY(INT64_MIN == AtoI64(sn38));
+	}
+	{
+		char16_t sn18[24]; Strlcpy(sn18, EA_CHAR16("-1011101110111011101"), EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(-1011101110111011101LL == AtoI64(sn18));
+
+		char16_t sn28[24]; Strlcpy(sn28, EA_CHAR16("9223372036854775807"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT64_MAX == AtoI64(sn28));
+
+		char16_t sn38[24]; Strlcpy(sn38, EA_CHAR16("-9223372036854775808"), EAArrayCount(sn38)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(INT64_MIN == AtoI64(sn38));
+	}
+	{
+		char32_t sn18[24]; Strlcpy(sn18, EA_CHAR32("-1011101110111011101"), EAArrayCount(sn18)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(-1011101110111011101LL == AtoI64(sn18));
+
+		char32_t sn28[24]; Strlcpy(sn28, EA_CHAR32("9223372036854775807"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT64_MAX == AtoI64(sn28));
+
+		char32_t sn38[24]; Strlcpy(sn38, EA_CHAR32("-9223372036854775808"), EAArrayCount(sn38)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(INT64_MIN == AtoI64(sn38));
+	}
+
+
+	// uint64_t AtoU64(const char8_t*  pString);
+	// uint64_t AtoU64(const char16_t* pString);
+	// uint64_t AtoU64(const char32_t* pString);
+	{
+		char8_t sn28[] = "18446744073709551615";
+		EATEST_VERIFY(UINT64_MAX == AtoU64(sn28));
+	}
+	{
+		char16_t sn28[24]; Strlcpy(sn28, EA_CHAR16("18446744073709551615"), EAArrayCount(sn28)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+		EATEST_VERIFY(UINT64_MAX == AtoU64(sn28));
+	}
+	{
+		char32_t sn28[24]; Strlcpy(sn28, EA_CHAR32("18446744073709553215"), EAArrayCount(sn28)); // Can't do char32_t variable[64] = EA_CHAR32(...) because some compilers don't support 32 bit string literals.
+		EATEST_VERIFY(UINT64_MAX == AtoU64(sn28));
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestAtof()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// double Atof(const char8_t*  pString);
+	// double Atof(const char16_t* pString);
+	// double Atof(const char32_t* pString);
+	{
+		const char8_t* kStrMin = "2.2250738585072014e-307"; // DBL_MIN is usually 2.2250738585072014e-308, but some Standard Libraries have trouble converting 
+		const double   kValMin =  2.2250738585072014e-307;  // to and from strings of that value. So we use -307 instead, which is pretty reliably supported.
+
+		const char8_t* kStrMax = "1.7976931348623158e+307"; // DBL_MAX is usually 1.7976931348623158e+308.
+		const double   kValMax =  1.7976931348623158e+307;
+
+		{
+			double d = Atof(kStrMax);
+			EATEST_VERIFY(DoubleEqual(d, kValMax));
+
+			d = Atof(kStrMin);
+			EATEST_VERIFY(DoubleEqual(d, kValMin));
+		}
+		{
+			char16_t sn18[32]; Strlcpy(sn18, kStrMax, EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+			double  d = Atof(sn18);
+			EATEST_VERIFY(DoubleEqual(d, kValMax));
+
+			char16_t sn28[32]; Strlcpy(sn28, kStrMin, EAArrayCount(sn28));
+			d = Atof(sn28);
+			EATEST_VERIFY(DoubleEqual(d, kValMin));
+		}
+		{
+			char32_t sn18[32]; Strlcpy(sn18, kStrMax, EAArrayCount(sn18));
+			double d = Atof(sn18);
+			EATEST_VERIFY(DoubleEqual(d, kValMax));
+
+			char32_t sn28[32]; Strlcpy(sn28, kStrMin, EAArrayCount(sn28));
+			d = Atof(sn28);
+			EATEST_VERIFY(DoubleEqual(d, kValMin));
+		}
+	}
+
+	// double AtofEnglish(const char8_t*  pString);
+	// double AtofEnglish(const char16_t* pString);
+	// double AtofEnglish(const char32_t* pString);
+	//
+	// AtofEnglish - this function fails on boundary values for double_t 
+	// as it performs some math inside that drives the values to infinity.
+	{
+		const char8_t* kStrMin = "2.2250738585071e-307"; // DBL_MIN is usually 2.2250738585072014e-308, but some Standard Libraries have trouble converting 
+		const double   kValMin =  2.2250738585071e-307;  // to and from strings of that value. So we use -307 instead, which is pretty reliably supported.
+
+		const char8_t* kStrMax = "1.7976931348622e+307"; // DBL_MAX is usually 1.7976931348623158e+308.
+		const double   kValMax =  1.7976931348622e+307;
+
+		{
+			EATEST_VERIFY(DoubleEqual(kValMin, AtofEnglish(kStrMin)));
+
+			EATEST_VERIFY(DoubleEqual(kValMax, AtofEnglish(kStrMax)));
+		}
+		{
+			char16_t sn18[32]; Strlcpy(sn18, kStrMin, EAArrayCount(sn18)); // Can't do char16_t variable[64] = EA_CHAR16(...) because some compilers don't support 16 bit string literals.
+			EATEST_VERIFY(DoubleEqual(kValMin, AtofEnglish(sn18)));
+
+			char16_t sn28[32]; Strlcpy(sn28, kStrMax, EAArrayCount(sn28));
+			EATEST_VERIFY(DoubleEqual(kValMax, AtofEnglish(sn28)));
+		}
+		{
+			char32_t sn18[32]; Strlcpy(sn18, kStrMin, EAArrayCount(sn18));
+			EATEST_VERIFY(DoubleEqual(kValMin, AtofEnglish(sn18)));
+
+			char32_t sn28[32]; Strlcpy(sn28, kStrMax, EAArrayCount(sn28));
+			EATEST_VERIFY(DoubleEqual(kValMax, AtofEnglish(sn28)));
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestFtoa()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// char8_t*  Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	// char16_t* Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	// char32_t* Ftoa(double dValue, char_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	{
+		char8_t sn18[128];
+
+		Ftoa(-1.7976931, sn18, 128, 7, false);
+		if(Strcmp("-1.7976931", sn18) != 0)
+		{
+			EA::UnitTest::Report("%s\n", sn18);
+			EATEST_VERIFY(Strcmp("-1.7976931", sn18) == 0);
+		}
+
+		Ftoa(10.7976931e299, sn18, 128, 5, true);
+		if(Strcmp("1.07977e+300", sn18) != 0)
+		{
+			EA::UnitTest::Report("%s\n", sn18);
+			EATEST_VERIFY(Strcmp("1.07977e+300", sn18) == 0);
+		}
+
+		Ftoa(-1.7976931, sn18, 128, 4, false);
+		if(Strcmp("-1.7977", sn18) != 0)
+		{
+			EA::UnitTest::Report("%s\n", sn18);
+			EATEST_VERIFY(Strcmp("-1.7977", sn18) == 0);
+		}
+
+		Ftoa(0.107976931e-299, sn18, 128, 5, true);
+		if(Strcmp("1.07977e-300", sn18) != 0)
+		{
+			EA::UnitTest::Report("%s\n", sn18);
+			EATEST_VERIFY(Strcmp("1.07977e-300", sn18) == 0);
+		}
+	}
+	{
+		char16_t sn18[128];
+
+		Ftoa(-1.7976931, sn18, 128, 7, false);
+		if(Strcmp(EA_CHAR16("-1.7976931"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR16("-1.7976931"), sn18) == 0);
+		}
+
+		Ftoa(10.7976931e299, sn18, 128, 5, true);
+		if(Strcmp(EA_CHAR16("1.07977e+300"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR16("1.07977e+300"), sn18) == 0);
+		}
+
+		Ftoa(-1.7976931, sn18, 128, 4, false);
+		if(Strcmp(EA_CHAR16("-1.7977"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR16("-1.7977"), sn18) == 0);
+		}
+
+		Ftoa(0.107976931e-299, sn18, 128, 5, true);
+		if(Strcmp(EA_CHAR16("1.07977e-300"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR16("1.07977e-300"), sn18) == 0);
+		}
+	}
+	{
+		char32_t sn18[128];
+
+		Ftoa(-1.7976931, sn18, 128, 7, false);
+		if(Strcmp(EA_CHAR32("-1.7976931"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR32("-1.7976931"), sn18) == 0);
+		}
+
+		Ftoa(10.7976931e299, sn18, 128, 5, true);
+		if(Strcmp(EA_CHAR32("1.07977e+300"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR32("1.07977e+300"), sn18) == 0);
+		}
+
+		Ftoa(-1.7976931, sn18, 128, 4, false);
+		if(Strcmp(EA_CHAR32("-1.7977"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR32("-1.7977"), sn18) == 0);
+		}
+
+		Ftoa(0.107976931e-299, sn18, 128, 5, true);
+		if(Strcmp(EA_CHAR32("1.07977e-300"), sn18) != 0)
+		{
+			EA::UnitTest::Report("%ls\n", sn18);
+			EATEST_VERIFY(Strcmp(EA_CHAR32("1.07977e-300"), sn18) == 0);
+		}
+	}
+
+
+	// char8_t*  FtoaEnglish(double dValue, char8_t*  pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	// char16_t* FtoaEnglish(double dValue, char16_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	// char32_t* FtoaEnglish(double dValue, char32_t* pResult, int nInputLength, int nPrecision, bool bExponentEnabled);
+	{
+		char8_t  sn18[128];
+		char8_t* pResult;
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 7, false);
+		EATEST_VERIFY(pResult && (Strcmp("-1.7976931", sn18) == 0));
+
+		pResult = FtoaEnglish(10.7976931e299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp("1.07977e+300", sn18) == 0));
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 4, false);
+		EATEST_VERIFY(pResult && (Strcmp("-1.7977", sn18) == 0));
+
+		pResult = FtoaEnglish(0.107976931e-299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp("1.07977e-300", sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 1, 0, false);
+		EATEST_VERIFY(!pResult && (Strcmp("", sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp("10000", sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp("10000", sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 6, 10, false);
+		EATEST_VERIFY(pResult == NULL);
+
+		pResult = FtoaEnglish(10000.12345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp("10000.123", sn18) == 0));
+
+		pResult = FtoaEnglish(10000.12348, sn18, 20, 8, false);
+		EATEST_VERIFY(pResult && (Strcmp("10000.12348", sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp("0", sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, true);
+		EATEST_VERIFY(pResult && (Strcmp("0", sn18) == 0));
+
+		pResult = FtoaEnglish(0.0001, sn18, 20, 4, true);
+		EATEST_VERIFY(pResult && (Strcmp("0.0001", sn18) == 0));
+
+		pResult = FtoaEnglish(.12345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp("0.12", sn18) == 0));
+
+		pResult = FtoaEnglish(.012345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp("0.012", sn18) == 0));
+
+		pResult = FtoaEnglish(.0012345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp("0", sn18) == 0));
+
+		pResult = FtoaEnglish(.123450000, sn18, 20, 17, false);
+		EATEST_VERIFY(pResult && (Strcmp("0.12345", sn18) == 0));
+	}
+	{
+		char16_t  sn18[128];
+		char16_t* pResult;
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 7, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("-1.7976931"), sn18) == 0));
+
+		pResult = FtoaEnglish(10.7976931e299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("1.07977e+300"), sn18) == 0));
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 4, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("-1.7977"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.107976931e-299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("1.07977e-300"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 1, 0, false);
+		EATEST_VERIFY(!pResult && (Strcmp(EA_CHAR16(""), sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("10000"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("10000"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 6, 10, false);
+		EATEST_VERIFY(pResult == NULL);
+
+		pResult = FtoaEnglish(10000.12345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("10000.123"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.12348, sn18, 20, 8, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("10000.12348"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0001, sn18, 20, 4, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0.0001"), sn18) == 0));
+
+		pResult = FtoaEnglish(.12345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0.12"), sn18) == 0));
+
+		pResult = FtoaEnglish(.012345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0.012"), sn18) == 0));
+
+		pResult = FtoaEnglish(.0012345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(.123450000, sn18, 20, 17, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR16("0.12345"), sn18) == 0));
+	}
+	{
+		char32_t  sn18[128];
+		char32_t* pResult;
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 7, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("-1.7976931"), sn18) == 0));
+
+		pResult = FtoaEnglish(10.7976931e299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("1.07977e+300"), sn18) == 0));
+
+		pResult = FtoaEnglish(-1.7976931, sn18, 128, 4, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("-1.7977"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.107976931e-299, sn18, 128, 5, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("1.07977e-300"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 1, 0, false);
+		EATEST_VERIFY(!pResult && (Strcmp(EA_CHAR32(""), sn18) == 0));
+
+		pResult = FtoaEnglish(10000, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("10000"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 10, 0, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("10000"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.003, sn18, 6, 10, false);
+		EATEST_VERIFY(pResult == NULL);
+
+		pResult = FtoaEnglish(10000.12345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("10000.123"), sn18) == 0));
+
+		pResult = FtoaEnglish(10000.12348, sn18, 20, 8, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("10000.12348"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0, sn18, 20, 3, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(0.0001, sn18, 20, 4, true);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0.0001"), sn18) == 0));
+
+		pResult = FtoaEnglish(.12345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0.12"), sn18) == 0));
+
+		pResult = FtoaEnglish(.012345, sn18, 20, 3, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0.012"), sn18) == 0));
+
+		pResult = FtoaEnglish(.0012345, sn18, 20, 2, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0"), sn18) == 0));
+
+		pResult = FtoaEnglish(.123450000, sn18, 20, 17, false);
+		EATEST_VERIFY(pResult && (Strcmp(EA_CHAR32("0.12345"), sn18) == 0));
+	}
+
+	return nErrorCount;
+}
+
+
+static int TestReduceFloatString()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// size_t ReduceFloatString(char8_t*  pString, size_t nLength = (size_t)~0);
+	// size_t ReduceFloatString(char16_t* pString, size_t nLength = (size_t)~0);
+	// size_t ReduceFloatString(char32_t* pString, size_t nLength = (size_t)~0);
+	{
+		char8_t pBuffer[64];
+		size_t  n;
+
+		Strcpy(pBuffer, "2.3400");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("2.34")) && (Strcmp("2.34", pBuffer) == 0));
+
+		Strcpy(pBuffer, "00000");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("0")) && (Strcmp("0", pBuffer) == 0));
+
+		Strcpy(pBuffer, ".0000");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("0")) && (Strcmp("0", pBuffer) == 0));
+
+		Strcpy(pBuffer, "000.0000");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("0")) && (Strcmp("0", pBuffer) == 0));
+
+		Strcpy(pBuffer, "-0.0000");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("-0")) && (Strcmp("-0", pBuffer) == 0));
+
+		Strcpy(pBuffer, "23.430000e10");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("23.43e10")) && (Strcmp("23.43e10", pBuffer) == 0));
+
+		Strcpy(pBuffer, "2.34001");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("2.34001")) && (Strcmp("2.34001", pBuffer) == 0));
+
+		Strcpy(pBuffer, "0.00001");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(".00001")) && (Strcmp(".00001", pBuffer) == 0));
+
+		Strcpy(pBuffer, "-0.00001");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("-0.00001")) && (Strcmp("-0.00001", pBuffer) == 0));
+
+		Strcpy(pBuffer, "23.43e10");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("23.43e10")) && (Strcmp("23.43e10", pBuffer) == 0));
+
+		Strcpy(pBuffer, "15e-0");
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen("15e-0")) && (Strcmp("15e-0", pBuffer) == 0));
+	}
+	{
+		char16_t pBuffer[64];
+		size_t   n;
+
+		Strcpy(pBuffer, EA_CHAR16("2.3400"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("2.34"))) && (Strcmp(EA_CHAR16("2.34"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("00000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("0"))) && (Strcmp(EA_CHAR16("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16(".0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("0"))) && (Strcmp(EA_CHAR16("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("000.0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("0"))) && (Strcmp(EA_CHAR16("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("-0.0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("-0"))) && (Strcmp(EA_CHAR16("-0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("23.430000e10"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("23.43e10"))) && (Strcmp(EA_CHAR16("23.43e10"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("2.34001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("2.34001"))) && (Strcmp(EA_CHAR16("2.34001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("0.00001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16(".00001"))) && (Strcmp(EA_CHAR16(".00001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("-0.00001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("-0.00001"))) && (Strcmp(EA_CHAR16("-0.00001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("23.43e10"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("23.43e10"))) && (Strcmp(EA_CHAR16("23.43e10"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR16("15e-0"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR16("15e-0"))) && (Strcmp(EA_CHAR16("15e-0"), pBuffer) == 0));
+	}
+	{
+		char32_t pBuffer[64];
+		size_t   n;
+
+		Strcpy(pBuffer, EA_CHAR32("2.3400"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("2.34"))) && (Strcmp(EA_CHAR32("2.34"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("00000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("0"))) && (Strcmp(EA_CHAR32("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32(".0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("0"))) && (Strcmp(EA_CHAR32("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("000.0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("0"))) && (Strcmp(EA_CHAR32("0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("-0.0000"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("-0"))) && (Strcmp(EA_CHAR32("-0"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("23.430000e10"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("23.43e10"))) && (Strcmp(EA_CHAR32("23.43e10"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("2.34001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("2.34001"))) && (Strcmp(EA_CHAR32("2.34001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("0.00001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32(".00001"))) && (Strcmp(EA_CHAR32(".00001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("-0.00001"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("-0.00001"))) && (Strcmp(EA_CHAR32("-0.00001"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("23.43e10"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("23.43e10"))) && (Strcmp(EA_CHAR32("23.43e10"), pBuffer) == 0));
+
+		Strcpy(pBuffer, EA_CHAR32("15e-0"));
+		n = ReduceFloatString(pBuffer);
+		EATEST_VERIFY((n == Strlen(EA_CHAR32("15e-0"))) && (Strcmp(EA_CHAR32("15e-0"), pBuffer) == 0));
+	}
+
+	return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestConvertString
+//
+static int TestConvertString()
+{
+	int nErrorCount = 0;
+
+	eastl::string8  s8("hello world");
+	eastl::string16 s16;
+	eastl::string32 s32;
+
+	// template <typename Dest, typename Source>
+	// inline bool Strlcpy(Dest& d, const Source& s);
+	{
+		// 8 -> 16
+		EA::StdC::Strlcpy(s16, s8);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s8.clear();
+
+		// 16 -> 8
+		EA::StdC::Strlcpy(s8, s16);
+		EATEST_VERIFY(s8 == "hello world");
+		s16.clear();
+
+
+		// 8 -> 16
+		// Note that using this non-const string causes this specialization to be called instead of the specialization that takes const Source*.
+		char8_t pNonConstString8[] = "hello world";
+		EA::StdC::Strlcpy(s16, pNonConstString8);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s8.clear();
+
+		// 16 -> 8
+		// Note that using this non-const string causes this specialization to be called instead of the specialization that takes const Source*.
+		char16_t pNonConstString16[] = EA_CHAR16("hello world");
+		EA::StdC::Strlcpy(s8, pNonConstString16);
+		EATEST_VERIFY(s8 == "hello world");
+		s16.clear();
+	}
+	{
+		// 8 -> 32
+		EA::StdC::Strlcpy(s32, s8);
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s8.clear();
+
+		// 32 -> 8
+		EA::StdC::Strlcpy(s8, s32);
+		EATEST_VERIFY(s8 == "hello world");
+		s32.clear();
+	}
+	{
+		// 8 -> 8
+		EA::StdC::Strlcpy<eastl::string8, eastl::string8>(s8, eastl::string8("hello world"));
+		EATEST_VERIFY(s8 == "hello world");
+		s8.clear();
+
+		// 16 -> 16
+		EA::StdC::Strlcpy<eastl::string16, eastl::string16>(s16, eastl::string16(EA_CHAR16("hello world")));
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s16.clear();
+
+		// 32 -> 32
+		EA::StdC::Strlcpy<eastl::string32, eastl::string32>(s32, eastl::string32(EA_CHAR32("hello world")));
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s32.clear();
+	}
+
+
+	// template <typename Dest, typename Source>
+	// inline bool Strlcpy(Dest& d, const Source* pSource, size_t sourceLength = (size_t)~0);
+	{
+		// 8 -> 16
+		s8 = "hello world";
+		EA::StdC::Strlcpy(s16, s8.c_str(), s8.length());
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s8.clear();
+
+		// 16 -> 8
+		EA::StdC::Strlcpy(s8, s16.c_str(), s16.length());
+		EATEST_VERIFY(s8 == "hello world");
+		s16.clear();
+	}
+	{
+		// 8 -> 32
+		EA::StdC::Strlcpy(s32, s8.c_str(), s8.length());
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s8.clear();
+
+		// 32 -> 8
+		EA::StdC::Strlcpy(s8, s32.c_str(), s32.length());
+		EATEST_VERIFY(s8 == "hello world");
+		s32.clear();
+	}
+	{
+		// 8 -> 8
+		EA::StdC::Strlcpy(s8, "hello world", 11);
+		EATEST_VERIFY(s8 == "hello world");
+		s8.clear();
+
+		// 16 -> 16
+		EA::StdC::Strlcpy(s16, EA_CHAR16("hello world"), 11);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s32.clear();
+
+		// 32 -> 32
+		EA::StdC::Strlcpy(s32, EA_CHAR32("hello world"), 11);
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s32.clear();
+	}
+
+
+	// template <typename Dest, typename Source>
+	// inline Dest Strlcpy(const Source& s);
+	{
+		// 8 -> 16
+		s8 = "hello world";
+		s16 = EA::StdC::Strlcpy<eastl::string16,  eastl::string8>(s8);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s8.clear();
+
+		// 16 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, eastl::string16>(s16);
+		EATEST_VERIFY(s8 == "hello world");
+		s16.clear();
+	}
+	{
+		// 8 -> 32
+		s32 = EA::StdC::Strlcpy<eastl::string32, eastl::string8>(s8);
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s8.clear();
+
+		// 32 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, eastl::string32>(s32);
+		EATEST_VERIFY(s8 == "hello world");
+		s32.clear();
+	}
+	{
+		// 8 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, eastl::string8>(eastl::string8("hello world"));
+		EATEST_VERIFY(s8 == "hello world");
+		s8.clear();
+
+		// 16 -> 16
+		s16 = EA::StdC::Strlcpy<eastl::string16, eastl::string16>(eastl::string16(EA_CHAR16("hello world")));
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s16.clear();
+
+		// 32 -> 32
+		s32 = EA::StdC::Strlcpy<eastl::string32, eastl::string32>(eastl::string32(EA_CHAR32("hello world")));
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s32.clear();
+	}
+
+	// template <typename Dest, typename Source>
+	// inline Dest Strlcpy(Dest& d, const Source* pSource, size_t sourceLength = (size_t)~0);
+	{
+		// 8 -> 16
+		s16 = EA::StdC::Strlcpy<eastl::string16, char8_t>("hello world", 11);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s8.clear();
+
+		// 16 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, char16_t>(EA_CHAR16("hello world"), 11);
+		EATEST_VERIFY(s8 == "hello world");
+		s16.clear();
+	}
+	{
+		// 8 -> 32
+		s32 = EA::StdC::Strlcpy<eastl::string32, char8_t>("hello world", 11);
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s8.clear();
+
+		// 32 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, char32_t>(EA_CHAR32("hello world"), 11);
+		EATEST_VERIFY(s8 == "hello world");
+		s32.clear();
+	}
+	{
+		// 8 -> 8
+		s8 = EA::StdC::Strlcpy<eastl::string8, char8_t>("hello world", 11);
+		EATEST_VERIFY(s8 == "hello world");
+		s8.clear();
+
+		// 16 -> 16
+		s16 = EA::StdC::Strlcpy<eastl::string16, char16_t>(EA_CHAR16("hello world"), 11);
+		EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+		s16.clear();
+
+		// 32 -> 32
+		s32 = EA::StdC::Strlcpy<eastl::string32, char32_t>(EA_CHAR32("hello world"), 11);
+		EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+		s32.clear();
+	}
+
+
+	// template <typename Dest, typename Source>
+	// inline bool Strlcat(Dest& d, const Source& s);
+	{
+		// 8 -> 16
+		s16 = EA_CHAR16("abc ");
+		EA::StdC::Strlcat(s16, eastl::string8("hello world"));
+		EATEST_VERIFY(s16 == EA_CHAR16("abc hello world"));
+
+		// 16 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, eastl::string16(EA_CHAR16("hello world")));
+		EATEST_VERIFY(s8 == "abc hello world");
+	}
+	{
+		// 8 -> 32
+		s32 = EA_CHAR32("abc ");
+		EA::StdC::Strlcat(s32, eastl::string8("hello world"));
+		EATEST_VERIFY(s32 == EA_CHAR32("abc hello world"));
+
+		// 32 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, eastl::string32(EA_CHAR32("hello world")));
+		EATEST_VERIFY(s8 == "abc hello world");
+	}
+	{
+		// 8 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, eastl::string8("hello world"));
+		EATEST_VERIFY(s8 == "abc hello world");
+		s8.clear();
+
+		// 16 -> 16
+		s16 = EA_CHAR16("abc ");
+		EA::StdC::Strlcat(s16, eastl::string16(EA_CHAR16("hello world")));
+		EATEST_VERIFY(s16 == EA_CHAR16("abc hello world"));
+		s16.clear();
+
+		// 32 -> 32
+		s32 = EA_CHAR32("abc ");
+		EA::StdC::Strlcat(s32, eastl::string32(EA_CHAR32("hello world")));
+		EATEST_VERIFY(s32 == EA_CHAR32("abc hello world"));
+		s32.clear();
+	}
+
+
+	// template <typename Dest, typename Source>
+	// inline bool Strlcat(Dest& d, const Source* s, size_t sourceLength = (size_t)~0);
+	{
+		// 8 -> 16
+		s16 = EA_CHAR16("abc ");
+		EA::StdC::Strlcat(s16, "hello world", 11);
+		EATEST_VERIFY(s16 == EA_CHAR16("abc hello world"));
+
+		// 16 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, EA_CHAR16("hello world"), 11);
+		EATEST_VERIFY(s8 == "abc hello world");
+	}
+	{
+		// 8 -> 32
+		s32 = EA_CHAR32("abc ");
+		EA::StdC::Strlcat(s32, "hello world", 11);
+		EATEST_VERIFY(s32 == EA_CHAR32("abc hello world"));
+
+		// 32 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, EA_CHAR32("hello world"), 11);
+		EATEST_VERIFY(s8 == "abc hello world");
+	}
+	{
+		// 8 -> 8
+		s8 = "abc ";
+		EA::StdC::Strlcat(s8, "hello world", 11);
+		EATEST_VERIFY(s8 == "abc hello world");
+		s8.clear();
+
+		// 16 -> 16
+		s16 = EA_CHAR16("abc ");
+		EA::StdC::Strlcat(s16, EA_CHAR16("hello world"), 11);
+		EATEST_VERIFY(s16 == EA_CHAR16("abc hello world"));
+		s16.clear();
+
+		// 32 -> 32
+		s32 = EA_CHAR32("abc ");
+		EA::StdC::Strlcat(s32, EA_CHAR32("hello world"), 11);
+		EATEST_VERIFY(s32 == EA_CHAR32("abc hello world"));
+		s32.clear();
+	}
+
+
+	{ // ConvertString is a deprecated name for Strlcpy.
+		// template <typename Source, typename Dest>
+		// inline bool ConvertString(const Source& s, Dest& d);
+		{
+			// 8 -> 16
+			s8 = "hello world";
+			EA::StdC::ConvertString(s8, s16);
+			EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+			s8.clear();
+
+			// 16 -> 8
+			EA::StdC::ConvertString(s16, s8);
+			EATEST_VERIFY(s8 == "hello world");
+			s16.clear();
+		}
+		{
+			// 8 -> 32
+			EA::StdC::ConvertString(s8, s32);
+			EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+			s8.clear();
+
+			// 32 -> 8
+			EA::StdC::ConvertString(s32, s8);
+			EATEST_VERIFY(s8 == "hello world");
+			s32.clear();
+		}
+
+
+		// template <typename Source, typename Dest>
+		// inline Dest ConvertString(const Source& s);
+		{
+			// 8 -> 16
+			s16 = EA::StdC::ConvertString<eastl::string8,  eastl::string16>(s8);
+			EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+			s8.clear();
+
+			// 16 -> 8
+			s8 = EA::StdC::ConvertString<eastl::string16, eastl::string8>(s16);
+			EATEST_VERIFY(s8 == "hello world");
+			s16.clear();
+		}
+		{
+			// 8 -> 32
+			s32 = EA::StdC::ConvertString<eastl::string8,  eastl::string32>(s8);
+			EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+			s8.clear();
+
+			// 32 -> 8
+			s8 = EA::StdC::ConvertString<eastl::string32, eastl::string8>(s32);
+			EATEST_VERIFY(s8 == "hello world");
+			s32.clear();
+		}
+
+		{
+			// 8 -> 8
+			s8 = EA::StdC::ConvertString<eastl::string8, eastl::string8>(eastl::string8("hello world"));
+			EATEST_VERIFY(s8 == "hello world");
+			s8.clear();
+
+			// 16 -> 16
+			s16 = EA::StdC::ConvertString<eastl::string16, eastl::string16>(eastl::string16(EA_CHAR16("hello world")));
+			EATEST_VERIFY(s16 == EA_CHAR16("hello world"));
+			s16.clear();
+
+			// 32 -> 32
+			s32 = EA::StdC::ConvertString<eastl::string32, eastl::string32>(eastl::string32(EA_CHAR32("hello world")));
+			EATEST_VERIFY(s32 == EA_CHAR32("hello world"));
+			s32.clear();
+		}
+
+		{   //Regression for user-reported problem.
+			const char8_t* pPath8 = "/abc/def/ghi/jkl";
+			eastl::string16 path16 = EA::StdC::ConvertString<eastl::string8, eastl::string16>(pPath8);
+			EATEST_VERIFY((path16.length() == EA::StdC::Strlen(pPath8)));
+		}
+	}
+
+	return nErrorCount;
+}
+
+template <class charType>
+int TestStrstripString(const charType* input, const charType* expectedOutput)
+{
+	using namespace EA::StdC;
+	int nErrorCount = 0;
+	size_t strLen = EA::StdC::Strlen(input) + 1;
+	charType* str = static_cast<charType*>(malloc((strLen) * sizeof(charType)));
+	Strncpy(str, input, strLen);
+	EATEST_VERIFY(Strcmp(Strstrip(str), expectedOutput) == 0);
+	free(str);
+	return nErrorCount;
+}
+
+static int TestStrstrip()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	nErrorCount += TestStrstripString("", "");
+	nErrorCount += TestStrstripString(" ", "");
+	nErrorCount += TestStrstripString("  ", "");
+	nErrorCount += TestStrstripString("        ", "");
+	nErrorCount += TestStrstripString("a", "a");
+	nErrorCount += TestStrstripString("a ", "a");
+	nErrorCount += TestStrstripString("a      ", "a");
+	nErrorCount += TestStrstripString(" a", "a");
+	nErrorCount += TestStrstripString("     a", "a");
+	nErrorCount += TestStrstripString(" a ", "a");
+	nErrorCount += TestStrstripString("  a  ", "a");
+	nErrorCount += TestStrstripString("        a ", "a");
+	nErrorCount += TestStrstripString(" a      ", "a");
+	nErrorCount += TestStrstripString("a b", "a b");
+	nErrorCount += TestStrstripString("a    b", "a    b");
+	nErrorCount += TestStrstripString(" a b", "a b");
+	nErrorCount += TestStrstripString("      a b", "a b");
+	nErrorCount += TestStrstripString("      a b ", "a b");
+	nErrorCount += TestStrstripString("a b ", "a b");
+	nErrorCount += TestStrstripString("a b    ", "a b");
+	nErrorCount += TestStrstripString(" a b    ", "a b");
+	nErrorCount += TestStrstripString(" a b ", "a b");
+	nErrorCount += TestStrstripString(" a     b ", "a     b");
+	nErrorCount += TestStrstripString("    a     b   ", "a     b");
+	nErrorCount += TestStrstripString("    a     b   ", "a     b");
+	nErrorCount += TestStrstripString("ab", "ab");
+	nErrorCount += TestStrstripString("abcdefghiklmnop", "abcdefghiklmnop");
+	nErrorCount += TestStrstripString("a b c d e f g h i k l m n o p", "a b c d e f g h i k l m n o p");
+	nErrorCount += TestStrstripString(" a b c d e f g h i k l m n o p ", "a b c d e f g h i k l m n o p");
+	nErrorCount += TestStrstripString("    abcdefg  hiklmnop    ", "abcdefg  hiklmnop");
+	nErrorCount += TestStrstripString("    abcdefg  hiklmnop", "abcdefg  hiklmnop");
+	nErrorCount += TestStrstripString("abcdefg  hiklmnop    ", "abcdefg  hiklmnop");
+	nErrorCount += TestStrstripString("abcdefg  hiklmnop", "abcdefg  hiklmnop");
+	nErrorCount += TestStrstripString(" \t \r\n \n \t \r\n \n ", "");
+	nErrorCount += TestStrstripString("\t \r\n \na b\t \r\n \n", "a b");
+	nErrorCount += TestStrstripString("\t a\r\n \n \t \r\nb \n", "a\r\n \n \t \r\nb");
+	nErrorCount += TestStrstripString(" \t \r\n \na \t \r\n \n ", "a");
+
+	nErrorCount += TestStrstripString(EA_CHAR16(""), EA_CHAR16(""));
+	nErrorCount += TestStrstripString(EA_CHAR16(" "), EA_CHAR16(""));
+	nErrorCount += TestStrstripString(EA_CHAR16("  "), EA_CHAR16(""));
+	nErrorCount += TestStrstripString(EA_CHAR16("        "), EA_CHAR16(""));
+	nErrorCount += TestStrstripString(EA_CHAR16("a"), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a      "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a"), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("     a"), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("  a  "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("        a "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a      "), EA_CHAR16("a"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a b"), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a    b"), EA_CHAR16("a    b"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a b"), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("      a b"), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("      a b "), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a b "), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a b    "), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a b    "), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a b "), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a     b "), EA_CHAR16("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("    a     b   "), EA_CHAR16("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("    a     b   "), EA_CHAR16("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("ab"), EA_CHAR16("ab"));
+	nErrorCount += TestStrstripString(EA_CHAR16("abcdefghiklmnop"), EA_CHAR16("abcdefghiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR16("a b c d e f g h i k l m n o p"), EA_CHAR16("a b c d e f g h i k l m n o p"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" a b c d e f g h i k l m n o p "), EA_CHAR16("a b c d e f g h i k l m n o p"));
+	nErrorCount += TestStrstripString(EA_CHAR16("    abcdefg  hiklmnop    "), EA_CHAR16("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR16("abcdefg  hiklmnop"), EA_CHAR16("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" \t \r\n \n \t \r\n \n "), EA_CHAR16(""));
+	nErrorCount += TestStrstripString(EA_CHAR16("\t \r\n \na b\t \r\n \n"), EA_CHAR16("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR16("\t a\r\n \n \t \r\nb \n"), EA_CHAR16("a\r\n \n \t \r\nb"));
+	nErrorCount += TestStrstripString(EA_CHAR16(" \t \r\n \na \t \r\n \n "), EA_CHAR16("a"));
+
+	nErrorCount += TestStrstripString(EA_CHAR32(""), EA_CHAR32(""));
+	nErrorCount += TestStrstripString(EA_CHAR32(" "), EA_CHAR32(""));
+	nErrorCount += TestStrstripString(EA_CHAR32("  "), EA_CHAR32(""));
+	nErrorCount += TestStrstripString(EA_CHAR32("        "), EA_CHAR32(""));
+	nErrorCount += TestStrstripString(EA_CHAR32("a"), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a      "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a"), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("     a"), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("  a  "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("        a "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a      "), EA_CHAR32("a"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a b"), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a    b"), EA_CHAR32("a    b"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a b"), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("      a b"), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("      a b "), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a b "), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a b    "), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a b    "), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a b "), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a     b "), EA_CHAR32("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("    a     b   "), EA_CHAR32("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("    a     b   "), EA_CHAR32("a     b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("ab"), EA_CHAR32("ab"));
+	nErrorCount += TestStrstripString(EA_CHAR32("abcdefghiklmnop"), EA_CHAR32("abcdefghiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR32("a b c d e f g h i k l m n o p"), EA_CHAR32("a b c d e f g h i k l m n o p"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" a b c d e f g h i k l m n o p "), EA_CHAR32("a b c d e f g h i k l m n o p"));
+	nErrorCount += TestStrstripString(EA_CHAR32("    abcdefg  hiklmnop    "), EA_CHAR32("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR32("    abcdefg  hiklmnop"), EA_CHAR32("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR32("abcdefg  hiklmnop    "), EA_CHAR32("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR32("abcdefg  hiklmnop"), EA_CHAR32("abcdefg  hiklmnop"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" \t \r\n \n \t \r\n \n "), EA_CHAR32(""));
+	nErrorCount += TestStrstripString(EA_CHAR32("\t \r\n \na b\t \r\n \n"), EA_CHAR32("a b"));
+	nErrorCount += TestStrstripString(EA_CHAR32("\t a\r\n \n \t \r\nb \n"), EA_CHAR32("a\r\n \n \t \r\nb"));
+	nErrorCount += TestStrstripString(EA_CHAR32(" \t \r\n \na \t \r\n \n "), EA_CHAR32("a"));
+
+	return nErrorCount;
+}
+
+
+
+static int TestStrstart()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// char8_t
+	EATEST_VERIFY( Strstart("",       ""));
+	EATEST_VERIFY( Strstart("a",      ""));
+	EATEST_VERIFY(!Strstart("",       "a"));
+	EATEST_VERIFY( Strstart("abcdef", "a"));
+	EATEST_VERIFY(!Strstart("abcdef", "A"));
+	EATEST_VERIFY( Strstart("abcdef", "abcdef"));
+	EATEST_VERIFY(!Strstart("abcdef", "bcdef "));
+	EATEST_VERIFY(!Strstart("abcdef", "Abcdef"));
+
+	EATEST_VERIFY( Stristart("",       ""));
+	EATEST_VERIFY( Stristart("a",      ""));
+	EATEST_VERIFY(!Stristart("",       "a"));
+	EATEST_VERIFY( Stristart("abcdef", "a"));
+	EATEST_VERIFY( Stristart("abcdef", "A"));
+	EATEST_VERIFY( Stristart("abcdef", "abcdef"));
+	EATEST_VERIFY(!Stristart("abcdef", "bcdef "));
+	EATEST_VERIFY( Stristart("abcdef", "Abcdef"));
+
+	// char16_t
+	EATEST_VERIFY( Strstart(EA_CHAR16(""),       EA_CHAR16("")));
+	EATEST_VERIFY( Strstart(EA_CHAR16("a"),      EA_CHAR16("")));
+	EATEST_VERIFY(!Strstart(EA_CHAR16(""),       EA_CHAR16("a")));
+	EATEST_VERIFY( Strstart(EA_CHAR16("abcdef"), EA_CHAR16("a")));
+	EATEST_VERIFY(!Strstart(EA_CHAR16("abcdef"), EA_CHAR16("A")));
+	EATEST_VERIFY( Strstart(EA_CHAR16("abcdef"), EA_CHAR16("abcdef")));
+	EATEST_VERIFY(!Strstart(EA_CHAR16("abcdef"), EA_CHAR16("bcdef ")));
+	EATEST_VERIFY(!Strstart(EA_CHAR16("abcdef"), EA_CHAR16("Abcdef")));
+
+	EATEST_VERIFY( Stristart(EA_CHAR16(""),       EA_CHAR16("")));
+	EATEST_VERIFY( Stristart(EA_CHAR16("a"),      EA_CHAR16("")));
+	EATEST_VERIFY(!Stristart(EA_CHAR16(""),       EA_CHAR16("a")));
+	EATEST_VERIFY( Stristart(EA_CHAR16("abcdef"), EA_CHAR16("a")));
+	EATEST_VERIFY( Stristart(EA_CHAR16("abcdef"), EA_CHAR16("A")));
+	EATEST_VERIFY( Stristart(EA_CHAR16("abcdef"), EA_CHAR16("abcdef")));
+	EATEST_VERIFY(!Stristart(EA_CHAR16("abcdef"), EA_CHAR16("bcdef ")));
+	EATEST_VERIFY( Stristart(EA_CHAR16("abcdef"), EA_CHAR16("Abcdef")));
+
+	// char32_t
+	EATEST_VERIFY( Strstart(EA_CHAR32(""),       EA_CHAR32("")));
+	EATEST_VERIFY( Strstart(EA_CHAR32("a"),      EA_CHAR32("")));
+	EATEST_VERIFY(!Strstart(EA_CHAR32(""),       EA_CHAR32("a")));
+	EATEST_VERIFY( Strstart(EA_CHAR32("abcdef"), EA_CHAR32("a")));
+	EATEST_VERIFY(!Strstart(EA_CHAR32("abcdef"), EA_CHAR32("A")));
+	EATEST_VERIFY( Strstart(EA_CHAR32("abcdef"), EA_CHAR32("abcdef")));
+	EATEST_VERIFY(!Strstart(EA_CHAR32("abcdef"), EA_CHAR32("bcdef ")));
+	EATEST_VERIFY(!Strstart(EA_CHAR32("abcdef"), EA_CHAR32("Abcdef")));
+
+	EATEST_VERIFY( Stristart(EA_CHAR32(""),       EA_CHAR32("")));
+	EATEST_VERIFY( Stristart(EA_CHAR32("a"),      EA_CHAR32("")));
+	EATEST_VERIFY(!Stristart(EA_CHAR32(""),       EA_CHAR32("a")));
+	EATEST_VERIFY( Stristart(EA_CHAR32("abcdef"), EA_CHAR32("a")));
+	EATEST_VERIFY( Stristart(EA_CHAR32("abcdef"), EA_CHAR32("A")));
+	EATEST_VERIFY( Stristart(EA_CHAR32("abcdef"), EA_CHAR32("abcdef")));
+	EATEST_VERIFY(!Stristart(EA_CHAR32("abcdef"), EA_CHAR32("bcdef ")));
+	EATEST_VERIFY( Stristart(EA_CHAR32("abcdef"), EA_CHAR32("Abcdef")));
+
+	return nErrorCount;
+}
+
+
+
+static int TestStrend()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount = 0;
+
+	// char8_t
+	EATEST_VERIFY( Strend("",       ""));
+	EATEST_VERIFY( Strend("f",      ""));
+	EATEST_VERIFY(!Strend("",       "f"));
+	EATEST_VERIFY( Strend("abcdef", "f"));
+	EATEST_VERIFY(!Strend("abcdef", "F"));
+	EATEST_VERIFY( Strend("abcdef", "abcdef"));
+	EATEST_VERIFY(!Strend("abcdef", "abcde "));
+	EATEST_VERIFY(!Strend("abcdef", " abcde"));
+	EATEST_VERIFY(!Strend("abcdef", "Abcdef"));
+	EATEST_VERIFY(!Strend("abcdef", "abcdefghi"));
+
+	EATEST_VERIFY( Striend("",       ""));
+	EATEST_VERIFY( Striend("f",      ""));
+	EATEST_VERIFY(!Striend("",       "f"));
+	EATEST_VERIFY( Striend("abcdef", "f"));
+	EATEST_VERIFY( Striend("abcdef", "F"));
+	EATEST_VERIFY( Striend("abcdef", "abcdef"));
+	EATEST_VERIFY(!Striend("abcdef", "abcde "));
+	EATEST_VERIFY(!Striend("abcdef", " abcde"));
+	EATEST_VERIFY( Striend("abcdef", "Abcdef"));
+	EATEST_VERIFY(!Striend("abcdef", "abcdefghi"));
+
+	// char16_t
+	EATEST_VERIFY( Strend(EA_CHAR16(""),       EA_CHAR16("")));
+	EATEST_VERIFY( Strend(EA_CHAR16("f"),      EA_CHAR16("")));
+	EATEST_VERIFY(!Strend(EA_CHAR16(""),       EA_CHAR16("f")));
+	EATEST_VERIFY( Strend(EA_CHAR16("abcdef"), EA_CHAR16("f")));
+	EATEST_VERIFY(!Strend(EA_CHAR16("abcdef"), EA_CHAR16("F")));
+	EATEST_VERIFY( Strend(EA_CHAR16("abcdef"), EA_CHAR16("abcdef")));
+	EATEST_VERIFY(!Strend(EA_CHAR16("abcdef"), EA_CHAR16("abcde ")));
+	EATEST_VERIFY(!Strend(EA_CHAR16("abcdef"), EA_CHAR16(" abcde")));
+	EATEST_VERIFY(!Strend(EA_CHAR16("abcdef"), EA_CHAR16("Abcdef")));
+	EATEST_VERIFY(!Strend(EA_CHAR16("abcdef"), EA_CHAR16("abcdefghi")));
+
+	EATEST_VERIFY( Striend(EA_CHAR16(""),       EA_CHAR16("")));
+	EATEST_VERIFY( Striend(EA_CHAR16("f"),      EA_CHAR16("")));
+	EATEST_VERIFY(!Striend(EA_CHAR16(""),       EA_CHAR16("f")));
+	EATEST_VERIFY( Striend(EA_CHAR16("abcdef"), EA_CHAR16("f")));
+	EATEST_VERIFY( Striend(EA_CHAR16("abcdef"), EA_CHAR16("F")));
+	EATEST_VERIFY( Striend(EA_CHAR16("abcdef"), EA_CHAR16("abcdef")));
+	EATEST_VERIFY(!Striend(EA_CHAR16("abcdef"), EA_CHAR16("abcde ")));
+	EATEST_VERIFY(!Striend(EA_CHAR16("abcdef"), EA_CHAR16(" abcde")));
+	EATEST_VERIFY( Striend(EA_CHAR16("abcdef"), EA_CHAR16("Abcdef")));
+	EATEST_VERIFY(!Striend(EA_CHAR16("abcdef"), EA_CHAR16("abcdefghi")));
+
+	// char32_t
+	EATEST_VERIFY( Strend(EA_CHAR32(""),       EA_CHAR32("")));
+	EATEST_VERIFY( Strend(EA_CHAR32("f"),      EA_CHAR32("")));
+	EATEST_VERIFY(!Strend(EA_CHAR32(""),       EA_CHAR32("f")));
+	EATEST_VERIFY( Strend(EA_CHAR32("abcdef"), EA_CHAR32("f")));
+	EATEST_VERIFY(!Strend(EA_CHAR32("abcdef"), EA_CHAR32("F")));
+	EATEST_VERIFY( Strend(EA_CHAR32("abcdef"), EA_CHAR32("abcdef")));
+	EATEST_VERIFY(!Strend(EA_CHAR32("abcdef"), EA_CHAR32("abcde ")));
+	EATEST_VERIFY(!Strend(EA_CHAR32("abcdef"), EA_CHAR32(" abcde")));
+	EATEST_VERIFY(!Strend(EA_CHAR32("abcdef"), EA_CHAR32("Abcdef")));
+	EATEST_VERIFY(!Strend(EA_CHAR32("abcdef"), EA_CHAR32("abcdefghi")));
+
+	EATEST_VERIFY( Striend(EA_CHAR32(""),       EA_CHAR32("")));
+	EATEST_VERIFY( Striend(EA_CHAR32("f"),      EA_CHAR32("")));
+	EATEST_VERIFY(!Striend(EA_CHAR32(""),       EA_CHAR32("f")));
+	EATEST_VERIFY( Striend(EA_CHAR32("abcdef"), EA_CHAR32("f")));
+	EATEST_VERIFY( Striend(EA_CHAR32("abcdef"), EA_CHAR32("F")));
+	EATEST_VERIFY( Striend(EA_CHAR32("abcdef"), EA_CHAR32("abcdef")));
+	EATEST_VERIFY(!Striend(EA_CHAR32("abcdef"), EA_CHAR32("abcde ")));
+	EATEST_VERIFY(!Striend(EA_CHAR32("abcdef"), EA_CHAR32(" abcde")));
+	EATEST_VERIFY( Striend(EA_CHAR32("abcdef"), EA_CHAR32("Abcdef")));
+	EATEST_VERIFY(!Striend(EA_CHAR32("abcdef"), EA_CHAR32("abcdefghi")));
+
+	return nErrorCount;
+}
+
+
+int TestString()
+{
+	int nErrorCount = 0;
+
+	EA::UnitTest::Report("TestString\n");
+
+	// Disable assertions, because we will be explicitly testing the failure 
+	// modes of some of the functions here. And we don't want the tests to 
+	// abort due to assertion failures.
+	bool assertionsEnabled = EA::StdC::GetAssertionsEnabled();
+	EA::StdC::SetAssertionsEnabled(false);
+
+	nErrorCount += TestStrtoi();
+	nErrorCount += TestAtof();
+	nErrorCount += TestFtoa();
+	nErrorCount += TestReduceFloatString();
+	nErrorCount += TestConvertString();
+	nErrorCount += TestStringCore();    
+	nErrorCount += TestEcvt();
+	nErrorCount += TestItoa();
+	nErrorCount += TestStrtod();
+	nErrorCount += TestStrstrip();
+	nErrorCount += TestStrstart();
+	nErrorCount += TestStrend();
+	
+	EA::StdC::SetAssertionsEnabled(assertionsEnabled);
+
+	return nErrorCount;
+}
+
+
+#if defined(_MSC_VER)
+	#pragma warning(pop)
+#endif
+
+

+ 1412 - 0
test/source/TestTextUtil.cpp

@@ -0,0 +1,1412 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAStdC/EATextUtil.h>
+#include <EAStdC/EAString.h>
+#include <EAStdCTest/EAStdCTest.h>
+#include <EATest/EATest.h>
+#include <EASTL/fixed_string.h>
+#include <EASTL/string.h>
+
+#include <string.h>
+
+#ifdef _MSC_VER
+	#pragma warning(disable: 4310)  // cast truncates constant value.
+#endif
+
+
+typedef eastl::basic_string<char8_t>  String8;
+typedef eastl::basic_string<char16_t> String16;
+typedef eastl::basic_string<char32_t> String32;
+
+
+
+static int TestUTF8()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	bool           bResult;
+	const char8_t* pResult;
+	char16_t       cResult;
+	size_t         nResult;
+	char8_t        buffer[32];
+
+	// We would need a lot more strings than this to test this functionality well.
+	const uint8_t str1[]    = { 0 };
+	const uint8_t str2[]    = { 'a', 'b', 'c', 0 };
+	const uint8_t str3[]    = { 0xe8,    0x8f, 0xa4,    0xc8, 0x80,    0x61,    0 }; // 0x83e4, 0x0200, 0x0061
+	const uint8_t str4[]    = { 0x7f,    0xc4, 0x80,    0xef, 0xbf,    0xb0,    0 }; // 0x007f, 0x0100, 0xfff0
+
+	const char8_t* strArray[] =
+	{ 
+		reinterpret_cast<const char8_t*>(str1), 
+		reinterpret_cast<const char8_t*>(str2), 
+		reinterpret_cast<const char8_t*>(str3), 
+		reinterpret_cast<const char8_t*>(str4)
+	};
+
+	// These represent bad combinations of bytes that any UTF8 decoder should recognize.
+	const uint8_t strBad1[] = { 0xc1, 0 };
+	const uint8_t strBad2[] = { 0xfe, 0 };
+	const uint8_t strBad3[] = { 0xc2, 0x7f, 0 };
+	const uint8_t strBad4[] = { 0xc2, 0xc0, 0 };
+	const uint8_t strBad5[] = { 0xe0, 0x9f, 0x80, 0 };
+	const uint8_t strBad6[] = { 0xe0, 0x80, 0xff, 0 };
+
+	// The following UTF-8 sequences should be rejected like malformed sequences, because they never represent valid ISO 10646 characters and a UTF-8 decoder that accepts them might introduce security problems comparable to overlong UTF-8 sequences. 
+	//const uint8_t strBad7[] = { 0xed, 0xa0, 0x80, 0 };
+	//const uint8_t strBad8[] = { 0xed, 0xbf, 0xbf, 0 };
+	//const uint8_t strBad9[] = { 0xef, 0xbf, 0xbe, 0 };
+
+	const char8_t* strBadArray[] =
+	{
+		reinterpret_cast<const char8_t*>(strBad1), 
+		reinterpret_cast<const char8_t*>(strBad2), 
+		reinterpret_cast<const char8_t*>(strBad3), 
+		reinterpret_cast<const char8_t*>(strBad4), 
+		reinterpret_cast<const char8_t*>(strBad5), 
+		reinterpret_cast<const char8_t*>(strBad6)
+	/*, reinterpret_cast<const char8_t*>(strBad7), 
+		reinterpret_cast<const char8_t*>(strBad8), 
+		reinterpret_cast<const char8_t*>(strBad9) */
+	};
+
+
+	// bool UTF8Validate(const char8_t* p, size_t nLength);
+	for(size_t i = 0; i < sizeof(strArray)/sizeof(strArray[0]); ++i)
+	{
+		bResult = UTF8Validate(strArray[i], strlen(strArray[i]));
+		EATEST_VERIFY(bResult);
+	}
+
+	for(size_t i = 0; i < sizeof(strBadArray)/sizeof(strBadArray[0]); ++i)
+	{
+		bResult = UTF8Validate(strBadArray[i], strlen(strBadArray[i]));
+		EATEST_VERIFY(!bResult);
+	}
+
+
+	// char8_t* UTF8Increment(const char8_t* p, size_t n);
+	// char8_t* UTF8Decrement(const char8_t* p, size_t n);
+	pResult = UTF8Increment(strArray[0], 1);
+	EATEST_VERIFY(pResult == (strArray[0] + 1));
+	pResult = UTF8Decrement(pResult, 1);
+	EATEST_VERIFY(pResult == strArray[0]);
+
+	pResult = UTF8Increment(strArray[1], 3);
+	EATEST_VERIFY(pResult == (strArray[1] + 3));
+	pResult = UTF8Decrement(pResult, 3);
+	EATEST_VERIFY(pResult == strArray[1]);
+
+	pResult = UTF8Increment(strArray[2], 3);
+	EATEST_VERIFY(pResult == (strArray[2] + 6));
+	pResult = UTF8Decrement(pResult, 3);
+	EATEST_VERIFY(pResult == strArray[2]);
+
+	pResult = UTF8Increment(strArray[3], 3);
+	EATEST_VERIFY(pResult == (strArray[3] + 6));
+	pResult = UTF8Decrement(pResult, 3);
+	EATEST_VERIFY(pResult == strArray[3]);
+
+
+	// size_t UTF8Length(const char8_t* p);
+	EATEST_VERIFY(UTF8Length("0123456789") == 10);
+	EATEST_VERIFY(UTF8Length("") == 0);
+	EATEST_VERIFY(UTF8Length("\xc2" "\xa2") == 1);
+	EATEST_VERIFY(UTF8Length("\xd7" "\x90") == 1);
+	EATEST_VERIFY(UTF8Length("\xe0" "\xbc" "\xa0") == 1);
+	EATEST_VERIFY(UTF8Length("\xc2\x80 \xc2\x81 \xdf\xbe \xdf\xbf") == 7);
+	EATEST_VERIFY(UTF8Length("\xe0\xa0\x80 \xe0\xa0\x81 \xef\xbf\xbe \xef\xbf\xbf") == 7);
+	EATEST_VERIFY(UTF8Length("\xf0\x90\x80\x80 \xf0\x90\x80\x81") == 3);
+	EATEST_VERIFY(UTF8Length("\xf4\x8f\xbf\xbe \xf4\x8f\xbf\xbf") == 3);
+
+
+	// size_t UTF8Length(const char16_t* p);
+	EATEST_VERIFY(UTF8Length(EA_CHAR16("0123456789")) == 10);
+	EATEST_VERIFY(UTF8Length(EA_CHAR16("")) == 0);
+	EATEST_VERIFY(UTF8Length(EA_CHAR16("\x00a0")) == 2);
+	EATEST_VERIFY(UTF8Length(EA_CHAR16("\x0400")) == 2);
+	EATEST_VERIFY(UTF8Length(EA_CHAR16("\x0800")) == 3);
+
+	// We have to break up the string into multiple sub-strings because the \x escape sequence has limitations in how it works.
+	eastl::fixed_string<char16_t, 32> s16; s16 = EA_CHAR16("\xffff"); s16 += EA_CHAR16("\xffff"); // We use a string object because some compilers don't support 16 bit string literals, and thus EA_CHAR16 is a function and doesn't just prepend "L" or "u" to the string.
+	EATEST_VERIFY(UTF8Length(s16.c_str()) == 6);
+
+	s16 = EA_CHAR16("\xffff"); s16 += EA_CHAR16("\x0900"); s16 += EA_CHAR16("0"); s16 += EA_CHAR16("\x00a0");
+	EATEST_VERIFY(UTF8Length(s16.c_str()) == 9);
+
+
+	// size_t UTF8Length(const char32_t* p);
+	EATEST_VERIFY(UTF8Length(EA_CHAR32("0123456789")) == 10);
+	EATEST_VERIFY(UTF8Length(EA_CHAR32("")) == 0);
+	EATEST_VERIFY(UTF8Length(EA_CHAR32("\x00a0")) == 2);
+	EATEST_VERIFY(UTF8Length(EA_CHAR32("\x0400")) == 2);
+	EATEST_VERIFY(UTF8Length(EA_CHAR32("\x0800")) == 3);
+
+	// We have to break up the string into multiple sub-strings because the \x escape sequence has limitations in how it works.
+	eastl::fixed_string<char32_t, 32> s32; s32 = EA_CHAR32("\xffff"); s32 += EA_CHAR32("\xffff"); // We use a string object because some compilers don't support 32 bit string literals, and thus EA_CHAR32 is a function and doesn't just prepend "L" or "u" to the string.
+	EATEST_VERIFY(UTF8Length(s32.c_str()) == 6);
+
+	s32 = EA_CHAR32("\xffff"); s32 += EA_CHAR32("\x0900"); s32 += EA_CHAR32("0"); s32 += EA_CHAR32("\x00a0");
+	EATEST_VERIFY(UTF8Length(s32.c_str()) == 9);
+
+
+	// size_t UTF8CharSize(const char8_t* p);
+	EATEST_VERIFY(UTF8CharSize(strArray[0]) == 1);
+	EATEST_VERIFY(UTF8CharSize(strArray[1]) == 1);
+	EATEST_VERIFY(UTF8CharSize(strArray[2] + 0) == 3);
+	EATEST_VERIFY(UTF8CharSize(strArray[2] + 3) == 2);
+	EATEST_VERIFY(UTF8CharSize(strArray[3] + 1) == 2);
+	EATEST_VERIFY(UTF8CharSize(strArray[3] + 3) == 3);
+
+
+	// size_t UTF8CharSize(char16_t c);
+	nResult = UTF8CharSize((char16_t)0x0001);
+	EATEST_VERIFY(nResult == 1);
+
+	nResult = UTF8CharSize((char16_t)0x007f);
+	EATEST_VERIFY(nResult == 1);
+
+	nResult = UTF8CharSize((char16_t)0x0080);
+	EATEST_VERIFY(nResult == 2);
+
+	nResult = UTF8CharSize((char16_t)0x07ff);
+	EATEST_VERIFY(nResult == 2);
+
+	nResult = UTF8CharSize((char16_t)0x0800);
+	EATEST_VERIFY(nResult == 3);
+
+	nResult = UTF8CharSize((char16_t)0xfffd);
+	EATEST_VERIFY(nResult == 3);
+
+
+	// size_t UTF8CharSize(char32_t c);
+	nResult = UTF8CharSize((char32_t)0x0001);
+	EATEST_VERIFY(nResult == 1);
+
+	nResult = UTF8CharSize((char32_t)0x007f);
+	EATEST_VERIFY(nResult == 1);
+
+	nResult = UTF8CharSize((char32_t)0x0080);
+	EATEST_VERIFY(nResult == 2);
+
+	nResult = UTF8CharSize((char32_t)0x07ff);
+	EATEST_VERIFY(nResult == 2);
+
+	nResult = UTF8CharSize((char32_t)0x0800);
+	EATEST_VERIFY(nResult == 3);
+
+	nResult = UTF8CharSize((char32_t)0xfffd);
+	EATEST_VERIFY(nResult == 3);
+
+
+	// char16_t UTF8ReadChar(const char8_t* p, const char8_t** ppEnd = NULL);
+	pResult = strArray[0];
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x0000) && (pResult == strArray[0] + 1));
+
+	pResult = strArray[1];
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 'a') && (pResult == strArray[1] + 1));
+
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 'b') && (pResult == strArray[1] + 2));
+
+	pResult = strArray[2];
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x83e4) && (pResult == strArray[2] + 3));
+
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x0200) && (pResult == strArray[2] + 5));
+
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x0061) && (pResult == strArray[2] + 6));
+
+	pResult = strArray[3];
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x007f) && (pResult == strArray[3] + 1));
+
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0x0100) && (pResult == strArray[3] + 3));
+
+	cResult = UTF8ReadChar(pResult, &pResult);
+	EATEST_VERIFY((cResult == 0xfff0) && (pResult == strArray[3] + 6));
+
+
+	// char8_t* UTF8WriteChar(char8_t* p, char16_t c);
+	pResult = buffer;
+	pResult = UTF8WriteChar((char8_t*)pResult, (char16_t)0x83e4);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char16_t)0x0200);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char16_t)0x0061);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char16_t)0x0000);
+	EATEST_VERIFY((strcmp(buffer, strArray[2]) == 0) && (pResult == buffer + 7));
+
+
+	// char8_t* UTF8WriteChar(char8_t* p, char32_t c);
+	pResult = buffer;
+	pResult = UTF8WriteChar((char8_t*)pResult, (char32_t)0x83e4);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char32_t)0x0200);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char32_t)0x0061);
+	pResult = UTF8WriteChar((char8_t*)pResult, (char32_t)0x0000);
+	EATEST_VERIFY((strcmp(buffer, strArray[2]) == 0) && (pResult == buffer + 7));
+
+
+	// bool UTF8IsSoloByte(char8_t c);
+	EATEST_VERIFY( UTF8IsSoloByte('\0'));
+	EATEST_VERIFY( UTF8IsSoloByte('\n'));
+	EATEST_VERIFY( UTF8IsSoloByte('a'));
+	EATEST_VERIFY( UTF8IsSoloByte((char8_t)0x7f));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0x80));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xfd));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xfe));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xff));
+
+
+	// bool UTF8IsLeadByte(char8_t c);
+	EATEST_VERIFY( UTF8IsSoloByte('\0'));
+	EATEST_VERIFY( UTF8IsSoloByte('\n'));
+	EATEST_VERIFY( UTF8IsSoloByte('a'));
+	EATEST_VERIFY( UTF8IsSoloByte((char8_t)0x7f));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xc0));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xd1));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xe4));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xf0));       // This assumes that we don't support 4, 5, 6 byte sequences.
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xfe));
+	EATEST_VERIFY(!UTF8IsSoloByte((char8_t)0xff));
+
+
+	// bool UTF8IsFollowByte(char8_t c);
+	EATEST_VERIFY(!UTF8IsFollowByte((char8_t)0x00));
+	EATEST_VERIFY(!UTF8IsFollowByte((char8_t)0x7f));
+	EATEST_VERIFY( UTF8IsFollowByte((char8_t)0x80));
+	EATEST_VERIFY( UTF8IsFollowByte((char8_t)0xbf));
+	EATEST_VERIFY(!UTF8IsFollowByte((char8_t)0xc0));
+	EATEST_VERIFY(!UTF8IsFollowByte((char8_t)0xff));
+
+	// char8_t* UTF8ReplaceInvalidChar(char8_t* pIn, size_t nLength, char8_t replaceWith);
+	{
+		char outBuffer[256] = {0};
+
+		{
+			auto* pBad  = u8"foofoobaazong";
+			auto* pGood = u8"foofoobaazong";
+
+			auto* pOut = UTF8ReplaceInvalidChar(pBad, UTF8Length(pBad), outBuffer, '?');
+
+			EATEST_VERIFY(UTF8Length(pBad) == size_t(pOut - outBuffer));
+			EATEST_VERIFY(Strcmp(pGood, outBuffer) == 0);
+		}
+
+		{
+			auto* pBad = "foofoo\xfa" "baazong";
+			auto* pGood = "foofoo?baazong";
+
+			auto* pOut = UTF8ReplaceInvalidChar(pBad, UTF8Length(pBad), outBuffer, '?');
+
+			EATEST_VERIFY(UTF8Length(pBad) == size_t(pOut - outBuffer));
+			EATEST_VERIFY(Strcmp(pGood, outBuffer) == 0);
+		}
+
+
+		{
+			auto* pBad = "foofoo\xfa\xfa\xfa\xfa\xfa" "baazong";
+			auto* pGood = "foofoo?????baazong";
+
+			auto* pOut = UTF8ReplaceInvalidChar(pBad, UTF8Length(pBad), outBuffer, '?');
+
+			EATEST_VERIFY(UTF8Length(pBad) == size_t(pOut - outBuffer));
+			EATEST_VERIFY(Strcmp(pGood, outBuffer) == 0);
+		}
+
+		{
+			auto* pBad = "foo\xfa" "foo\xfa\xfa" "b\xfa" "a\xfa" "a\xfa" "z\xfa" "o\xfa" "n\xfa" "g\xfa";
+			auto* pGood = "foo?foo??b?a?a?z?o?n?g?";
+
+			auto* pOut = UTF8ReplaceInvalidChar(pBad, UTF8Length(pBad), outBuffer, '?');
+
+			EATEST_VERIFY(UTF8Length(pBad) == size_t(pOut - outBuffer));
+			EATEST_VERIFY(Strcmp(pGood, outBuffer) == 0);
+		}
+	}
+
+	return nErrorCount;
+}
+
+
+
+int TestTextUtil()
+{
+	using namespace EA::StdC;
+
+	int nErrorCount(0);
+
+	EA::UnitTest::Report("TestTextUtil\n");
+
+	nErrorCount += TestUTF8();
+
+	// WildcardMatch
+	{
+		// char8_t
+		EATEST_VERIFY(WildcardMatch("abcde", "*e",     false) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "*f",     false) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "???de",  false) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "????g",  false) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*c??",   false) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "*e??",   false) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*????",  false) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "bcdef",  false) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*?????", false) == true);
+
+		EATEST_VERIFY(WildcardMatch("abcdE", "*E",     true) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "*f",     true) == false);
+		EATEST_VERIFY(WildcardMatch("abcDE", "???de",  true) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "????g",  true) == false);
+		EATEST_VERIFY(WildcardMatch("abCde", "*c??",   true) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*e??",   true) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*????",  true) == true);
+		EATEST_VERIFY(WildcardMatch("abcde", "bcdef",  true) == false);
+		EATEST_VERIFY(WildcardMatch("abcde", "*?????", true) == true);
+
+		// char16_t
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*e"),     false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*f"),     false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("???de"),  false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("????g"),  false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*c??"),   false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*e??"),   false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*????"),  false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("bcdef"),  false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*?????"), false) == true);
+
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcdE"), EA_CHAR16("*E"),     true) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*f"),     true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcDE"), EA_CHAR16("???de"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("????g"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abCde"), EA_CHAR16("*c??"),   true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*e??"),   true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*????"),  true) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("bcdef"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR16("abcde"), EA_CHAR16("*?????"), true) == true);
+
+		// char32_t
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*e"),     false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*f"),     false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("???de"),  false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("????g"),  false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*c??"),   false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*e??"),   false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*????"),  false) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("bcdef"),  false) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*?????"), false) == true);
+
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcdE"), EA_CHAR32("*E"),     true) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*f"),     true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcDE"), EA_CHAR32("???de"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("????g"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abCde"), EA_CHAR32("*c??"),   true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*e??"),   true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*????"),  true) == true);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("bcdef"),  true) == false);
+		EATEST_VERIFY(WildcardMatch(EA_CHAR32("abcde"), EA_CHAR32("*?????"), true) == true);
+	}
+
+
+	// EASTDC_API bool ParseDelimitedText(const char8_t* pText, const char8_t* pTextEnd, char8_t cDelimiter, 
+	//                                    const char8_t*& pToken, const char8_t*& pTokenEnd, const char8_t** ppNewText);
+	// EASTDC_API bool ParseDelimitedText(const char16_t* pText, const char16_t* pTextEnd, char16_t cDelimiter, 
+	//                                    const char16_t*& pToken, const char16_t*& pTokenEnd, const char16_t** ppNewText);
+	// EASTDC_API bool ParseDelimitedText(const char32_t* pText, const char32_t* pTextEnd, char32_t cDelimiter, 
+	//                                    const char32_t*& pToken, const char32_t*& pTokenEnd, const char32_t** ppNewText);
+	{
+		/// Example behavior:
+		///   1  Input String                                             MaxResults        Delimiter    Return Value     OutputArray Size  Output Array
+		///   2  "";                                                      -1                ' '          0                0                 ""
+		///   3  "000 111";                                               -1                ' '          2                2                 "000"  "111"
+		///   4  "000 111   222   333 444 \"555 555\" 666";               -1                ' '          7                7                 "000"  "111"  "222"  "333"  "444"  "555 555"  "666"
+		///   5  "     000 111 222         333                ";          -1                ' '          4                4                 "000"  "111"  "222"  "333"
+		///   6  "     000 111 222         333                ";           2                ' '          2                2                 "000"  "111"
+		///   7  "";                                                      -1                ','          0                0                 ""
+		///   8  "000,111";                                               -1                ','          2                2                 "000"  "111"
+		///   9  "000,  111 , 222   333 ,444 \"555,  555  \" 666";        -1                ','          4                4                 "000"  "111"  "222   333"  "444 \"555,  555  \" 666"
+		///  10  "  ,, 000 ,111, 222,         333          ,     ";       -1                ','          6                6                 ""   ""   "000"   "111"   "222"   "333"
+		///  11  "  ,, 000 ,111, 222,         333          ,     ";        2                ','          0                0                 ""   ""
+
+		const char16_t* pTest2  = EA_CHAR16("");
+		const char16_t* pTest3  = EA_CHAR16("000 111");
+		const char16_t* pTest4  = EA_CHAR16("  \"555 555\"   ");
+	  //const char16_t* pTest5  = EA_CHAR16("     000 111 222         333                ");
+	  //const char16_t* pTest6  = EA_CHAR16("     000 111 222         333                ");
+	  //const char16_t* pTest7  = EA_CHAR16("     000 111 222         333                ");
+	  //const char16_t* pTest8  = EA_CHAR16("000,111"));
+	  //const char16_t* pTest9  = EA_CHAR16("000,  111 , 222   333 ,444 \"555,  555  \" 666");
+	  //const char16_t* pTest10 = EA_CHAR16("  ,, 000 ,111, 222,         333          ,     ");
+	  //const char16_t* pTest11 = EA_CHAR16("  ,, 000 ,111, 222,         333          ,     ");
+
+		bool            result;
+		const char16_t* pToken;
+		const char16_t* pTokenEnd;
+		const char16_t* pNewText;
+
+		// Test2
+		result = ParseDelimitedText(pTest2, pTest2 + Strlen(pTest2), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(!result && (pToken == pTest2) && (pTokenEnd == pTest2) && (pNewText == pTest2));
+
+		// Test3
+		result = ParseDelimitedText(pTest3, pTest3 + Strlen(pTest3), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest3) && (pTokenEnd == pTest3 + 3) && (pNewText == pTest3 + 3));
+
+		result = ParseDelimitedText(pTest3 + 3, pTest3 + Strlen(pTest3), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest3 + 4) && (pTokenEnd == pTest3 + 7) && (pNewText == pTest3 + 7));
+
+		// Test4
+		result = ParseDelimitedText(pTest4, pTest4 + Strlen(pTest4), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest4 + 3) && (pTokenEnd == pTest4 + 10) && (pNewText == pTest4 + 11));
+	}
+	{
+		const char32_t* pTest2  = EA_CHAR32("");
+		const char32_t* pTest3  = EA_CHAR32("000 111");
+		const char32_t* pTest4  = EA_CHAR32("  \"555 555\"   ");
+	  //const char32_t* pTest5  = EA_CHAR32("     000 111 222         333                ");
+	  //const char32_t* pTest6  = EA_CHAR32("     000 111 222         333                ");
+	  //const char32_t* pTest7  = EA_CHAR32("     000 111 222         333                ");
+	  //const char32_t* pTest8  = EA_CHAR32("000,111"));
+	  //const char32_t* pTest9  = EA_CHAR32("000,  111 , 222   333 ,444 \"555,  555  \" 666");
+	  //const char32_t* pTest10 = EA_CHAR32("  ,, 000 ,111, 222,         333          ,     ");
+	  //const char32_t* pTest11 = EA_CHAR32("  ,, 000 ,111, 222,         333          ,     ");
+
+		bool            result;
+		const char32_t* pToken;
+		const char32_t* pTokenEnd;
+		const char32_t* pNewText;
+
+		// Test2
+		result = ParseDelimitedText(pTest2, pTest2 + Strlen(pTest2), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(!result && (pToken == pTest2) && (pTokenEnd == pTest2) && (pNewText == pTest2));
+
+		// Test3
+		result = ParseDelimitedText(pTest3, pTest3 + Strlen(pTest3), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest3) && (pTokenEnd == pTest3 + 3) && (pNewText == pTest3 + 3));
+
+		result = ParseDelimitedText(pTest3 + 3, pTest3 + Strlen(pTest3), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest3 + 4) && (pTokenEnd == pTest3 + 7) && (pNewText == pTest3 + 7));
+
+		// Test4
+		result = ParseDelimitedText(pTest4, pTest4 + Strlen(pTest4), ' ', pToken, pTokenEnd, &pNewText);
+		EATEST_VERIFY(result && (pToken == pTest4 + 3) && (pTokenEnd == pTest4 + 10) && (pNewText == pTest4 + 11));
+	}
+
+
+	// ConvertBinaryDataToASCIIArray / ConvertASCIIArrayToBinaryData
+	{
+		uint8_t  data[4] = { 0x12, 0x34, 0x56, 0x78 };
+		char8_t  result8[32];
+		char16_t result16[32];
+		char32_t result32[32];
+
+		ConvertBinaryDataToASCIIArray(&data, 4, result8);
+		EATEST_VERIFY(Strcmp(result8, "12345678") == 0);
+
+		ConvertBinaryDataToASCIIArray(&data, 4, result16);
+		EATEST_VERIFY(Strcmp(result16, EA_CHAR16("12345678")) == 0);
+
+		ConvertBinaryDataToASCIIArray(&data, 4, result32);
+		EATEST_VERIFY(Strcmp(result32, EA_CHAR32("12345678")) == 0);
+
+
+		memset(data, 0, sizeof(data));
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result8, 8, data) == true);
+		EATEST_VERIFY((data[0] == 0x12) && (data[3] == 0x78));
+
+		memset(data, 0, sizeof(data));
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result16, 8, data) == true);
+		EATEST_VERIFY((data[0] == 0x12) && (data[3] == 0x78));
+
+		memset(data, 0, sizeof(data));
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result32, 8, data) == true);
+		EATEST_VERIFY((data[0] == 0x12) && (data[3] == 0x78));
+
+
+		memset(data, 0, sizeof(data));
+		result8[0] = '_'; // Set it to an invalid hex character.
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result8, 8, data) == false);
+		EATEST_VERIFY((data[0] == 0x02) && (data[3] == 0x78));
+
+		memset(data, 0, sizeof(data));
+		result16[0] = '_'; // Set it to an invalid hex character.
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result16, 8, data) == false);
+		EATEST_VERIFY((data[0] == 0x02) && (data[3] == 0x78));
+
+		memset(data, 0, sizeof(data));
+		result32[0] = '_'; // Set it to an invalid hex character.
+		EATEST_VERIFY(ConvertASCIIArrayToBinaryData(result32, 8, data) == false);
+		EATEST_VERIFY((data[0] == 0x02) && (data[3] == 0x78));
+	}
+
+
+	// EASTDC_API const char8_t*  GetTextLine(const char8_t* pText, const char8_t* pTextEnd, const char8_t** ppNewText);
+	{
+		const char8_t* p1 = "";
+		const char8_t* p2 = "\n";
+		const char8_t* p3 = "\r\n";
+		const char8_t* p4 = "\r\n\n";
+		const char8_t* p5 = "\n\r\r";
+		const char8_t* p6 = "aaa\nbbb\rccc\r\nddd";
+		const char8_t* p7 = "aaa\nddd\n";
+		const char8_t* p8 = "aaa\nddd\r\n";
+
+		const char8_t* pLine;
+		const char8_t* pLineEnd;
+		const char8_t* pLineNext;
+
+		pLine = p1;
+		pLineEnd = GetTextLine(pLine, p1 + Strlen(p1), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == pLine);
+
+		pLine = p2;
+		pLineEnd = GetTextLine(pLine, p2 + Strlen(p2), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p2 + Strlen(p2));
+
+		pLine = p3;
+		pLineEnd = GetTextLine(pLine, p3 + Strlen(p3), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p3 + Strlen(p3));
+
+		pLine = p4;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p4 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p4 + Strlen(p4));
+
+		pLine = p5;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p5 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p5 + Strlen(p5));
+
+		pLine = p6;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 3);
+		EATEST_VERIFY(pLineNext == p6 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 7);
+		EATEST_VERIFY(pLineNext == p6 + 8);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 11);
+		EATEST_VERIFY(pLineNext == p6 + 13);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + Strlen(p6));
+		EATEST_VERIFY(pLineNext == p6 + Strlen(p6));
+
+		pLine = p7;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 3);
+		EATEST_VERIFY(pLineNext == p7 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 7);
+		EATEST_VERIFY(pLineNext == p7 + 8);
+
+		pLine = p8;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 3);
+		EATEST_VERIFY(pLineNext == p8 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 7);
+		EATEST_VERIFY(pLineNext == p8 + 9);
+	}
+
+
+
+	// EASTDC_API const char16_t* GetTextLine(const char16_t* pText, const char16_t* pTextEnd, const char16_t** ppNewText);
+	{
+		const char16_t* p1 = EA_CHAR16("");
+		const char16_t* p2 = EA_CHAR16("\n");
+		const char16_t* p3 = EA_CHAR16("\r\n");
+		const char16_t* p4 = EA_CHAR16("\r\n\n");
+		const char16_t* p5 = EA_CHAR16("\n\r\r");
+		const char16_t* p6 = EA_CHAR16("aaa\nbbb\rccc\r\nddd");
+		const char16_t* p7 = EA_CHAR16("aaa\nddd\n");
+		const char16_t* p8 = EA_CHAR16("aaa\nddd\r\n");
+
+		const char16_t* pLine;
+		const char16_t* pLineEnd;
+		const char16_t* pLineNext;
+
+		pLine = p1;
+		pLineEnd = GetTextLine(pLine, p1 + Strlen(p1), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == pLine);
+
+		pLine = p2;
+		pLineEnd = GetTextLine(pLine, p2 + Strlen(p2), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p2 + Strlen(p2));
+
+		pLine = p3;
+		pLineEnd = GetTextLine(pLine, p3 + Strlen(p3), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p3 + Strlen(p3));
+
+		pLine = p4;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p4 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p4 + Strlen(p4));
+
+		pLine = p5;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p5 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p5 + Strlen(p5));
+
+		pLine = p6;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 3);
+		EATEST_VERIFY(pLineNext == p6 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 7);
+		EATEST_VERIFY(pLineNext == p6 + 8);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 11);
+		EATEST_VERIFY(pLineNext == p6 + 13);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + Strlen(p6));
+		EATEST_VERIFY(pLineNext == p6 + Strlen(p6));
+
+		pLine = p7;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 3);
+		EATEST_VERIFY(pLineNext == p7 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 7);
+		EATEST_VERIFY(pLineNext == p7 + 8);
+
+		pLine = p8;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 3);
+		EATEST_VERIFY(pLineNext == p8 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 7);
+		EATEST_VERIFY(pLineNext == p8 + 9);
+	}
+
+	// EASTDC_API const char32_t* GetTextLine(const char32_t* pText, const char32_t* pTextEnd, const char32_t** ppNewText);
+	{
+		const char32_t* p1 = EA_CHAR32("");
+		const char32_t* p2 = EA_CHAR32("\n");
+		const char32_t* p3 = EA_CHAR32("\r\n");
+		const char32_t* p4 = EA_CHAR32("\r\n\n");
+		const char32_t* p5 = EA_CHAR32("\n\r\r");
+		const char32_t* p6 = EA_CHAR32("aaa\nbbb\rccc\r\nddd");
+		const char32_t* p7 = EA_CHAR32("aaa\nddd\n");
+		const char32_t* p8 = EA_CHAR32("aaa\nddd\r\n");
+
+		const char32_t* pLine;
+		const char32_t* pLineEnd;
+		const char32_t* pLineNext;
+
+		pLine = p1;
+		pLineEnd = GetTextLine(pLine, p1 + Strlen(p1), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == pLine);
+
+		pLine = p2;
+		pLineEnd = GetTextLine(pLine, p2 + Strlen(p2), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p2 + Strlen(p2));
+
+		pLine = p3;
+		pLineEnd = GetTextLine(pLine, p3 + Strlen(p3), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p3 + Strlen(p3));
+
+		pLine = p4;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p4 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p4 + Strlen(p4), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p4 + Strlen(p4));
+
+		pLine = p5;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == pLine);
+		EATEST_VERIFY(pLineNext == p5 + 2);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p5 + Strlen(p5), &pLineNext);
+		EATEST_VERIFY(pLineEnd == pLine);
+		EATEST_VERIFY(pLineNext == p5 + Strlen(p5));
+
+		pLine = p6;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 3);
+		EATEST_VERIFY(pLineNext == p6 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 7);
+		EATEST_VERIFY(pLineNext == p6 + 8);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + 11);
+		EATEST_VERIFY(pLineNext == p6 + 13);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p6 + Strlen(p6), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p6 + Strlen(p6));
+		EATEST_VERIFY(pLineNext == p6 + Strlen(p6));
+
+		pLine = p7;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 3);
+		EATEST_VERIFY(pLineNext == p7 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p7 + Strlen(p7), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p7 + 7);
+		EATEST_VERIFY(pLineNext == p7 + 8);
+
+		pLine = p8;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 3);
+		EATEST_VERIFY(pLineNext == p8 + 4);
+		pLine = pLineNext;
+		pLineEnd = GetTextLine(pLine, p8 + Strlen(p8), &pLineNext);
+		EATEST_VERIFY(pLineEnd  == p8 + 7);
+		EATEST_VERIFY(pLineNext == p8 + 9);
+	}
+
+
+	// template<typename String>
+	//  bool GetTextLine(String& sSource, String* pLine);
+	{
+		// To do
+	}
+
+
+
+	// EASTDC_API bool SplitTokenDelimited(const char8_t*  pSource, size_t nSourceLength, char8_t  cDelimiter, char8_t*  pToken, size_t nTokenLength, const char8_t**  ppNewSource = NULL);
+	{
+		// To do
+	}
+
+	// EASTDC_API bool SplitTokenDelimited(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+	{
+		const char16_t* pSource;
+		char16_t  pToken[8];
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a,b"     "a"      "b"          true
+
+		pSource = EA_CHAR16("a,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "ab,b"    "ab"     "b"          true
+
+		pSource = EA_CHAR16("ab,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("ab"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",a,b"    ""       "a,b"        true
+
+		pSource = EA_CHAR16(",a,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("a,b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",b"      ""       "b"          true
+
+		pSource = EA_CHAR16(",b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",,b"     ""       ",b"         true
+
+		pSource = EA_CHAR16(",,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(",b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",a,"     ""       "a,"         true
+
+		pSource = EA_CHAR16(",a,");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("a,"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a,"      "a"      ""           true
+
+		pSource = EA_CHAR16("a,");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ","       ""       ""           true
+
+		pSource = EA_CHAR16(",");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a"       "a"      ""           false
+
+		pSource = EA_CHAR16("a");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ""        ""       ""           false
+
+		pSource = EA_CHAR16("");
+
+		EATEST_VERIFY(!SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		// various additional tests
+		pSource = EA_CHAR16("testing,long,tokens");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("testing"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("long,tokens"));
+
+		pSource = EA_CHAR16("tokentoolarge,test");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("tokento"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("test"));
+
+		pSource = EA_CHAR16("test,source,length");
+
+		EATEST_VERIFY(!SplitTokenDelimited(pSource, 0, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("test,source,length"));
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, 2, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("te"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("st,source,length"));
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, 3, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("st"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("source,length"));
+	}
+
+	// EASTDC_API bool SplitTokenDelimited(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
+	{
+		const char32_t* pSource;
+		char32_t  pToken[8];
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a,b"     "a"      "b"          true
+
+		pSource = EA_CHAR32("a,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "ab,b"    "ab"     "b"          true
+
+		pSource = EA_CHAR32("ab,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("ab"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",a,b"    ""       "a,b"        true
+
+		pSource = EA_CHAR32(",a,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("a,b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",b"      ""       "b"          true
+
+		pSource = EA_CHAR32(",b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",,b"     ""       ",b"         true
+
+		pSource = EA_CHAR32(",,b");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(",b"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ",a,"     ""       "a,"         true
+
+		pSource = EA_CHAR32(",a,");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("a,"));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a,"      "a"      ""           true
+
+		pSource = EA_CHAR32("a,");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ","       ""       ""           true
+
+		pSource = EA_CHAR32(",");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    "a"       "a"      ""           false
+
+		pSource = EA_CHAR32("a");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///    source    token    new source   return
+		///    ---------------------------------------
+		///    ""        ""       ""           false
+
+		pSource = EA_CHAR32("");
+
+		EATEST_VERIFY(!SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		// various additional tests
+		pSource = EA_CHAR32("testing,long,tokens");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("testing"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("long,tokens"));
+
+		pSource = EA_CHAR32("tokentoolarge,test");
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, kLengthNull, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("tokento"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("test"));
+
+		pSource = EA_CHAR32("test,source,length");
+
+		EATEST_VERIFY(!SplitTokenDelimited(pSource, 0, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("test,source,length"));
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, 2, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("te"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("st,source,length"));
+
+		EATEST_VERIFY(SplitTokenDelimited(pSource, 3, ',', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("st"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("source,length"));
+	}
+
+
+	// template<typename String, typename Char>
+	// bool SplitTokenDelimited(String& sSource, Char cDelimiter, String* pToken);
+	{
+		// To do
+	}
+
+
+	// EASTDC_API bool SplitTokenSeparated(const char8_t*  pSource, size_t nSourceLength, char8_t  cDelimiter, char8_t*  pToken, size_t nTokenLength, const char8_t**  ppNewSource = NULL);
+	{
+		// To do
+	}
+
+	// EASTDC_API bool SplitTokenSeparated(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
+	{
+		String16        sSource, sToken;
+		const char16_t* pSource;
+		char16_t        pToken[8];
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a"       "a"      ""           true
+
+		pSource = EA_CHAR16("a");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a b"     "a"      "b"          true
+
+		pSource = EA_CHAR16("a b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a  b"    "a"      "b"          true
+
+		pSource = EA_CHAR16("a  b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a b"    "a"      "b"          true
+
+		pSource = EA_CHAR16(" a b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull,' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a b "   "a"      "b "         true
+
+		pSource = EA_CHAR16(" a b ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("b "));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a "     "a"      ""           true
+
+		pSource = EA_CHAR16(" a ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a  "    "a"      ""           true
+
+		pSource = EA_CHAR16(" a  ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("a"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    ""        ""       ""           false
+
+		pSource = EA_CHAR16("");
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " "       ""       ""           false
+
+		pSource = EA_CHAR16(" ");
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " "       ""       ""           false
+
+		pSource = NULL;
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16(""));
+		EATEST_VERIFY(pSource == NULL);
+
+
+		// various additional tests
+		pSource = EA_CHAR16("testing;;;source;;;length");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, 4, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("test"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("ing;;;source;;;length"));
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, 5, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("ing"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16(";source;;;length"));
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("source"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("length"));
+
+		pSource = EA_CHAR16(";;;;;;;123 456;;;;;a");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ';', pToken,8, &pSource));
+		EATEST_VERIFY(String16(pToken) == EA_CHAR16("123 456"));
+		EATEST_VERIFY(String16(pSource) == EA_CHAR16("a"));
+	}
+
+	// EASTDC_API bool SplitTokenSeparated(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
+	{
+		String32        sSource, sToken;
+		const char32_t* pSource;
+		char32_t        pToken[8];
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a"       "a"      ""           true
+
+		pSource = EA_CHAR32("a");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a b"     "a"      "b"          true
+
+		pSource = EA_CHAR32("a b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    "a  b"    "a"      "b"          true
+
+		pSource = EA_CHAR32("a  b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a b"    "a"      "b"          true
+
+		pSource = EA_CHAR32(" a b");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull,' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b"));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a b "   "a"      "b "         true
+
+		pSource = EA_CHAR32(" a b ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("b "));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a "     "a"      ""           true
+
+		pSource = EA_CHAR32(" a ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " a  "    "a"      ""           true
+
+		pSource = EA_CHAR32(" a  ");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("a"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    ""        ""       ""           false
+
+		pSource = EA_CHAR32("");
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " "       ""       ""           false
+
+		pSource = EA_CHAR32(" ");
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(""));
+
+
+		///   source    token    new source   return
+		///   ---------------------------------------
+		///    " "       ""       ""           false
+
+		pSource = NULL;
+
+		EATEST_VERIFY(!SplitTokenSeparated(pSource, kLengthNull, ' ', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32(""));
+		EATEST_VERIFY(pSource == NULL);
+
+
+		// various additional tests
+		pSource = EA_CHAR32("testing;;;source;;;length");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, 4, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("test"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("ing;;;source;;;length"));
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, 5, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("ing"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32(";source;;;length"));
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ';', pToken, 8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("source"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("length"));
+
+		pSource = EA_CHAR32(";;;;;;;123 456;;;;;a");
+
+		EATEST_VERIFY(SplitTokenSeparated(pSource, kLengthNull, ';', pToken,8, &pSource));
+		EATEST_VERIFY(String32(pToken) == EA_CHAR32("123 456"));
+		EATEST_VERIFY(String32(pSource) == EA_CHAR32("a"));
+	}
+
+
+	// template<typename String, typename Char>
+	// bool SplitTokenSeparated(String& sSource, Char c, String* pToken);
+	{
+		// To do
+	}
+
+	return nErrorCount;
+}
+
+
+
+
+
+
+
+
+
+
+

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov