/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuroraUtils.hpp Date: 2021-6-9 Author: Reece ***/ #pragma once #if !defined(AURORA_RUNTIME_MAKE_SHARED) #define AURORA_RUNTIME_MAKE_SHARED std::make_shared #endif template static inline AuSPtr AuMakeShared(Args&&... args) { try { return AURORA_RUNTIME_MAKE_SHARED(std::forward(args)...); } catch (...) { return {}; } } #if !defined(AURORA_RUNTIME_MAKE_PAIR) #define AURORA_RUNTIME_MAKE_PAIR std::make_pair #endif template static inline auto AuMakePair(Args&&... args) { return AURORA_RUNTIME_MAKE_PAIR(std::forward(args)...); } #if defined(AURORA_IS_MODERNNT_DERIVED) && (defined(_WINDOWS_) || defined(_OTHER_MS_MAIN_HEADER_GUARDS_HERE)) static inline void AuWin32CloseHandle(HANDLE &handle) { HANDLE local; if ((local = std::exchange(handle, INVALID_HANDLE_VALUE)) != INVALID_HANDLE_VALUE) { CloseHandle(local); } } #endif template static AuSPtr AuUnsafeRaiiToShared(T *in) { return AuSPtr(in, [](T *){}); } template static AuSPtr AuUnsafeRaiiToShared(const AuUPtr &in) { return AuSPtr(in.get(), [](T *){}); } template static constexpr int AuArraySize(const T(&array)[Z]) { return Z; } #if defined(DEBUG) || defined(STAGING) template static inline void __declspec(noreturn) SysPanic(T... args); template static void inline SafeDelete(T *in) { static_assert(std::is_base_of::type>::value, "Couldn't not safe delete from type T because it is not derived from Z"); auto cast = dynamic_cast(in); if (cast == nullptr) { Z re; SysPanic("Tried to free: 0x{:x}, type \"{}\" was not inherited from \"{}\"", AuUInt(in), typeid(in).name(), typeid(re).name()); } delete cast; } #else template static void inline SafeDelete(T *in) { static_assert(std::is_base_of::type>::value, "Couldn't not safe delete from type T because it is not derived from Z"); delete static_cast(in); } #endif static constexpr inline AuUInt32 AuConvertMagicTag32(const char buffer[4]) { AuUInt32 magic {}; if (Aurora::Build::kCurrentEndian == Aurora::Build::ECPUEndian::eCPULittle) { magic |= AuUInt32(buffer[0]); magic |= AuUInt32(buffer[1]) << 8; magic |= AuUInt32(buffer[2]) << 16; magic |= AuUInt32(buffer[3]) << 24; // LE will look alright in memory dumps // MSFT uses tags that read back the initial string value when read back hex ints // I prefer binary streams and file headers contain a 4x or 8x ascii char headers (eg. LuaX) } else { // Lazy reinterpret cast reads will always be flipped // Assume byte buffers read/write machine endian // Assume *reinterpret_cast returns machine endian // BE needs to be flipped in memory // BE will look fine in memory dumps // BE will also look fine in stack/variable dumps when printed in hex magic |= AuUInt32(buffer[4]); magic |= AuUInt32(buffer[2]) << 8; magic |= AuUInt32(buffer[1]) << 16; magic |= AuUInt32(buffer[0]) << 24; } // Determinstic across platforms, perhaps unexpected by endian normalized streams // When asserting read(noEndian) against a tag, an endian swap would cause the // assertion to fail, thus providing you with the endian match check // This step is delegated to a de-facto machine endian buffer builder // ByteBuffers that normalize for endianness continue to work with tags // irrespective of reader/writer endianness return magic; } static constexpr inline AuUInt64 AuConvertMagicTag64(const char buffer[8]) { AuUInt64 magic {}; if (Aurora::Build::kCurrentEndian == Aurora::Build::ECPUEndian::eCPULittle) { magic |= AuUInt64(buffer[0]); magic |= AuUInt64(buffer[1]) << 8; magic |= AuUInt64(buffer[2]) << 16; magic |= AuUInt64(buffer[3]) << 24; magic |= AuUInt64(buffer[4]) << 32; magic |= AuUInt64(buffer[5]) << 40; magic |= AuUInt64(buffer[6]) << 48; magic |= AuUInt64(buffer[7]) << 56; } else { magic |= AuUInt64(buffer[7]); magic |= AuUInt64(buffer[6]) << 8; magic |= AuUInt64(buffer[5]) << 16; magic |= AuUInt64(buffer[4]) << 24; magic |= AuUInt64(buffer[3]) << 32; magic |= AuUInt64(buffer[2]) << 40; magic |= AuUInt64(buffer[1]) << 48; magic |= AuUInt64(buffer[0]) << 56; } return magic; } template static inline AuOptional> AuOptionalSharedDynamicCast(AuOptional> &in) { if (!in.has_value()) return {}; return std::dynamic_pointer_cast(in.value()); } template static inline AuOptional> AuOptionalSharedStaticCast(AuOptional> &in) { if (!in.has_value()) return {}; return std::static_pointer_cast(in.value()); } static inline bool AuEndsWith(AuString const &value, AuString const &ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } static inline bool AuStartsWith(AuString const &value, AuString const &starting) { return value.rfind(starting, 0) == 0; } template static inline AuString AuToStringASCIIOp(T op, const AuString &in) { AuString ret; ret.resize(in.size()); std::transform(in.begin(), in.end(), ret.begin(), [=](const char &c) { return op(c); }); return ret; } static inline AuString AuToLower(const AuString &in) { return AuToStringASCIIOp(std::tolower, in); } static inline AuString AuToUpper(const AuString &in) { return AuToStringASCIIOp(std::toupper, in); } template static inline bool AuTryFind(Map &map, const Key &key, Value *&ptr) { auto itr = map.find(key); if (itr != map.end()) { ptr = &itr->second; return true; } else { ptr = nullptr; return false; } } template static inline bool AuTryFind(Map *map, const Key &key, Value *&ptr) { auto itr = map->find(key); if (itr != map->end()) { ptr = &itr->second; return true; } else { ptr = nullptr; return false; } } template static inline bool AuTryFind(Map &map, const Key &key) { auto itr = map.find(key); if (itr != map.end()) { return true; } else { return false; } } template static inline bool AuExists(Range &a, const Key &item) { return std::find(a.begin(), a.end(), item) != a.end(); } template static inline bool AuTryFind(Map *map, const Key &key) { auto itr = map->find(key); if (itr != map->end()) { return true; } else { return false; } } template static inline bool AuTryFindGeneric(Map &map, const Key &key, Value *&ptr) { auto itr = map.find(key); if (itr != map.end()) { ptr = &*itr; return true; } else { ptr = nullptr; return false; } } template static inline bool AuTryDelete(Map &map, const Key &key) { auto itr = map.find(key); if (itr != map.end()) { map.erase(itr); return true; } else { return false; } } template static inline bool AuTryDeleteList(List &list, const Key &key) { auto itr = std::find(list.begin(), list.end(), key); if (itr != list.end()) { list.erase(itr); return true; } else { return false; } } template static inline bool AuTryInsert(Container &container, const Type &value) { try { container.insert(container.end(), value); return true; } catch (...) { return false; } } template static inline bool AuTryInsert(Container &container, Type &&value) { try { container.insert(container.end(), value); return true; } catch (...) { return false; } } template static inline bool AuTryInsert(Container *container, const Type &value) { try { container->insert(container->end(), value); return true; } catch (...) { return false; } } template static inline bool AuTryInsert(Container *container, Type &&value) { try { container->insert(container->end(), value); return true; } catch (...) { return false; } } template static inline bool AuTryInsertNoEnd(Container &container, Type &&value) // move { try { container.insert(value); return true; } catch (...) { return false; } } template static inline bool AuTryInsertNoEnd(Container &container, const Type &value) // copy { try { container.insert(value); return true; } catch (...) { return false; } } template static inline bool AuTryInsertNoEnd(Container *container, Type &&value) // move { try { container->insert(value); return true; } catch (...) { return false; } } template static inline bool AuTryInsertNoEnd(Container *container, const Type &value) // copy { try { container->insert(value); return true; } catch (...) { return false; } } namespace Aurora::Memory { struct ByteBuffer; } template static inline bool AuTryResize(T &list, AuUInt length) { try { if constexpr (std::is_same_v) { return list.Resize(length); } else { list.resize(length); return true; } } catch (...) { return false; } } template static inline bool AuTryDownsize(T &list, AuUInt length) { try { if constexpr (std::is_same_v) { return list.Resize(length); } else { list.resize(length); list.shrink_to_fit(); return true; } } catch (...) { return false; } } static inline AuString AuReplaceAll(AuString &str, const AuString &from, const AuString &to) { size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // Handles case where 'to' is a substring of 'from' } return str; // :( } // i told myself not to copy this, required a split function twice, now here we are :D static inline AuList AuSplitString(const AuString& str, const AuString& delim, bool ignoreEmpty = true) { AuList tokens; AuUInt prev = 0, pos = 0; do { pos = str.find(delim, prev); if (pos == AuString::npos) pos = str.length(); auto token = str.substr(prev, pos-prev); if ((!token.empty()) && ignoreEmpty) tokens.push_back(token); prev = pos + delim.length(); } while (pos < str.length() && prev < str.length()); return tokens; } // more copy/pasta. work smart, not hard. // i dont want to waste time working out template kinks between clang and msvc template class base,typename derived> struct is_base_of_template_impl_au { template static constexpr std::true_type test(const base *); static constexpr std::false_type test(...); using type = decltype(test(std::declval())); }; template < template class base,typename derived> using AuIsBaseOfTemplate = typename is_base_of_template_impl_au::type; template static inline bool AuTestBit(T value, AuUInt8 idx) { return value & (T(1) << T(idx)); } template static inline void AuSetBit(T &value, AuUInt8 idx) { value |= T(1) << T(idx); } template static inline void AuClearBit(T &value, AuUInt8 idx) { value &= ~(T(1) << T(idx)); } #if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_ARM) #define AURORA_PERMIT_ARBITRARY_REF #endif template static inline T AuReadGenericLE(const void *ptr, int offset) { #if defined(AURORA_PERMIT_ARBITRARY_REF) && defined(AU_CPU_ENDIAN_LITTLE) return *reinterpret_cast(reinterpret_cast(ptr) + offset); #else T temp; std::memcpy(&temp, reinterpret_cast(ptr) + offset, sizeof(temp)); #if !defined(AU_CPU_ENDIAN_LITTLE) #if defined(AURORA_COMPILER_MSVC) if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ulong(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_uint64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ushort(temp); } #else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap32(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = (temp << 8) | ((temp >> 8) & 0xFF); } #endif #endif return temp; #endif } template static inline T AuReadGenericBE(const void *ptr, int offset) { #if defined(AURORA_PERMIT_ARBITRARY_REF) && defined(AU_CPU_ENDIAN_BIG) return *reinterpret_cast(reinterpret_cast(ptr) + offset); #else T temp; std::memcpy(&temp, reinterpret_cast(ptr) + offset, sizeof(temp)); #if defined(AU_CPU_ENDIAN_LITTLE) #if defined(AURORA_COMPILER_MSVC) if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ulong(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_uint64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ushort(temp); } #else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap32(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = (temp << 8) | ((temp >> 8) & 0xFF); } #endif #endif return temp; #endif } static inline AuUInt64 AuReadU64LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuUInt32 AuReadU32LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuUInt16 AuReadU16LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuUInt8 AuReadU8LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuInt64 AuReadS64LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuInt32 AuReadS32LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuInt16 AuReadS16LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuInt8 AuReadS8LE(const void *ptr, int offset) { return AuReadGenericLE(ptr, offset); } static inline AuUInt64 AuReadU64BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuUInt32 AuReadU32BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuUInt16 AuReadU16BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuUInt8 AuReadU8BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuInt64 AuReadS64BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuInt32 AuReadS32BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuInt16 AuReadS16BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuInt8 AuReadS8BE(const void *ptr, int offset) { return AuReadGenericBE(ptr, offset); } static inline AuUInt64 AuReadU64(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadU64LE(ptr, offset); #else return AuReadU64BE(ptr, offset); #endif } static inline AuUInt32 AuReadU32(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadU32LE(ptr, offset); #else return AuReadU32BE(ptr, offset); #endif } static inline AuUInt16 AuReadU16(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadU16LE(ptr, offset); #else return AuReadU16BE(ptr, offset); #endif } static inline AuUInt8 AuReadU8(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadU8LE(ptr, offset); #else return AuReadU8BE(ptr, offset); #endif } static inline AuInt64 AuReadS64(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadS64LE(ptr, offset); #else return AuReadS64BE(ptr, offset); #endif } static inline AuInt32 AuReadS32(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadS32LE(ptr, offset); #else return AuReadS32BE(ptr, offset); #endif } static inline AuInt16 AuReadS16(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadS16LE(ptr, offset); #else return AuReadS16BE(ptr, offset); #endif } static inline AuInt8 AuReadS8(const void *ptr, int offset) { #if defined(AU_CPU_ENDIAN_LITTLE) return AuReadS8LE(ptr, offset); #else return AuReadS8BE(ptr, offset); #endif } template static inline void AuWriteGenericLE(void *ptr, int offset, T value) { #if defined(AURORA_PERMIT_ARBITRARY_REF) && defined(AU_CPU_ENDIAN_LITTLE) *reinterpret_cast(reinterpret_cast(ptr) + offset) = value; #else T temp = value; #if !defined(AU_CPU_ENDIAN_LITTLE) #if defined(AURORA_COMPILER_MSVC) if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ulong(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_uint64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ushort(temp); } #else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap32(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = (temp << 8) | ((temp >> 8) & 0xFF); } #endif #endif std::memcpy(reinterpret_cast(ptr) + offset, &temp, sizeof(temp)); #endif } template static inline void AuWriteGenericBE(void *ptr, T value, int offset) { #if defined(AURORA_PERMIT_ARBITRARY_REF) && defined(AU_CPU_ENDIAN_BIG) *reinterpret_cast(reinterpret_cast(ptr) + offset) = value; #else T temp = value; #if defined(AU_CPU_ENDIAN_LITTLE) #if defined(AURORA_COMPILER_MSVC) if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ulong(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_uint64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = _byteswap_ushort(temp); } #else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap32(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = __builtin_bswap64(temp); } else if constexpr (std::is_same_v || std::is_same_v) { temp = (temp << 8) | ((temp >> 8) & 0xFF); } #endif #endif std::memcpy(reinterpret_cast(ptr) + offset, &temp, sizeof(temp)); #endif } static inline void AuWriteU64LE(void *ptr, int offset, AuUInt64 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteU32LE(void *ptr, int offset, AuUInt32 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteU16LE(void *ptr, int offset, AuUInt16 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteU8LE(void *ptr, int offset, AuUInt8 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteS64LE(void *ptr, int offset, AuInt64 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteS32LE(void *ptr, int offset, AuInt32 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteS16LE(void *ptr, int offset, AuInt16 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteS8LE(void *ptr, int offset, AuInt8 value) { AuWriteGenericLE(ptr, offset, value); } static inline void AuWriteU64BE(void *ptr, int offset, AuUInt64 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteU32BE(void *ptr, int offset, AuUInt32 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteU16BE(void *ptr, int offset, AuUInt16 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteU8BE(void *ptr, int offset, AuUInt8 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteS64BE(void *ptr, int offset, AuInt64 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteS32BE(void *ptr, int offset, AuInt32 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteS16BE(void *ptr, int offset, AuInt16 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteS8BE(void *ptr, int offset, AuInt8 value) { AuWriteGenericBE(ptr, offset, value); } static inline void AuWriteU64(void *ptr, int offset, AuUInt64 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteU64LE(ptr, offset, value); #else AuWriteU64BE(ptr, offset, value); #endif } static inline void AuWriteU32(void *ptr, int offset, AuUInt32 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteU32LE(ptr, offset, value); #else AuWriteU32BE(ptr, offset, value); #endif } static inline void AuWriteU16(void *ptr, int offset, AuUInt16 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteU16LE(ptr, offset, value); #else AuWriteU16BE(ptr, offset, value); #endif } static inline void AuWriteU8(void *ptr, int offset, AuUInt8 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteU8LE(ptr, offset, value); #else AuWriteU8BE(ptr, offset, value); #endif } static inline void AuWriteS64(void *ptr, int offset, AuInt64 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteS64LE(ptr, offset, value); #else AuWriteS64BE(ptr, offset, value); #endif } static inline void AuWriteS32(void *ptr, int offset, AuInt32 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteS32LE(ptr, offset, value); #else AuWriteS32BE(ptr, offset, value); #endif } static inline void AuWriteS16(void *ptr, int offset, AuInt16 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteS16LE(ptr, offset, value); #else AuWriteS16BE(ptr, offset, value); #endif } static inline void AuWriteS8(void *ptr, int offset, AuInt8 value) { #if defined(AU_CPU_ENDIAN_LITTLE) AuWriteS8LE(ptr, offset, value); #else AuWriteS8BE(ptr, offset, value); #endif }