#include #include "../../c++stl/string-impl.hpp" #include "internalString.hpp" #if defined(GEODE_IS_ANDROID32) static auto constexpr NEW_SYM = "_Znwj"; #elif defined(GEODE_IS_ANDROID64) static auto constexpr NEW_SYM = "_Znwm"; #endif static auto constexpr DELETE_SYM = "_ZdlPv"; static void* getLibHandle() { static void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD); return handle; } namespace geode::base { uintptr_t get() { static std::uintptr_t basePtr = 0u; if (basePtr == 0u) { auto handle = getLibHandle(); // JNI_OnLoad is present on all versions of GD auto sym = dlsym(handle, "JNI_OnLoad"); assert(sym != nullptr); Dl_info p; auto dlAddrRes = dladdr(sym, &p); assert(dlAddrRes != 0); basePtr = reinterpret_cast(p.dli_fbase); } return basePtr; } } void* gd::operatorNew(size_t size) { static auto fnPtr = reinterpret_cast(dlsym(getLibHandle(), NEW_SYM)); return fnPtr(size); } void gd::operatorDelete(void* ptr) { static auto fnPtr = reinterpret_cast(dlsym(getLibHandle(), DELETE_SYM)); return fnPtr(ptr); } using namespace geode::stl; void* g_ourInternalString = nullptr; static auto& emptyInternalString() { static StringData::Internal* ptr = [] { StringData::Internal internal; internal.m_size = 0; internal.m_capacity = 0; // make our empty internal string different from gd's internal.m_refcount = 1'000'000'000; // use char* so we can do easy pointer arithmetic with it auto* buffer = static_cast(gd::operatorNew(sizeof(internal) + 1)); std::memcpy(buffer, &internal, sizeof(internal)); buffer[sizeof(internal)] = 0; g_ourInternalString = reinterpret_cast(buffer); return reinterpret_cast(buffer + sizeof(internal)); }(); return ptr; } void setEmptyInternalString(gd::string* str) { auto* internal = *reinterpret_cast(str); // make sure its empty if (internal[-1].m_size == 0 && internal[-1].m_capacity == 0 && internal[-1].m_refcount == 0) { emptyInternalString() = internal; g_ourInternalString = nullptr; // leak our internal string because we dont know if someone still has a pointer to it // its only like 20 bytes so who cares anyways } } namespace geode::stl { void StringImpl::setEmpty() { data.m_data = emptyInternalString(); } void StringImpl::free() { if (data.m_data == nullptr || data.m_data == emptyInternalString()) return; if (data.m_data[-1].m_refcount <= 0) { gd::operatorDelete(&data.m_data[-1]); data.m_data = nullptr; } else { --data.m_data[-1].m_refcount; } } char* StringImpl::getStorage() { return reinterpret_cast(data.m_data); } // TODO: add a copyFrom(string const&) to take advantage // of gnustl refcounted strings void StringImpl::setStorage(const std::string_view str) { this->free(); if (str.size() == 0) { this->setEmpty(); return; } StringData::Internal internal; internal.m_size = str.size(); internal.m_capacity = str.size(); internal.m_refcount = 0; // use char* so we can do easy pointer arithmetic with it auto* buffer = static_cast(gd::operatorNew(str.size() + 1 + sizeof(internal))); std::memcpy(buffer, &internal, sizeof(internal)); std::memcpy(buffer + sizeof(internal), str.data(), str.size()); data.m_data = reinterpret_cast(buffer + sizeof(internal)); this->getStorage()[str.size()] = 0; } size_t StringImpl::getSize() { return data.m_data[-1].m_size; } void StringImpl::setSize(size_t size) { // TODO: implement this, remember its copy-on-write... } size_t StringImpl::getCapacity() { return data.m_data[-1].m_capacity; } void StringImpl::setCapacity(size_t cap) { // TODO: implement this, remember its copy-on-write... } }