1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00
Beef/BeefRT/rt/Internal.cpp

917 lines
21 KiB
C++
Raw Normal View History

2019-08-23 11:56:54 -07:00
#pragma warning(disable:4996)
#define HEAPHOOK
//#define USE_CHARCONV
#include <stdio.h>
//#include <crtdefs.h>
//#include <malloc.h>
2019-08-23 11:56:54 -07:00
#include <stdlib.h>
#include <string.h>
//#include <intrin.h>
#ifdef USE_CHARCONV
#include <charconv>
#endif
//#define OBJECT_GUARD_END_SIZE 8
#define OBJECT_GUARD_END_SIZE 0
//#define BF_USE_STOMP_ALLOC 1
//extern "C"
//{
//#include "gperftools/stacktrace.h"
//}
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_ReturnAddress)
#define BF_RETURN_ADDRESS _ReturnAddress()
#else
#define BF_RETURN_ADDRESS __builtin_return_address(0)
#endif
#include "BeefySysLib/Common.h"
#include "BfObjects.h"
//#include "gc.h"
#include "StompAlloc.h"
#include "BeefySysLib/platform/PlatformHelper.h"
#include "ffi.h"
#include "Thread.h"
#ifdef BF_PLATFORM_WINDOWS
#include <fcntl.h>
#include <io.h>
#endif
USING_NS_BF;
static Beefy::StringT<0> gCmdLineString;
bf::System::Runtime::BfRtCallbacks gBfRtCallbacks;
BfRtFlags gBfRtFlags = (BfRtFlags)0;
namespace bf
{
namespace System
{
class Object;
class Exception;
//System::Threading::Thread* gMainThread;
class Internal
{
private:
BFRT_EXPORT static void __BfStaticCtor();
BFRT_EXPORT static void __BfStaticDtor();
BFRT_EXPORT static void BfStaticCtor();
BFRT_EXPORT static void BfStaticDtor();
BFRT_EXPORT static void Shutdown();
public:
BFRT_EXPORT static Object* UnsafeCastToObject(void* inPtr);
BFRT_EXPORT static void* UnsafeCastToPtr(Object* obj);
BFRT_EXPORT static void ObjectDynCheck(Object* object, int typeId, bool allowNull);
BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId);
BFRT_EXPORT static void Throw(Exception* ex);
BFRT_EXPORT static void ThrowIndexOutOfRange(intptr stackOffset);
BFRT_EXPORT static void FatalError(String* error, intptr stackOffset = 0);
BFRT_EXPORT static void MemCpy(void* dest, void* src, intptr length);
BFRT_EXPORT static void MemMove(void* dest, void* src, intptr length);
BFRT_EXPORT static void MemSet(void* addr, uint8 val, intptr length);
BFRT_EXPORT static int CStrLen(char* charPtr);
BFRT_EXPORT static void* Malloc(intptr length);
BFRT_EXPORT static void Free(void* ptr);
BFRT_EXPORT static void* VirtualAlloc(intptr size, bool canExecute, bool canWrite);
BFRT_EXPORT static int64 GetTickCountMicro();
BFRT_EXPORT static void BfDelegateTargetCheck(void* target);
BFRT_EXPORT static void* LoadSharedLibrary(char* filePath);
BFRT_EXPORT static void LoadSharedLibraryInto(char* filePath, void** libDest);
BFRT_EXPORT static void* GetSharedProcAddress(void* libHandle, char* procName);
BFRT_EXPORT static void GetSharedProcAddressInto(void* libHandle, char* procName, void** procDest);
BFRT_EXPORT static char* GetCommandLineArgs();
BFRT_EXPORT static void BfLog(char* str);
BFRT_EXPORT static void ProfilerCmd(char* str);
BFRT_EXPORT static void ReportMemory();
private:
BFRT_EXPORT static void Test_Init(char* testData);
BFRT_EXPORT static int32 Test_Query();
BFRT_EXPORT static void Test_Finish();
};
namespace IO
{
class File
{
private:
BFRT_EXPORT static bool Exists(char* fileName);
};
class Directory
{
private:
BFRT_EXPORT static bool Exists(char* fileName);
};
}
namespace Diagnostics
{
namespace Contracts
{
class Contract
{
public:
enum ContractFailureKind : uint8
{
ContractFailureKind_Precondition,
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
ContractFailureKind_Postcondition,
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
ContractFailureKind_PostconditionOnException,
ContractFailureKind_Invariant,
ContractFailureKind_Assert,
ContractFailureKind_Assume,
};
private:
BFRT_EXPORT static void ReportFailure(ContractFailureKind failureKind, char* userMessage, int userMessageLen, char* conditionText, int conditionTextLen);
};
}
class Debug
{
private:
BFRT_EXPORT static void Write(char* str, intptr strLen);
};
}
namespace FFI
{
enum FFIABI : int32;
enum FFIResult : int32;
struct FFIType;
struct FFILIB
{
struct FFICIF;
BFRT_EXPORT static void* ClosureAlloc(intptr size, void** outFunc);
BFRT_EXPORT static FFIResult PrepCif(FFICIF* cif, FFIABI abi, int32 nargs, FFIType* rtype, FFIType** argTypes);
BFRT_EXPORT static void Call(FFICIF* cif, void* funcPtr, void* rvalue, void** args);
};
}
struct Float
{
private:
BFRT_EXPORT static int ToString(float f, char* outStr);
};
struct Double
{
private:
BFRT_EXPORT static int ToString(double f, char* outStr);
};
}
}
//#define BF_TRACK_SIZES 1
#if BF_TRACK_SIZES
static int sAllocSizes[1024*1024];
static int sHighestId = 0;
#endif
using namespace bf::System;
#ifndef BF_PLATFORM_WINDOWS
bool IsDebuggerPresent()
{
return false;
}
#endif
extern "C" BFRT_EXPORT int BF_CALLTYPE ftoa(float val, char* str)
{
return sprintf(str, "%1.9f", val);
}
/*static void* MallocHook(size_t size, const void *caller)
{
printf("MallocHook\n");
return NULL;
}*/
/*static int __cdecl HeapHook(int a, size_t b, void* c, void** d)
{
printf("Heap Hook\n");
return 0;
}*/
//////////////////////////////////////////////////////////////////////////
//static Beefy::StringT<0> gErrorString;
static const char* volatile gErrorString = NULL;
void SetErrorString(const char* str)
{
char* newStr = strdup(str);
while (true)
{
const char* prevStr = gErrorString;
auto result = ::InterlockedCompareExchangePointer((void* volatile*)&gErrorString, (void*)newStr, (void*)prevStr);
if (result != prevStr)
continue;
if (prevStr != NULL)
free((void*)prevStr);
break;
}
}
#define SETUP_ERROR(str, skip) SetErrorString(str); gBfRtCallbacks.DebugMessageData_SetupError(str, skip)
static void GetCrashInfo()
{
auto errorString = gErrorString;
if (errorString != NULL)
{
Beefy::String debugStr;
debugStr += "Beef Error: ";
debugStr += (const char*)errorString;
BfpSystem_AddCrashInfo(debugStr.c_str());
}
}
void bf::System::Runtime::Init(int version, int flags, BfRtCallbacks* callbacks)
{
BfpSystemInitFlags sysInitFlags = BfpSystemInitFlag_InstallCrashCatcher;
if ((flags & 4) != 0)
sysInitFlags = (BfpSystemInitFlags)(sysInitFlags | BfpSystemInitFlag_SilentCrash);
BfpSystem_Init(BFP_VERSION, sysInitFlags);
BfpSystem_AddCrashInfoFunc(GetCrashInfo);
if (gBfRtCallbacks.Alloc != NULL)
{
BfpSystem_FatalError(StrFormat("BeefRT already initialized. Multiple executable modules in the same process cannot dynamically link to the Beef runtime.").c_str(), "BEEF FATAL ERROR");
}
2019-08-23 11:56:54 -07:00
if (version != BFRT_VERSION)
{
BfpSystem_FatalError(StrFormat("BeefRT build version '%d' does not match requested version '%d'", BFRT_VERSION, version).c_str(), "BEEF FATAL ERROR");
}
gBfRtCallbacks = *callbacks;
gBfRtFlags = (BfRtFlags)flags;
Beefy::String cmdLine;
BfpSystemResult result;
BFP_GETSTR_HELPER(cmdLine, result, BfpSystem_GetCommandLine(__STR, __STRLEN, &result));
char* cmdLineStr = (char*)cmdLine.c_str();
//::MessageBoxA(NULL, cmdLineStr, "BFRT", 0);
char* useCmdLineStr = cmdLineStr;
if (cmdLineStr[0] != 0)
{
bool nameQuoted = cmdLineStr[0] == '\"';
Beefy::String passedName;
int i;
for (i = (nameQuoted ? 1 : 0); cmdLineStr[i] != 0; i++)
{
wchar_t c = cmdLineStr[i];
if (((nameQuoted) && (c == '"')) ||
((!nameQuoted) && (c == ' ')))
{
i++;
break;
}
passedName += cmdLineStr[i];
}
useCmdLineStr += i;
while (*useCmdLineStr == L' ')
useCmdLineStr++;
}
gCmdLineString = useCmdLineStr;
}
void bf::System::Runtime::SetErrorString(char* errorStr)
{
::SetErrorString(errorStr);
}
void bf::System::Runtime::AddCrashInfoFunc(void* func)
{
BfpSystem_AddCrashInfoFunc(*(BfpCrashInfoFunc*)&func);
}
void bf::System::Runtime::SetCrashReportKind(bf::System::Runtime::RtCrashReportKind crashReportKind)
{
BfpSystem_SetCrashReportKind((BfpCrashReportKind)crashReportKind);
}
//////////////////////////////////////////////////////////////////////////
void Internal::Shutdown()
{
BfInternalThread::WaitForAllDone();
if (gBfRtCallbacks.GC_Shutdown != NULL)
gBfRtCallbacks.GC_Shutdown();
BfpSystem_Shutdown();
}
void Internal::BfStaticCtor()
{
__BfStaticCtor();
}
void Internal::__BfStaticCtor()
{
}
void Internal::BfStaticDtor()
{
}
void Internal::__BfStaticDtor()
{
}
Object* Internal::UnsafeCastToObject(void* inPtr)
{
return (Object*)inPtr;
}
void* Internal::UnsafeCastToPtr(Object* obj)
{
return (void*)obj;
}
void Internal::Throw(Exception* ex)
{
bf::System::String* exStr = gBfRtCallbacks.String_Alloc();
gBfRtCallbacks.Object_ToString(ex, exStr);
Beefy::String errorStr = StrFormat("FATAL: %s", exStr->CStr());
SETUP_ERROR(errorStr.c_str(), 1);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
printf("Thrown: %s", errorStr.c_str());
//TODO: What about capturing callstack?
exit(3);
//throw ex;
}
void Internal::ThrowIndexOutOfRange(intptr stackOffset)
{
if ((stackOffset != -1) && (::IsDebuggerPresent()))
{
SETUP_ERROR("Index out of range", (int)(2 + stackOffset));
BF_DEBUG_BREAK();
}
BfpSystem_FatalError("Index out of range", "FATAL ERROR");
}
void Internal::FatalError(bf::System::String* error, intptr stackOffset)
{
if ((stackOffset != -1) && (::IsDebuggerPresent()))
{
SETUP_ERROR(error->CStr(), (int)(2 + stackOffset));
BF_DEBUG_BREAK();
}
BfpSystem_FatalError(error->CStr(), "FATAL ERROR");
}
void Internal::MemCpy(void* dest, void* src, intptr length)
{
memcpy(dest, src, length);
}
void Internal::MemMove(void* dest, void* src, intptr length)
{
memmove(dest, src, length);
}
void Internal::MemSet(void* addr, uint8 val, intptr length)
{
memset(addr, val, length);
}
int Internal::CStrLen(char* charPtr)
{
return (int)strlen(charPtr);
}
void* Internal::Malloc(intptr length)
{
#if BF_USE_STOMP_ALLOC
return StompAlloc(length);
#elif BF_TRACK_SIZES
uint8* allocPtr = (uint8*)malloc(length + 16);
*((int*)allocPtr) = length;
sAllocSizes[0] += length;
return allocPtr + 16;
#else
return malloc(length);
#endif
}
void* Internal::VirtualAlloc(intptr size, bool canExecute, bool canWrite)
{
#ifdef BF_PLATFORM_WINDOWS
OutputDebugStrF("Performing VirtualAlloc: %d %d %d\n", size, canExecute, canWrite);
int prot = PAGE_READWRITE;
if (canExecute && canWrite)
prot = PAGE_EXECUTE_READWRITE;
else if (canExecute)
prot = PAGE_EXECUTE_READ;
void* ptr = ::VirtualAlloc(NULL, size, MEM_RESERVE, prot);
return ptr;
#else
BF_FATAL("Not supported");
return NULL;
2019-08-23 11:56:54 -07:00
#endif
}
void Internal::Free(void* ptr)
{
#if BF_USE_STOMP_ALLOC
StompFree(ptr);
#elif BF_TRACK_SIZES
uint8* allocPtr = ((uint8*)ptr) - 16;
sAllocSizes[0] -= *((int*)allocPtr);
free(allocPtr);
#else
free(ptr);
#endif
}
BFRT_EXPORT int64 Internal::GetTickCountMicro()
{
return BFGetTickCountMicro();
}
void Internal::BfDelegateTargetCheck(void* target)
{
if (target != NULL)
{
SETUP_ERROR("Attempting pass non-static method reference to extern method", 2);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
}
void* Internal::LoadSharedLibrary(char* libName)
{
//::MessageBox(NULL, "Hey", "Dude", 0);
void* libHandle = BfpDynLib_Load(libName);
if (libHandle == NULL)
{
Beefy::String errorStr = StrFormat("Failed to load shared library: %s", libName);
SETUP_ERROR(errorStr.c_str(), 1);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
return libHandle;
}
void Internal::LoadSharedLibraryInto(char* libName, void** libDest)
{
if (*libDest == NULL)
*libDest = LoadSharedLibrary(libName);
}
void* Internal::GetSharedProcAddress(void* libHandle, char* procName)
{
if (libHandle == NULL)
return NULL;
void* procAddr = BfpDynLib_GetProcAddress((BfpDynLib*)libHandle, procName);
if (procAddr == NULL)
{
char libFileName[4096];
int libFileNameLen = 4096;
BfpDynLib_GetFilePath((BfpDynLib*)libHandle, libFileName, &libFileNameLen, NULL);
Beefy::String errorStr = StrFormat("Failed to load shared procedure '%s' from '%s'", procName, libFileName);
SETUP_ERROR(errorStr.c_str(), 1);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
return procAddr;
}
void Internal::GetSharedProcAddressInto(void* libHandle, char* procName, void** procDest)
{
*procDest = GetSharedProcAddress(libHandle, procName);
}
char* Internal::GetCommandLineArgs()
{
return (char*)gCmdLineString.c_str();
}
void Internal::BfLog(char* str)
{
// static int lineNum = 0;
// lineNum++;
//
// static FILE* fp = fopen("dbg_internal.txt", "wb");
//
// Beefy::String aResult = StrFormat("%d ", lineNum) + str;
// fwrite(aResult.c_str(), 1, aResult.length(), fp);
// fflush(fp);
}
void Internal::ProfilerCmd(char* str)
{
if (!::IsDebuggerPresent())
return;
gBfRtCallbacks.DebugMessageData_SetupProfilerCmd(str);
BF_DEBUG_BREAK();
}
void Internal::ReportMemory()
{
int totalMem = 0;
#if BF_TRACK_SIZES
for (int i = 0; i <= sHighestId; i++)
totalMem += sAllocSizes[i];
OutputDebugStrF("Beef Object Memory: %dk\n", totalMem / 1024);
#endif
}
static int gTestMethodIdx = -1;
static uint32 gTestStartTick = 0;
static BfpFile* gClientPipe = NULL;
static Beefy::String gTestInBuffer;
static void TestString(const StringImpl& str)
{
BfpFileResult fileResult;
BfpFile_Write(gClientPipe, str.c_str(), str.length(), -1, &fileResult);
BF_ASSERT_REL(fileResult == BfpFileResult_Ok);
}
static void TestReadCmd(Beefy::String& str)
{
while (true)
{
int crPos = (int)gTestInBuffer.IndexOf('\n');
if (crPos != -1)
{
str = gTestInBuffer.Substring(0, crPos);
gTestInBuffer.Remove(0, crPos + 1);
return;
}
char data[1024];
BfpFileResult fileResult;
int readSize = (int)BfpFile_Read(gClientPipe, data, 1024, -1, &fileResult);
if ((fileResult == BfpFileResult_Ok) || (fileResult == BfpFileResult_PartialData))
{
gTestInBuffer.Append(data, readSize);
}
else
{
BF_FATAL("Failed to read pipe to test manager");
}
}
}
void Internal::Test_Init(char* testData)
{
BfpSystem_SetCrashReportKind(BfpCrashReportKind_None);
Beefy::String args = GetCommandLineArgs();
BfpFileResult fileResult;
gClientPipe = BfpFile_Create(args.c_str(), BfpFileCreateKind_OpenExisting, (BfpFileCreateFlags)(BfpFileCreateFlag_Read | BfpFileCreateFlag_Write | BfpFileCreateFlag_Pipe), BfpFileAttribute_None, &fileResult);
if (fileResult != BfpFileResult_Ok)
BF_FATAL("Test_Init failed to create pipe to test manager");
Beefy::String outStr;
outStr += ":TestInit\n";
outStr += testData;
outStr += "\n";
outStr += ":TestBegin\n";
TestString(outStr);
}
int32 Internal::Test_Query()
{
if (gTestMethodIdx != -1)
{
uint32 tickEnd = BfpSystem_TickCount();
TestString(StrFormat(":TestResult\t%d\n", tickEnd - gTestStartTick));
}
TestString(":TestQuery\n");
Beefy::String result;
TestReadCmd(result);
Beefy::String param;
int tabPos = (int)result.IndexOf('\t');
if (tabPos != -1)
{
param = result.Substring(tabPos + 1);
result.RemoveToEnd(tabPos);
}
if (result == ":TestRun")
{
gTestStartTick = BfpSystem_TickCount();
gTestMethodIdx = atoi(param.c_str());
return gTestMethodIdx;
}
else if (result == ":TestFinish")
{
return -1;
}
else
{
printf("Command Str: %s\n", result.c_str());
BF_FATAL("Invalid test command string from test manager");
}
return false;
}
void Internal::Test_Finish()
{
TestString(":TestFinish\n");
if (gClientPipe != NULL)
{
BfpFile_Release(gClientPipe);
gClientPipe = NULL;
}
}
///
int GetStackTrace(void **result, int max_depth, int skip_count);
void BfLog(const char* fmt ...);
static const int cMaxStackTraceCount = 1024;
struct PendingAllocState
{
bool mHasData;
void* mStackTrace[cMaxStackTraceCount];
int mStackTraceCount;
int mMetadataBytes;
bool IsSmall(intptr curAllocBytes)
{
if ((mStackTraceCount > 255) || (mMetadataBytes > 255))
return false;
const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
if (curAllocBytes <= maxSmallObjectSize)
return true;
intptr objBytes = curAllocBytes - mStackTraceCount*sizeof(intptr) - mMetadataBytes;
return (objBytes < maxSmallObjectSize);
}
};
void Internal::ObjectDynCheck(bf::System::Object* object, int typeId, bool allowNull)
{
return;
if (object == NULL)
{
if (allowNull)
return;
SETUP_ERROR("Attempting unboxing on null object", 1);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
return;
}
auto result = gBfRtCallbacks.Object_DynamicCastToTypeId(object, typeId);
if (result == NULL)
{
Beefy::String errorStr = "Attempting invalid cast on object";
//errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, object->GetTypeName().c_str(), object);
errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, "System.Object", object);
SETUP_ERROR(errorStr.c_str(), 2);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
}
void Internal::ObjectDynCheckFailed(bf::System::Object* object, int typeId)
{
if (object == NULL)
{
SETUP_ERROR("Attempting unboxing on null object", 1);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
return;
}
Beefy::String errorStr = "Attempting invalid cast on object";
errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, "System.Object", object);
SETUP_ERROR(errorStr.c_str(), 2);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
extern "C" BFRT_EXPORT int PrintF(const char* fmt, ...)
{
int ret;
/* Declare a va_list type variable */
va_list myargs;
/* Initialise the va_list variable with the ... after fmt */
va_start(myargs, fmt);
/* Forward the '...' to vprintf */
ret = vprintf(fmt, myargs);
/* Clean up the va_list */
va_end(myargs);
return ret;
}
///
using namespace bf::System::Diagnostics::Contracts;
void Contract::ReportFailure(Contract::ContractFailureKind failureKind, char* userMessage, int userMessageLen, char* conditionText, int conditionTextLen)
{
Beefy::String userMessageStr;
if (userMessageLen > 0)
userMessageStr.Reference(userMessage, userMessageLen);
Beefy::String conditionTextStr;
if (conditionTextLen > 0)
conditionTextStr.Reference(conditionText, conditionTextLen);
Beefy::String errorMsg = "Contract";
if (failureKind == Contract::ContractFailureKind_Assert)
errorMsg += ": Assert failed";
if (userMessage != NULL)
errorMsg += Beefy::String(": ") + userMessageStr;
if (conditionText != NULL)
errorMsg += Beefy::String(": ") + conditionTextStr;
if (::IsDebuggerPresent())
{
SETUP_ERROR(errorMsg.c_str(), 3);
BF_DEBUG_BREAK();
gBfRtCallbacks.DebugMessageData_Fatal();
}
BfpSystem_FatalError(errorMsg.c_str(), "CONTRACT ERROR");
return;
}
void bf::System::Diagnostics::Debug::Write(char* str, intptr strLen)
{
Beefy::String strVal(str, strLen);
OutputDebugStr(strVal);
}
//////////////////////////////////////////////////////////////////////////
bool IO::File::Exists(char* fileName)
{
return BfpFile_Exists(fileName);
}
bool IO::Directory::Exists(char* fileName)
{
return BfpDirectory_Exists(fileName);
}
//////////////////////////////////////////////////////////////////////////
void* bf::System::FFI::FFILIB::ClosureAlloc(intptr size, void** outFunc)
{
return ffi_closure_alloc(size, outFunc);
}
bf::System::FFI::FFIResult bf::System::FFI::FFILIB::PrepCif(bf::System::FFI::FFILIB::FFICIF* cif, bf::System::FFI::FFIABI abi, int32 nargs, bf::System::FFI::FFIType* rtype, bf::System::FFI::FFIType** argTypes)
{
return (bf::System::FFI::FFIResult)ffi_prep_cif((ffi_cif*)cif, (ffi_abi)abi, nargs, (ffi_type*)rtype, (ffi_type**)argTypes);
}
void bf::System::FFI::FFILIB::Call(bf::System::FFI::FFILIB::FFICIF* cif, void* funcPtr, void* rvalue, void** args)
{
ffi_call((ffi_cif*)cif, (void(*)())funcPtr, rvalue, args);
}
//////////////////////////////////////////////////////////////////////////
static int ToString(float d, char* outStr)
{
sprintf(outStr, "%1.9g", d);
int len = (int)strlen(outStr);
for (int i = 0; outStr[i] != 0; i++)
{
if (outStr[i] == '.')
{
int checkC = len - 1;
while (true)
{
char c = outStr[checkC];
if (c == '.')
{
return checkC;
}
else if (c != '0')
{
for (int j = i + 1; j <= checkC; j++)
if (outStr[j] == 'e')
return len;
return checkC + 1;
}
checkC--;
}
}
}
return len;
}
static int ToString(double d, char* outStr)
{
sprintf(outStr, "%1.17g", d);
int len = (int)strlen(outStr);
for (int i = 0; outStr[i] != 0; i++)
{
if (outStr[i] == '.')
{
int checkC = len - 1;
while (true)
{
char c = outStr[checkC];
if (c == '.')
{
return checkC;
}
else if (c == 'e')
{
return len;
}
else if (c != '0')
{
for (int j = i + 1; j <= checkC; j++)
if (outStr[j] == 'e')
return len;
return checkC + 1;
}
checkC--;
}
}
}
return len;
}
int Float::ToString(float f, char* outStr)
{
#ifdef USE_CHARCONV
auto result = std::to_chars(outStr, outStr + 256, f);
return (int)(result.ptr - outStr);
#else
return ::ToString(f, outStr);
#endif
}
int Double::ToString(double d, char* outStr)
{
#ifdef USE_CHARCONV
auto result = std::to_chars(outStr, outStr + 256, d);
return (int)(result.ptr - outStr);
#else
return ::ToString(d, outStr);
#endif
}