2025-07-03 12:53:49 +04:00

192 lines
5.4 KiB
C++

#include "il2cpp-config.h"
#include "gc/Allocator.h"
#include "gc/GarbageCollector.h"
#include "gc/GCHandle.h"
#include "os/Atomic.h"
#include "os/Mutex.h"
#include "vm/Exception.h"
#include "vm/String.h"
#include "vm/Object.h"
#include "vm/Profiler.h"
#include "gc/AppendOnlyGCHashMap.h"
#include "utils/StringUtils.h"
#include <string>
#include <memory.h>
#include "il2cpp-class-internals.h"
#include "il2cpp-object-internals.h"
#include "Baselib.h"
#include "Cpp/ReentrantLock.h"
namespace il2cpp
{
namespace vm
{
static Il2CppString* s_EmptyString;
void String::InitializeEmptyString(Il2CppClass* stringClass)
{
IL2CPP_ASSERT(s_EmptyString == NULL && "Empty string was already initialized");
// size for string and null terminator
s_EmptyString = static_cast<Il2CppString*>(gc::GarbageCollector::AllocateFixed(sizeof(Il2CppString) + 2, 0));
s_EmptyString->object.klass = stringClass;
s_EmptyString->length = 0;
s_EmptyString->chars[0] = 0;
}
void String::CleanupEmptyString()
{
IL2CPP_ASSERT(s_EmptyString && "Empty string was not yet initialized");
gc::GarbageCollector::FreeFixed(s_EmptyString);
s_EmptyString = NULL;
}
Il2CppString* String::Empty()
{
IL2CPP_ASSERT(s_EmptyString && "Empty string was not yet initialized");
return s_EmptyString;
}
int32_t String::GetHash(Il2CppString* str)
{
const Il2CppChar* p = utils::StringUtils::GetChars(str);
int i, len = utils::StringUtils::GetLength(str);
uint32_t h = 0;
for (i = 0; i < len; i++)
{
h = (h << 5) - h + *p;
p++;
}
return h;
}
Il2CppString* String::New(const char* str)
{
return NewLen(str, (uint32_t)strlen(str));
}
Il2CppString* String::NewWrapper(const char* str)
{
return New(str);
}
Il2CppString* String::NewLen(const char* str, uint32_t length)
{
UTF16String utf16Chars = il2cpp::utils::StringUtils::Utf8ToUtf16(str, length);
return NewUtf16(utf16Chars.c_str(), (uint32_t)utf16Chars.length());
}
Il2CppString* String::NewUtf16(const Il2CppChar* text, int32_t len)
{
Il2CppString *s;
s = NewSize(len);
IL2CPP_ASSERT(s != NULL);
memcpy(utils::StringUtils::GetChars(s), text, (size_t)len * 2);
return s;
}
Il2CppString* String::NewUtf16(const utils::StringView<Il2CppChar>& text)
{
IL2CPP_ASSERT(text.Length() < static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
return NewUtf16(text.Str(), static_cast<int32_t>(text.Length()));
}
Il2CppString* String::NewSize(int32_t len)
{
if (len == 0)
return Empty();
Il2CppString *s;
IL2CPP_ASSERT(len >= 0);
size_t size = (sizeof(Il2CppString) + (((size_t)len + 1) * 2));
/* overflow ? can't fit it, can't allocate it! */
if (static_cast<uint32_t>(len) > size)
Exception::RaiseOutOfMemoryException();
s = reinterpret_cast<Il2CppString*>(Object::AllocatePtrFree(size, il2cpp_defaults.string_class));
s->length = len;
#if NEED_TO_ZERO_PTRFREE
s->chars[len] = 0;
#endif
#if IL2CPP_ENABLE_PROFILER
if (Profiler::ProfileAllocations())
Profiler::Allocation((Il2CppObject*)s, il2cpp_defaults.string_class);
#endif
return s;
}
struct InternedString
{
int32_t length;
const Il2CppChar* chars;
};
class InternedStringHash
{
public:
size_t operator()(const InternedString& ea) const
{
return utils::StringUtils::Hash(ea.chars, ea.length);
}
};
class InternedStringCompare
{
public:
bool operator()(const InternedString& ea, const InternedString& eb) const
{
return (ea.length == eb.length) && (0 == memcmp(ea.chars, eb.chars, sizeof(Il2CppChar) * ea.length));
}
};
typedef il2cpp::gc::AppendOnlyGCHashMap<InternedString, Il2CppString*, InternedStringHash, InternedStringCompare> InternedStringMap;
static InternedStringMap* s_InternedStringMap;
Il2CppString* String::Intern(Il2CppString* str)
{
// allocate this at runtime since it uses GC allocator to keep managed strings alive and needs GC initialized
if (s_InternedStringMap == NULL)
{
InternedStringMap* newMap = new InternedStringMap();
if (os::Atomic::CompareExchangePointer<InternedStringMap>(&s_InternedStringMap, newMap, NULL) != NULL)
delete newMap;
}
InternedString internedString = { str->length, str->chars };
Il2CppString* value = NULL;
if (s_InternedStringMap->TryGetValue(internedString, &value))
return value;
internedString.chars = utils::StringUtils::GetChars(str);
return s_InternedStringMap->GetOrAdd(internedString, str);
}
Il2CppString* String::IsInterned(Il2CppString* str)
{
// if this is NULL, it means we have no interned strings
if (s_InternedStringMap == NULL)
return NULL;
InternedString internedString = { str->length, str->chars };
Il2CppString* value = NULL;
if (s_InternedStringMap->TryGetValue(internedString, &value))
return value;
return NULL;
}
} /* namespace vm */
} /* namespace il2cpp */