#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 #include #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(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& text) { IL2CPP_ASSERT(text.Length() < static_cast(std::numeric_limits::max())); return NewUtf16(text.Str(), static_cast(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(len) > size) Exception::RaiseOutOfMemoryException(); s = reinterpret_cast(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 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(&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 */