Compare commits

..

4 Commits

Author SHA1 Message Date
8a430faf4c [keel] Cleanup
All checks were successful
Build / build (push) Successful in 1m19s
2025-08-12 22:57:13 -05:00
e59382dd60 [keel] Address undefined behavior 2025-08-12 22:39:08 -05:00
3006e77ef3 [ox/std] Add and integrate std::launder
All checks were successful
Build / build (push) Successful in 1m30s
2025-08-12 22:37:21 -05:00
59c112a69c [studio] Fix navigate back not to iterate on the first item twice
All checks were successful
Build / build (push) Successful in 1m55s
2025-08-09 15:15:34 -05:00
22 changed files with 125 additions and 124 deletions

View File

@@ -30,11 +30,49 @@ constexpr void *operator new(std::size_t, void *addr) noexcept {
constexpr void *operator new[](std::size_t, void *addr) noexcept {
return addr;
}
namespace std {
template<typename T>
[[nodiscard]]
constexpr T* launder(T* __p) noexcept {
return __builtin_launder(__p);
}
template<typename T, typename... Args, bool noex>
void launder(T(*)(Args...) noexcept(noex)) = delete;
template<typename T, typename... Args, bool noex>
void launder(T(*)(Args......) noexcept(noex)) = delete;
void launder(void*) = delete;
void launder(void const*) = delete;
void launder(volatile void*) = delete;
void launder(volatile void const*) = delete;
}
#endif
namespace ox {
/**
* Aliases type T in size and alignment to allow allocating space for a T
* without running the constructor.
*/
template<typename T, std::size_t sz = sizeof(T)>
struct alignas(alignof(T)) AllocAlias {
char buff[sz];
constexpr AllocAlias() noexcept = default;
[[nodiscard]]
auto data() noexcept {
return reinterpret_cast<T*>(this);
}
[[nodiscard]]
auto data() const noexcept {
return reinterpret_cast<T const*>(this);
}
};
template<typename T, typename U = T, typename ...Args>
[[nodiscard]]
constexpr U *make(Args &&...args) noexcept {

View File

@@ -63,25 +63,6 @@ namespace ox {
using CString = char const*;
/**
* Aliases type T in size and alignment to allow allocating space for a T
* without running the constructor.
*/
template<typename T, std::size_t sz = sizeof(T)>
struct alignas(alignof(T)) AllocAlias {
char buff[sz];
constexpr AllocAlias() noexcept = default;
[[nodiscard]]
auto data() noexcept {
return reinterpret_cast<T*>(this);
}
[[nodiscard]]
auto data() const noexcept {
return reinterpret_cast<const T*>(this);
}
};
template<std::size_t sz>
struct SignedType {
};

View File

@@ -34,10 +34,10 @@ struct VectorAllocator {
ox::Array<AllocAlias<T>, Size> m_data = {};
protected:
constexpr VectorAllocator() noexcept = default;
constexpr VectorAllocator(const VectorAllocator&) noexcept = default;
constexpr VectorAllocator(VectorAllocator const&) noexcept = default;
constexpr VectorAllocator(VectorAllocator&&) noexcept = default;
constexpr void allocate(T **items, std::size_t cap) noexcept {
constexpr void allocate(T **items, std::size_t const cap) noexcept {
// small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr
if (std::is_constant_evaluated() || cap > Size) {
*items = Allocator{}.allocate(cap);
@@ -49,42 +49,26 @@ struct VectorAllocator {
constexpr void moveConstructItemsFrom(
T **items,
VectorAllocator *src,
const std::size_t count,
const std::size_t cap) noexcept {
std::size_t const count,
std::size_t const cap) noexcept {
// this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM,
// try removing it later
if (!std::is_constant_evaluated()) {
if (cap <= m_data.size() && count <= m_data.size()) {
for (auto i = 0u; i < count; ++i) {
const auto dstItem = reinterpret_cast<T *>(&m_data[i]);
const auto srcItem = reinterpret_cast<T *>(&src->m_data[i]);
std::construct_at<T>(dstItem, std::move(*srcItem));
auto const srcItem = std::launder(reinterpret_cast<T*>(&src->m_data[i]));
new (&m_data[i]) T(std::move(*srcItem));
}
if (count) {
*items = std::launder(reinterpret_cast<T*>(m_data.data()));
} else {
*items = reinterpret_cast<T*>(m_data.data());
}
}
}
constexpr void moveItemsFrom(
T **items,
VectorAllocator *src,
const std::size_t count,
const std::size_t cap) noexcept {
// this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM,
// try removing it later
if (!std::is_constant_evaluated()) {
if (cap <= m_data.size() && count <= m_data.size()) {
for (std::size_t i = 0; i < count; ++i) {
const auto dstItem = reinterpret_cast<T *>(&m_data[i]);
const auto srcItem = reinterpret_cast<T *>(&src->m_data[i]);
*dstItem = std::move(*srcItem);
}
*items = reinterpret_cast<T*>(m_data.data());
}
}
}
constexpr void deallocate(T *items, std::size_t cap) noexcept {
constexpr void deallocate(T *const items, std::size_t const cap) noexcept {
// small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr
if (std::is_constant_evaluated()) {
if (items) {
@@ -103,10 +87,10 @@ template<typename T, typename Allocator>
struct VectorAllocator<T, Allocator, 0> {
protected:
constexpr VectorAllocator() noexcept = default;
constexpr VectorAllocator(const VectorAllocator&) noexcept = default;
constexpr VectorAllocator(VectorAllocator const&) noexcept = default;
constexpr VectorAllocator(VectorAllocator&&) noexcept = default;
constexpr void allocate(T **items, std::size_t cap) noexcept {
constexpr void allocate(T **items, std::size_t const cap) noexcept {
*items = Allocator{}.allocate(cap);
}
@@ -114,15 +98,15 @@ struct VectorAllocator<T, Allocator, 0> {
constexpr void moveConstructItemsFrom(
T**,
VectorAllocator*,
const std::size_t,
const std::size_t) noexcept {
std::size_t const,
std::size_t const) noexcept {
}
[[maybe_unused]]
constexpr void moveItemsFrom(T**, VectorAllocator*, const std::size_t, const std::size_t) noexcept {
constexpr void moveItemsFrom(T**, VectorAllocator*, std::size_t const, std::size_t const) noexcept {
}
constexpr void deallocate(T *items, std::size_t cap) noexcept {
constexpr void deallocate(T *const items, std::size_t const cap) noexcept {
if (items) {
Allocator{}.deallocate(items, cap);
}
@@ -421,7 +405,7 @@ constexpr Vector<T, SmallVectorSize, Allocator> &Vector<T, SmallVectorSize, Allo
m_size = other.m_size;
m_cap = other.m_cap;
m_items = other.m_items;
this->moveItemsFrom(&m_items, &other, m_size, m_cap);
this->moveConstructItemsFrom(&m_items, &other, m_size, m_cap);
other.m_size = 0;
other.m_cap = 0;
other.m_items = nullptr;

View File

@@ -15,7 +15,7 @@ target_link_libraries(
target_compile_definitions(
NostalgiaStudio PUBLIC
OLYMPIC_APP_VERSION="d2025.07.0"
OLYMPIC_APP_VERSION="dev build"
)
install(

View File

@@ -18,7 +18,7 @@
<string>APPL</string>
<key>CFBundleVersion</key>
<string>d2025.07.0</string>
<string>dev build</string>
<key>LSMinimumSystemVersion</key>
<string>12.0.0</string>

View File

@@ -13,7 +13,7 @@ namespace keel {
constexpr auto K1HdrSz = 40;
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept;
ox::Result<ox::UUID> readUuidHeader(ox::BufferView const &buff) noexcept;
ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept;
@@ -35,15 +35,15 @@ ox::Result<T> readAsset(ox::BufferView buff) noexcept {
return out;
}
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept;
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView const &buff) noexcept;
struct AssetHdr {
ox::UUID uuid;
ox::ClawHeader clawHdr;
};
ox::Result<ox::StringView> readAssetTypeId(ox::BufferView buff) noexcept;
ox::Result<ox::StringView> readAssetTypeId(ox::BufferView const &buff) noexcept;
ox::Result<AssetHdr> readAssetHeader(ox::BufferView buff) noexcept;
ox::Result<AssetHdr> readAssetHeader(ox::BufferView const &buff) noexcept;
}

View File

@@ -147,7 +147,7 @@ template<typename T>
ox::Result<AssetRef<T>> readObj(
Context &ctx,
ox::StringViewCR assetId,
[[maybe_unused]] bool forceLoad = false) noexcept {
[[maybe_unused]] bool const forceLoad = false) noexcept {
#ifndef OX_BARE_METAL
return readObjFile<T>(ctx, assetId, forceLoad);
#else
@@ -159,7 +159,7 @@ template<typename T>
ox::Result<AssetRef<T>> readObj(
Context &ctx,
ox::FileAddress const &file,
[[maybe_unused]] bool forceLoad = false) noexcept {
[[maybe_unused]] bool const forceLoad = false) noexcept {
#ifndef OX_BARE_METAL
OX_REQUIRE(assetId, file.getPath());
return readObj<T>(ctx, ox::StringView(assetId), forceLoad);
@@ -178,7 +178,7 @@ ox::Error writeObj(
Context &ctx,
ox::FileAddress const &file,
T const &obj,
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept {
ox::ClawFormat const fmt = ox::ClawFormat::Metal) noexcept {
OX_REQUIRE(objBuff, ox::writeClaw(obj, fmt));
return ctx.rom->write(file, objBuff.data(), objBuff.size());
}

View File

@@ -214,7 +214,7 @@ class Converter {
}
[[nodiscard]]
BaseConverter const &converter() const noexcept {
return *m_buff.data();
return *std::launder(m_buff.data());
}
};

View File

@@ -16,7 +16,7 @@ class TypeStore: public ox::TypeStore {
ox::String m_descPath;
public:
explicit TypeStore(ox::FileSystem &fs, ox::StringView descPath) noexcept;
explicit TypeStore(ox::FileSystem &fs, ox::StringViewCR descPath) noexcept;
protected:
ox::Result<ox::UPtr<ox::DescriptorType>> loadDescriptor(ox::StringView typeId) noexcept override;

View File

@@ -6,7 +6,7 @@
namespace keel {
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept {
ox::Result<ox::UUID> readUuidHeader(ox::BufferView const &buff) noexcept {
if (buff.size() < K1HdrSz) [[unlikely]] {
return ox::Error{1, "Insufficient data to contain complete Keel header"};
}
@@ -27,16 +27,15 @@ ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept {
return id;
}
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept {
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView const &buff) noexcept {
std::size_t offset = 0;
if (!readUuidHeader(buff).error) {
offset = K1HdrSz;
}
buff += offset;
return ox::readClaw(ts, buff);
return ox::readClaw(ts, buff + offset);
}
ox::Result<ox::StringView> readAssetTypeId(ox::BufferView const buff) noexcept {
ox::Result<ox::StringView> readAssetTypeId(ox::BufferView const &buff) noexcept {
auto const err = readUuidHeader(buff).error;
auto const offset = err ? 0u : K1HdrSz;
if (offset >= buff.size()) [[unlikely]] {
@@ -45,15 +44,14 @@ ox::Result<ox::StringView> readAssetTypeId(ox::BufferView const buff) noexcept {
return ox::readClawTypeId(buff + offset);
}
ox::Result<AssetHdr> readAssetHeader(ox::BufferView buff) noexcept {
ox::Result<AssetHdr> readAssetHeader(ox::BufferView const &buff) noexcept {
ox::Result<AssetHdr> out;
auto const err = readUuidHeader(buff).moveTo(out.value.uuid);
auto const offset = err ? 0u : K1HdrSz;
if (offset >= buff.size()) [[unlikely]] {
return ox::Error(1, "Buffer too small for expected data");
}
buff += offset;
OX_RETURN_ERROR(ox::readClawHeader(buff).moveTo(out.value.clawHdr));
OX_RETURN_ERROR(ox::readClawHeader(buff + offset).moveTo(out.value.clawHdr));
return out;
}

View File

@@ -6,7 +6,7 @@
namespace keel {
TypeStore::TypeStore(ox::FileSystem &fs, ox::StringView descPath) noexcept:
TypeStore::TypeStore(ox::FileSystem &fs, ox::StringViewCR descPath) noexcept:
m_fs(fs),
m_descPath(descPath) {
}

View File

@@ -25,7 +25,7 @@ static std::map<ox::StringView, ox::Error(*)()> tests = {
},
};
int main(int argc, char const **argv) {
int main(int const argc, char const **argv) {
int retval = -1;
if (argc > 0) {
auto const args = ox::Span{argv, static_cast<size_t>(argc)};