diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp index b0a357d0d..2162fb3e0 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp @@ -18,9 +18,6 @@ namespace ams::fs { - /* Controls whether MakeUniqueBuffer uses a custom buffer wrapper which wraps the size inline. */ - #define AMS_FS_IMPL_MAKE_UNIQUE_BUFFER_WITH_INLINE_SIZE - /* ACCURATE_TO_VERSION: Unknown */ using AllocateFunction = void *(*)(size_t); using DeallocateFunction = void (*)(void *, size_t); @@ -134,144 +131,6 @@ namespace ams::fs { } }; - #if defined(AMS_FS_IMPL_MAKE_UNIQUE_BUFFER_WITH_INLINE_SIZE) - template BufferEntryType> - class BufferWithInlineSize final { - static_assert(sizeof(BufferEntryType) == 1); - NON_COPYABLE(BufferWithInlineSize); - NON_MOVEABLE(BufferWithInlineSize); - private: - template static constexpr inline T EncodedSizeMask = util::IsLittleEndian() ? (static_cast(3u) << (BITSIZEOF(T) - 2)) : static_cast(3u); - - template static constexpr inline T EncodedSize1 = util::IsLittleEndian() ? (static_cast(0u) << (BITSIZEOF(T) - 2)) : static_cast(0u); - template static constexpr inline T EncodedSize2 = util::IsLittleEndian() ? (static_cast(1u) << (BITSIZEOF(T) - 2)) : static_cast(1u); - template static constexpr inline T EncodedSize4 = util::IsLittleEndian() ? (static_cast(2u) << (BITSIZEOF(T) - 2)) : static_cast(2u); - template static constexpr inline T EncodedSize8 = util::IsLittleEndian() ? (static_cast(3u) << (BITSIZEOF(T) - 2)) : static_cast(3u); - - static constexpr inline u64 TestSize1Mask = ~((static_cast(1u) << (BITSIZEOF(u8) - 2)) - static_cast(1)); - static constexpr inline u64 TestSize2Mask = ~((static_cast(1u) << (BITSIZEOF(u16) - 2)) - static_cast(1)); - static constexpr inline u64 TestSize4Mask = ~((static_cast(1u) << (BITSIZEOF(u32) - 2)) - static_cast(1)); - static constexpr inline u64 TestSize8Mask = ~((static_cast(1u) << (BITSIZEOF(u64) - 2)) - static_cast(1)); - - template - static constexpr ALWAYS_INLINE SizeType EncodeSize(SizeType type, size_t size) noexcept { - if constexpr (util::IsLittleEndian()) { - return type | static_cast(size); - } else { - return type | (static_cast(size) << 2); - } - } - - template - static constexpr ALWAYS_INLINE size_t DecodeSize(const SizeType encoded) noexcept { - if constexpr (util::IsLittleEndian()) { - /* Small optimization: 1-byte size has size type field == 0 and no shifting, can return the value directly. */ - if constexpr (sizeof(SizeType) == 1) { - static_assert(EncodedSize1 == 0); - return encoded; - } else { - /* On little endian, we want to mask out the high bits storing the size field. */ - constexpr SizeType DecodedSizeMask = static_cast(~EncodedSizeMask); - - return encoded & DecodedSizeMask; - } - } else { - /* On big endian, we want to shift out the low bits storing the size type field. */ - return encoded >> 2; - } - } - - template - static ALWAYS_INLINE void DeleteBufferImpl(BufferEntryType *buffer) noexcept { - /* Get pointer to start of allocation. */ - SizeType *alloc = reinterpret_cast(buffer) - 1; - - /* Decode the size of the allocation. */ - const size_t alloc_size = sizeof(SizeType) + DecodeSize(*alloc); - - /* Delete the buffer. */ - return ::ams::fs::impl::Deallocate(alloc, alloc_size); - } - - template - static std::unique_ptr MakeBuffer(size_t size) noexcept { - /* Allocate a buffer. */ - SizeType *alloc = static_cast(::ams::fs::impl::Allocate(sizeof(SizeType) + size)); - if (AMS_UNLIKELY(alloc == nullptr)) { - return nullptr; - } - - /* Write the encoded size. */ - if constexpr (util::IsLittleEndian()) { - *alloc = EncodedSizeType | static_cast(size); - } else { - *alloc = EncodedSizeType | (static_cast(size) << 2); - } - - /* Return our buffer. */ - return std::unique_ptr(reinterpret_cast(alloc + 1)); - } - - static void DeleteBuffer(BufferEntryType *buffer) noexcept { - /* Convert to u8 pointer */ - const u8 *buffer_u8 = reinterpret_cast(buffer); - - /* Determine the storage size for the size. */ - const auto size_type = buffer_u8[-1] & EncodedSizeMask; - if (size_type == EncodedSize1) { - return DeleteBufferImpl(buffer); - } else if (size_type == EncodedSize2) { - return DeleteBufferImpl(buffer); - } else if (size_type == EncodedSize4) { - return DeleteBufferImpl(buffer); - } else /* if (size_type == EncodedSize8) */ { - return DeleteBufferImpl(buffer); - } - } - private: - BufferEntryType m_buffer[1]; - private: - ALWAYS_INLINE BufferWithInlineSize() noexcept { /* ... */ } - public: - ALWAYS_INLINE ~BufferWithInlineSize() noexcept { /* ... */ } - public: - ALWAYS_INLINE operator BufferEntryType *() noexcept { - return m_buffer; - } - - ALWAYS_INLINE operator const BufferEntryType *() const noexcept { - return m_buffer; - } - public: - static ALWAYS_INLINE std::unique_ptr Make(size_t size) noexcept { - /* Create based on overhead size. */ - if (!(size & TestSize1Mask)) { - return MakeBuffer>(size); - } else if (!(size & TestSize2Mask)) { - return MakeBuffer>(size); - } else if (!(size & TestSize4Mask)) { - return MakeBuffer>(size); - } else /* if (!(size & TestSize8Mask)) */ { - /* Check pre-condition. */ - AMS_ASSERT(!(size & TestSize8Mask)); - return MakeBuffer>(size); - } - } - public: - static ALWAYS_INLINE void *operator new(size_t) noexcept { AMS_ABORT(AMS_CURRENT_FUNCTION_NAME); } - - static ALWAYS_INLINE void *operator new(size_t size, Newable *placement) noexcept { AMS_ABORT(AMS_CURRENT_FUNCTION_NAME); } - - static ALWAYS_INLINE void operator delete(void *ptr, size_t) noexcept { - /* Delete the buffer. */ - DeleteBuffer(reinterpret_cast(ptr)->m_buffer); - } - - static void *operator new[](size_t size) noexcept = delete; - static void operator delete[](void *ptr, size_t size) noexcept = delete; - }; - #endif - template auto MakeUnique() { /* Check that we're not using MakeUnique unnecessarily. */ @@ -296,15 +155,6 @@ namespace ams::fs { return ReturnType(static_cast(::ams::fs::impl::Allocate(alloc_size)), Deleter(alloc_size)); } - template - auto MakeUniqueBuffer(size_t size) { - #if defined(AMS_FS_IMPL_MAKE_UNIQUE_BUFFER_WITH_INLINE_SIZE) - return BufferWithInlineSize::Make(size); - #else - return ::ams::fs::impl::MakeUnique(size); - #endif - } - } template diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_path.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_path.hpp index a29fdff1d..3505790cb 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_path.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_path.hpp @@ -24,6 +24,7 @@ namespace ams::fs { class DirectoryPathParser; /* ACCURATE_TO_VERSION: 13.4.0.0 */ + /* NOTE: Intentional inaccuracy in custom WriteBuffer class, to save 0x10 bytes (0x28 -> 0x18) over Nintendo's implementation. */ class Path { NON_COPYABLE(Path); NON_MOVEABLE(Path); @@ -32,53 +33,105 @@ namespace ams::fs { static constexpr size_t WriteBufferAlignmentLength = 8; private: friend class DirectoryPathParser; - private: - using WriteBuffer = decltype(::ams::fs::impl::MakeUniqueBuffer(0)); + public: + class WriteBuffer { + NON_COPYABLE(WriteBuffer); + private: + char *m_buffer; + size_t m_length_and_is_normalized; + public: + constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) { /* ... */ } - template T> - static ALWAYS_INLINE char *GetBuffer(T &write_buffer) { - if constexpr (std::same_as(0))>) { - return write_buffer.get(); - } else { - return static_cast(*write_buffer); - } - } + constexpr ~WriteBuffer() { + if (m_buffer != nullptr) { + ::ams::fs::impl::Deallocate(m_buffer, this->GetLength()); + this->ResetBuffer(); + } + } - static ALWAYS_INLINE WriteBuffer MakeWriteBuffer(size_t size) { - return ::ams::fs::impl::MakeUniqueBuffer(size); - } + constexpr WriteBuffer(WriteBuffer &&rhs) : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) { + rhs.ResetBuffer(); + } + + constexpr WriteBuffer &operator=(WriteBuffer &&rhs) { + m_buffer = rhs.m_buffer; + m_length_and_is_normalized = rhs.m_length_and_is_normalized; + + rhs.ResetBuffer(); + + return *this; + } + + constexpr ALWAYS_INLINE void ResetBuffer() { + m_buffer = nullptr; + this->SetLength(0); + } + + constexpr ALWAYS_INLINE char *Get() const { + return m_buffer; + } + + constexpr ALWAYS_INLINE size_t GetLength() const { + return m_length_and_is_normalized >> 1; + } + + constexpr ALWAYS_INLINE bool IsNormalized() const { + return static_cast(m_length_and_is_normalized & 1); + } + + constexpr ALWAYS_INLINE void SetNormalized() { + m_length_and_is_normalized |= static_cast(1); + } + + constexpr ALWAYS_INLINE void SetNotNormalized() { + m_length_and_is_normalized &= ~static_cast(1); + } + private: + constexpr ALWAYS_INLINE WriteBuffer(char *buffer, size_t length) : m_buffer(buffer), m_length_and_is_normalized(0) { + this->SetLength(length); + } + public: + static WriteBuffer Make(size_t length) { + if (void *alloc = ::ams::fs::impl::Allocate(length); alloc != nullptr) { + return WriteBuffer(static_cast(alloc), length); + } else { + return WriteBuffer(); + } + } + private: + + constexpr ALWAYS_INLINE void SetLength(size_t size) { + m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1); + } + }; private: const char *m_str; - util::TypedStorage m_write_buffer; - size_t m_write_buffer_length_and_is_normalized; + WriteBuffer m_write_buffer; public: - Path() : m_str(EmptyPath), m_write_buffer_length_and_is_normalized(0) { - util::ConstructAt(m_write_buffer, nullptr); + constexpr Path() : m_str(EmptyPath), m_write_buffer() { + /* ... */ } - constexpr Path(const char *s, util::ConstantInitializeTag) : m_str(s), m_write_buffer(), m_write_buffer_length_and_is_normalized(1) { } - - constexpr ~Path() { - if (!std::is_constant_evaluated()) { - util::DestroyAt(m_write_buffer); - } + constexpr Path(const char *s, util::ConstantInitializeTag) : m_str(s), m_write_buffer() { + m_write_buffer.SetNormalized(); } + constexpr ~Path() { /* ... */ } + WriteBuffer ReleaseBuffer() { /* Check pre-conditions. */ - AMS_ASSERT(util::GetReference(m_write_buffer) != nullptr); + AMS_ASSERT(m_write_buffer.Get() != nullptr); /* Reset. */ m_str = EmptyPath; - this->SetWriteBufferLength(0); /* Return our write buffer. */ - return std::move(util::GetReference(m_write_buffer)); + return std::move(m_write_buffer); } constexpr Result SetShallowBuffer(const char *buffer) { /* Check pre-conditions. */ - AMS_ASSERT(this->GetWriteBufferLength() == 0); + AMS_ASSERT(m_write_buffer.GetLength() == 0); /* Check the buffer is valid. */ R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); @@ -121,7 +174,7 @@ namespace ams::fs { R_TRY(this->Preallocate(len + 1)); /* Copy the path. */ - const size_t copied = util::Strlcpy(GetBuffer(util::GetReference(m_write_buffer)), rhs.GetString(), len + 1); + const size_t copied = util::Strlcpy(m_write_buffer.Get(), rhs.GetString(), len + 1); R_UNLESS(copied == len, fs::ResultUnexpectedInPathA()); /* Set normalized. */ @@ -165,7 +218,7 @@ namespace ams::fs { R_TRY(this->Preallocate(len + 1)); /* Format our path into our new buffer. */ - const auto real_len = util::VSNPrintf(GetBuffer(util::GetReference(m_write_buffer)), this->GetWriteBufferLength(), fmt, vl); + const auto real_len = util::VSNPrintf(m_write_buffer.Get(), m_write_buffer.GetLength(), fmt, vl); AMS_ASSERT(real_len == len); AMS_UNUSED(real_len); @@ -186,8 +239,8 @@ namespace ams::fs { R_TRY(this->InitializeImpl(path, std::strlen(path))); /* Replace slashes as desired. */ - if (const auto write_buffer_length = this->GetWriteBufferLength(); write_buffer_length > 1) { - fs::Replace(this->GetWriteBuffer(), write_buffer_length - 1, '\\', '/'); + if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) { + fs::Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/'); } /* Set not normalized. */ @@ -204,8 +257,8 @@ namespace ams::fs { R_TRY(this->InitializeImpl(path, std::strlen(path))); /* Replace slashes as desired. */ - if (this->GetWriteBufferLength() > 1) { - if (auto *p = this->GetWriteBuffer(); p[0] == '/' && p[1] == '/') { + if (m_write_buffer.GetLength() > 1) { + if (auto *p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') { p[0] = '\\'; p[1] = '\\'; } @@ -229,7 +282,7 @@ namespace ams::fs { /* Replace unc as desired. */ if (m_str[0]) { - auto *p = this->GetWriteBuffer(); + auto *p = m_write_buffer.Get(); /* Replace :/// -> \\ as needed. */ if (auto *sep = std::strstr(p, ":///"); sep != nullptr) { @@ -336,8 +389,8 @@ namespace ams::fs { /* Reset our write buffer. */ WriteBuffer old_write_buffer; - if (util::GetReference(m_write_buffer) != nullptr) { - old_write_buffer = std::move(util::GetReference(m_write_buffer)); + if (m_write_buffer.Get() != nullptr) { + old_write_buffer = std::move(m_write_buffer); this->ClearBuffer(); } @@ -345,9 +398,9 @@ namespace ams::fs { R_TRY(this->Preallocate(cur_len + 1 + child_len + 1)); /* Get our write buffer. */ - auto *dst = this->GetWriteBuffer(); - if (old_write_buffer != nullptr && cur_len > 0) { - util::Strlcpy(dst, static_cast(*old_write_buffer), cur_len + 1); + auto *dst = m_write_buffer.Get(); + if (old_write_buffer.Get() != nullptr && cur_len > 0) { + util::Strlcpy(dst, old_write_buffer.Get(), cur_len + 1); } /* Add separator. */ @@ -388,15 +441,15 @@ namespace ams::fs { Result RemoveChild() { /* If we don't have a write-buffer, ensure that we have one. */ - if (util::GetReference(m_write_buffer) == nullptr) { + if (m_write_buffer.Get() == nullptr) { if (const auto len = std::strlen(m_str); len > 0) { R_TRY(this->Preallocate(len)); - util::Strlcpy(GetBuffer(util::GetReference(m_write_buffer)), m_str, len + 1); + util::Strlcpy(m_write_buffer.Get(), m_str, len + 1); } } /* Check that it's possible for us to remove a child. */ - auto *p = this->GetWriteBuffer(); + auto *p = m_write_buffer.Get(); s32 len = std::strlen(p); R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), fs::ResultNotImplemented()); @@ -436,7 +489,7 @@ namespace ams::fs { /* If we're not normalized, normalize. */ if (!normalized) { /* Determine necessary buffer length. */ - auto len = this->GetWriteBufferLength(); + auto len = m_write_buffer.GetLength(); if (flags.IsRelativePathAllowed() && fs::IsPathRelative(m_str)) { len += 2; } @@ -446,14 +499,14 @@ namespace ams::fs { /* Allocate a new buffer. */ const size_t size = util::AlignUp(len, WriteBufferAlignmentLength); - auto buf = MakeWriteBuffer(size); - R_UNLESS(buf != nullptr, fs::ResultAllocationMemoryFailedMakeUnique()); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, fs::ResultAllocationMemoryFailedMakeUnique()); /* Normalize into it. */ - R_TRY(PathFormatter::Normalize(*buf, size, GetBuffer(util::GetReference(m_write_buffer)), this->GetWriteBufferLength(), flags)); + R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(), m_write_buffer.GetLength(), flags)); /* Set the normalized buffer as our buffer. */ - this->SetModifiableBuffer(std::move(buf), size); + this->SetModifiableBuffer(std::move(buf)); } /* Set normalized. */ @@ -462,41 +515,43 @@ namespace ams::fs { } private: void ClearBuffer() { - util::GetReference(m_write_buffer).reset(); - this->SetWriteBufferLength(0); + m_write_buffer.ResetBuffer(); m_str = EmptyPath; } - void SetModifiableBuffer(WriteBuffer &&buffer, size_t size) { + void SetModifiableBuffer(WriteBuffer &&buffer) { /* Check pre-conditions. */ - AMS_ASSERT(buffer.get() != nullptr); - AMS_ASSERT(size > 0); - AMS_ASSERT(util::IsAligned(size, WriteBufferAlignmentLength)); + AMS_ASSERT(buffer.Get() != nullptr); + AMS_ASSERT(buffer.GetLength() > 0); + AMS_ASSERT(util::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength)); + + /* Get whether we're normalized. */ + if (m_write_buffer.IsNormalized()) { + buffer.SetNormalized(); + } else { + buffer.SetNotNormalized(); + } /* Set write buffer. */ - util::GetReference(m_write_buffer) = std::move(buffer); - this->SetWriteBufferLength(size); - m_str = GetBuffer(util::GetReference(m_write_buffer)); + m_write_buffer = std::move(buffer); + m_str = m_write_buffer.Get(); } constexpr void SetReadOnlyBuffer(const char *buffer) { m_str = buffer; - if (!std::is_constant_evaluated()) { - util::GetReference(m_write_buffer) = nullptr; - this->SetWriteBufferLength(0); - } + m_write_buffer.ResetBuffer(); } Result Preallocate(size_t length) { /* Allocate additional space, if needed. */ - if (length > this->GetWriteBufferLength()) { + if (length > m_write_buffer.GetLength()) { /* Allocate buffer. */ const size_t size = util::AlignUp(length, WriteBufferAlignmentLength); - auto buf = MakeWriteBuffer(size); - R_UNLESS(buf != nullptr, fs::ResultAllocationMemoryFailedMakeUnique()); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, fs::ResultAllocationMemoryFailedMakeUnique()); /* Set write buffer. */ - this->SetModifiableBuffer(std::move(buf), size); + this->SetModifiableBuffer(std::move(buf)); } R_SUCCEED(); @@ -508,7 +563,7 @@ namespace ams::fs { R_TRY(this->Preallocate(size + 1)); /* Copy the path. */ - const size_t copied = util::Strlcpy(this->GetWriteBuffer(), path, size + 1); + const size_t copied = util::Strlcpy(m_write_buffer.Get(), path, size + 1); R_UNLESS(copied >= size, fs::ResultUnexpectedInPathA()); } else { /* We can just clear the buffer. */ @@ -518,30 +573,20 @@ namespace ams::fs { R_SUCCEED(); } - char *GetWriteBuffer() { - AMS_ASSERT(util::GetReference(m_write_buffer) != nullptr); - return GetBuffer(util::GetReference(m_write_buffer)); + constexpr char *GetWriteBuffer() { + AMS_ASSERT(m_write_buffer.Get() != nullptr); + return m_write_buffer.Get(); } constexpr ALWAYS_INLINE size_t GetWriteBufferLength() const { - return m_write_buffer_length_and_is_normalized >> 1; + return m_write_buffer.GetLength(); } - constexpr ALWAYS_INLINE void SetWriteBufferLength(size_t size) { - m_write_buffer_length_and_is_normalized = (m_write_buffer_length_and_is_normalized & 1) | (size << 1); - } + constexpr ALWAYS_INLINE bool IsNormalized() const { return m_write_buffer.IsNormalized(); } - constexpr ALWAYS_INLINE bool IsNormalized() const { - return static_cast(m_write_buffer_length_and_is_normalized & 1); - } + constexpr ALWAYS_INLINE void SetNormalized() { m_write_buffer.SetNormalized(); } - constexpr ALWAYS_INLINE void SetNormalized() { - m_write_buffer_length_and_is_normalized |= static_cast(1); - } - - constexpr ALWAYS_INLINE void SetNotNormalized() { - m_write_buffer_length_and_is_normalized &= ~static_cast(1); - } + constexpr ALWAYS_INLINE void SetNotNormalized() { m_write_buffer.SetNotNormalized(); } public: ALWAYS_INLINE bool operator==(const fs::Path &rhs) const { return std::strcmp(this->GetString(), rhs.GetString()) == 0; } ALWAYS_INLINE bool operator!=(const fs::Path &rhs) const { return !(*this == rhs); }