Remove index property, store it packed in the pointer instead

To achieve this I force memory to a 16-bytes alignment, to
achieve this code will allocate 15 extra bytes on the heap
but each Block and Frame object will be 8 bytes smaller. Given
they are created on the fly and passed around a lot, I think
this is a good tradeoff. Not super urgent, but I got it done
now, mostly to make sure index is used in a sane way and doesn't
take too large values, which would prevent me from making this
change later in the future.
This commit is contained in:
King_DuckZ 2025-02-24 18:56:39 +00:00
parent 773eaf3f81
commit 7af7ce540d
11 changed files with 616 additions and 20 deletions

View file

@ -0,0 +1,37 @@
/* Copyright 2020-2025, Michele Santullo
* This file is part of memoserv.
*
* Memoserv is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Memoserv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Memoserv. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <memory>
namespace dhandy {
template <typename T, typename I, std::size_t A>
class PackedPointer;
} //namespace dhandy
namespace mc::psx {
std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0);
const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0);
bool is_block_aligned (const std::uint8_t* ptr);
template <typename T, std::size_t A>
bool is_block_aligned (const dhandy::PackedPointer<T, std::size_t, A>& ptr);
std::unique_ptr<std::uint8_t[]> make_unique_for_block_aligned(std::size_t size);
} //namespace mc::psx

View file

@ -20,6 +20,7 @@
#include "memcard/frame.hpp" #include "memcard/frame.hpp"
#include "memcard/frame_iterator.hpp" #include "memcard/frame_iterator.hpp"
#include "memcard/country_code.hpp" #include "memcard/country_code.hpp"
#include "duckhandy/packed_pointer.hpp"
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@ -44,17 +45,21 @@ public:
using data_type = typename std::conditional<Const, const uint8_t, uint8_t>::type; using data_type = typename std::conditional<Const, const uint8_t, uint8_t>::type;
typedef FrameIterator<Const, Const> iterator; typedef FrameIterator<Const, Const> iterator;
typedef FrameIterator<true, Const> const_iterator; typedef FrameIterator<true, Const> const_iterator;
typedef BasicFrame<Const> FrameType;
static const constexpr std::size_t FrameCount = 64; static const constexpr std::size_t FrameCount = 64;
static const constexpr std::size_t TOCBlockIndex = 0xFF; static const constexpr std::size_t TOCBlockIndex = 0xFF;
static const constexpr uint16_t LastLink = 0xFFFF; static const constexpr uint16_t LastLink = 0xFFFF;
static constexpr std::size_t BlockByteSize = BasicFrame<Const>::Size * FrameCount; static constexpr std::size_t BlockByteSize = FrameType::Size * FrameCount;
static constexpr auto DataAlignment = FrameType::DataAlignment;
static constexpr bool IsConst = Const;
static_assert(BlockByteSize % DataAlignment == 0);
BasicBlock() = default; BasicBlock() = default;
constexpr BasicBlock (const BasicBlock& other); constexpr BasicBlock (const BasicBlock& other);
BasicBlock(BasicBlock&&) = default; BasicBlock(BasicBlock&&) = default;
BasicBlock (data_type* blk, std::size_t index); BasicBlock (data_type* blk, std::size_t index);
BasicBlock (data_type* blk, BasicFrame<Const> toc_entry, std::size_t index); BasicBlock (data_type* blk, FrameType toc_entry, std::size_t index);
template <bool Const2> BasicBlock(const BasicBlock<Const2>& other); template <bool Const2> BasicBlock(const BasicBlock<Const2>& other);
~BasicBlock(); ~BasicBlock();
@ -68,9 +73,9 @@ public:
data_type* data() { return m_begin; } data_type* data() { return m_begin; }
const data_type* data() const { return m_begin; } const data_type* data() const { return m_begin; }
BasicFrame<Const> frame(unsigned int idx); FrameType frame(unsigned int idx);
ConstFrame frame(unsigned int idx) const; ConstFrame frame(unsigned int idx) const;
BasicFrame<Const> toc(); FrameType toc();
ConstFrame toc() const; ConstFrame toc() const;
static constexpr std::size_t size() { return FrameCount; } static constexpr std::size_t size() { return FrameCount; }
@ -79,7 +84,7 @@ public:
bool has_magic() const; bool has_magic() const;
IconDisplayFlag icon_display_flag() const; IconDisplayFlag icon_display_flag() const;
std::size_t block_count() const; std::size_t block_count() const;
std::size_t index() const { return m_index; } std::size_t index() const { return dhandy::get<0>(m_begin); }
std::string title() const; std::string title() const;
std::size_t available_blocks() const; std::size_t available_blocks() const;
uint32_t use_byte() const; uint32_t use_byte() const;
@ -95,9 +100,8 @@ public:
BasicBlock& operator= (BasicBlock&&) = default; BasicBlock& operator= (BasicBlock&&) = default;
private: private:
BasicFrame<Const> m_toc_entry; FrameType m_toc_entry;
std::size_t m_index; dhandy::PackedPointer<data_type, std::size_t, DataAlignment> m_begin;
data_type* m_begin;
}; };
[[gnu::const]] [[gnu::const]]
@ -122,7 +126,6 @@ inline int32_t calc_icon_count (IconDisplayFlag idf) {
template <bool Const> template <bool Const>
constexpr BasicBlock<Const>::BasicBlock (const BasicBlock& other) : constexpr BasicBlock<Const>::BasicBlock (const BasicBlock& other) :
m_toc_entry(other.m_toc_entry), m_toc_entry(other.m_toc_entry),
m_index(other.m_index),
m_begin(other.m_begin) m_begin(other.m_begin)
{ {
} }
@ -131,7 +134,6 @@ template <bool Const>
template <bool Const2> template <bool Const2>
inline BasicBlock<Const>::BasicBlock(const BasicBlock<Const2>& other) : inline BasicBlock<Const>::BasicBlock(const BasicBlock<Const2>& other) :
m_toc_entry(other.m_toc_entry), m_toc_entry(other.m_toc_entry),
m_index(other.m_index),
m_begin(other.m_begin) m_begin(other.m_begin)
{ {
} }

View file

@ -17,6 +17,7 @@
#pragma once #pragma once
#include "duckhandy/packed_pointer.hpp"
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
@ -29,6 +30,11 @@ class BasicFrame {
using data_type = typename std::conditional<Const, const uint8_t, uint8_t>::type; using data_type = typename std::conditional<Const, const uint8_t, uint8_t>::type;
public: public:
static const constexpr unsigned int Size = 128; static const constexpr unsigned int Size = 128;
//Standard memory cards can hold up to 15 blocks so we need 4 bits in the
//data pointer field to use for index storing. This means alignment of the
//supplied block should be at least 16
static constexpr std::size_t DataAlignment = 16;
static_assert(Size % DataAlignment == 0);
BasicFrame() = default; BasicFrame() = default;
BasicFrame (data_type* data, std::size_t index); BasicFrame (data_type* data, std::size_t index);
@ -60,8 +66,7 @@ public:
private: private:
void extract (std::size_t offset, std::size_t sz, uint8_t* out); void extract (std::size_t offset, std::size_t sz, uint8_t* out);
std::size_t m_index; dhandy::PackedPointer<data_type, std::size_t, DataAlignment> m_data;
data_type* m_data;
}; };
template <typename T, bool Const> template <typename T, bool Const>
@ -78,7 +83,6 @@ inline T read_as (const BasicFrame<Const>& frame, std::size_t offset) {
template <bool Const> template <bool Const>
template <bool Const2> template <bool Const2>
inline BasicFrame<Const>::BasicFrame (const BasicFrame<Const2>& other) { inline BasicFrame<Const>::BasicFrame (const BasicFrame<Const2>& other) {
m_index = other.m_index;
m_data = other.m_data; m_data = other.m_data;
} }

View file

@ -58,7 +58,9 @@ public:
const_iterator end() const; const_iterator end() const;
private: private:
std::vector<uint8_t> m_data; const std::uint8_t* data_ptr() const;
std::vector<std::uint8_t> m_data;
}; };
std::vector<std::size_t> block_group (const MemoryCard& mc, std::size_t index); std::vector<std::size_t> block_group (const MemoryCard& mc, std::size_t index);

View file

@ -0,0 +1,180 @@
/* Copyright 2016-2025 Michele Santullo
* This file is part of "duckhandy".
*
* "duckhandy" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "duckhandy" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef id895441FC389F481EB824A809A0BF5004
#define id895441FC389F481EB824A809A0BF5004
#include <cstddef>
#include <stdexcept>
#include <climits>
namespace dhandy {
namespace implem {
template <typename T, std::size_t Index>
class BitfieldData;
template <typename T, std::size_t Index>
class BitfieldData : public BitfieldData<T, Index - 1> {
protected:
BitfieldData (T init) : BitfieldData<T, Index - 1>(init), m_value(init) {}
public:
T& value() { return m_value; }
T value() const { return m_value; }
private:
T m_value;
};
template <typename T>
class BitfieldData<T, 1> {
protected:
BitfieldData (T init) : m_value(init) {}
public:
T& value() { return m_value; }
T value() const { return m_value; }
private:
T m_value;
};
template <typename T>
class BitfieldData<T, 0>;
struct SizeAndOffs {
std::size_t size, offset;
};
struct IndexAndSize {
std::size_t index, size;
};
template <std::size_t Sum>
constexpr inline SizeAndOffs size_and_offset_impl (std::size_t) {
throw std::out_of_range("Requested pack index out of range");
}
template <std::size_t Sum, std::size_t Sz, std::size_t... Sizes>
constexpr inline SizeAndOffs size_and_offset_impl (std::size_t index) {
return (index ? size_and_offset_impl<Sum + Sz, Sizes...>(index - 1) : SizeAndOffs{Sz, Sum});
}
template <std::size_t... Sizes>
constexpr inline SizeAndOffs size_and_offset (std::size_t index) {
return size_and_offset_impl<0, Sizes...>(index);
}
template <std::size_t CurrMax, std::size_t MaxIdx, std::size_t Idx>
constexpr inline IndexAndSize index_and_size_impl() {
throw std::runtime_error("Should not be reached");
}
template <std::size_t CurrMax, std::size_t MaxIdx, std::size_t Idx, std::size_t Sz, std::size_t... Sizes>
constexpr inline IndexAndSize index_and_size_impl() {
return (Idx ? index_and_size_impl<
(CurrMax > Sz ? CurrMax : Sz),
(CurrMax > Sz ? MaxIdx : Idx),
Idx - 1,
Sizes...
>() : IndexAndSize{(CurrMax > Sz ? MaxIdx : Idx) - 1, CurrMax > Sz ? CurrMax : Sz});
}
template <std::size_t Sz, std::size_t... Sizes>
constexpr inline IndexAndSize index_and_size() {
return index_and_size_impl<Sz, sizeof...(Sizes), sizeof...(Sizes), Sz, Sizes...>();
}
template <typename T, std::size_t... Sizes>
struct BitfieldFacts {
static const constexpr std::size_t Sum = size_and_offset<Sizes...>(sizeof...(Sizes) - 1).size + size_and_offset<Sizes...>(sizeof...(Sizes) - 1).offset;
static const constexpr std::size_t ElementCount = (Sum + CHAR_BIT * sizeof(T) - 1) / (CHAR_BIT * sizeof(T));
static const constexpr std::size_t LargestIndex = index_and_size<Sizes...>().index;
static const constexpr std::size_t LargestSize = index_and_size<Sizes...>().size;
};
template <typename T>
[[gnu::const]]
constexpr inline T make_mask (std::size_t len) noexcept {
return (T{1} << len) - 1;
}
} //namespace implem
template <typename T, std::size_t... Sizes>
class BitfieldPack;
template <std::size_t Idx, typename T, std::size_t... Sizes>
T get (const BitfieldPack<T, Sizes...>&);
template <std::size_t Idx, typename T, typename V, std::size_t... Sizes>
void set (BitfieldPack<T, Sizes...>&, V);
template <typename T, std::size_t... Sizes>
class BitfieldPack : private implem::BitfieldData<T, implem::BitfieldFacts<T, Sizes...>::ElementCount> {
typedef implem::BitfieldFacts<T, Sizes...> Facts;
template <std::size_t Idx, typename U, std::size_t... S> friend U get (const BitfieldPack<U, S...>&);
template <std::size_t Idx, typename U, typename V, std::size_t... S> friend void set (BitfieldPack<U, S...>&, V);
typedef implem::BitfieldData<T, Facts::ElementCount> base_type;
static_assert(Facts::LargestSize <= sizeof(T) * CHAR_BIT, "Bitfield size can't be larger than the data type itself");
public:
template <std::size_t Idx>
static constexpr T max_at_index = implem::make_mask<T>(implem::size_and_offset<Sizes...>(Idx).size);
static constexpr std::size_t element_count = sizeof...(Sizes);
BitfieldPack (T init=T{}) : base_type(init) {}
};
template <std::size_t Idx, typename T, std::size_t... Sizes>
T get (const BitfieldPack<T, Sizes...>& pack) {
using implem::size_and_offset;
using implem::BitfieldData;
using implem::make_mask;
static_assert(Idx < sizeof...(Sizes), "Index out of bounds");
const constexpr auto so = size_and_offset<Sizes...>(Idx);
const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT);
const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT);
const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT;
const T bottom = static_cast<const BitfieldData<T, index + 1>&>(pack).value() >> reloffs;
if constexpr (bleeds_into_next) {
const std::size_t safe_index = index + 2;
const T val = static_cast<const BitfieldData<T, safe_index>&>(pack).value();
const T top = val << (sizeof(T) * CHAR_BIT - reloffs);
return static_cast<T>(top | bottom) & make_mask<T>(so.size);
}
else {
return bottom & make_mask<T>(so.size);
}
}
template <std::size_t Idx, typename T, typename V, std::size_t... Sizes>
void set (BitfieldPack<T, Sizes...>& pack, V val) {
using implem::size_and_offset;
using implem::BitfieldData;
using implem::make_mask;
static_assert(Idx < sizeof...(Sizes), "Index out of bounds");
const constexpr auto so = size_and_offset<Sizes...>(Idx);
const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT);
const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT);
const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT;
T& bottom = static_cast<BitfieldData<T, index + 1>&>(pack).value();
bottom &= ~(make_mask<T>(so.size) << reloffs);
bottom |= static_cast<T>((val & make_mask<T>(so.size)) << reloffs);
if (bleeds_into_next) {
const std::size_t safe_index = index + 2 - not bleeds_into_next;
T& top = static_cast<BitfieldData<T, safe_index>&>(pack).value();
const constexpr std::size_t shift = sizeof(T) * CHAR_BIT - reloffs;
top &= ~(make_mask<T>(so.size) >> shift);
top |= val >> shift;
}
}
} //namespace dhandy
#endif

View file

@ -0,0 +1,306 @@
/* Copyright 2016-2025 Michele Santullo
* This file is part of "duckhandy".
*
* "duckhandy" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "duckhandy" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef id83CE9FAB90684250B98EA584C548E13F
#define id83CE9FAB90684250B98EA584C548E13F
#include "bitfield_pack.hpp"
#include <cstdint>
#include <utility>
#include <climits>
#include <type_traits>
#include <cassert>
#include <concepts>
namespace dhandy {
namespace detail {
//This monster expression is just the one-liner version of this
//implementation of log2:
//unsigned int v; // 32-bit value to find the log2 of
//register unsigned int r; // result of log2(v) will go here
//register unsigned int shift;
//
//r = (v > 0xFFFF) << 4; v >>= r;
//shift = (v > 0xFF ) << 3; v >>= shift; r |= shift;
//shift = (v > 0xF ) << 2; v >>= shift; r |= shift;
//shift = (v > 0x3 ) << 1; v >>= shift; r |= shift;
// r |= (v >> 1);
//see https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog for
//an explanation of that.
//This is a shorter version of it but it only works if the input is a power
//of 2:
//(((((NUM&0xAAAAAAAA)!=0)|((NUM&0xFFFF0000)!=0)<<4)|((NUM&0xFF00FF00)!=0)<<
//3)|((NUM&0xF0F0F0F0)!=0)<<2)|((NUM&0xCCCCCCCC)!=0)<<1
//Source for that is also available at the page linked above.
template <std::uint32_t NUM> constexpr std::size_t pointer_unused_bit_count =
((((((NUM > 0xFFFF) << 4) | (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) <<
3)) | ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) <<
4)) > 0xFF) << 3)) > 0xF) << 2)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >>
(((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM >
0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF)
<< 2)) > 0x3) << 1)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> (
(NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4)
) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) >> ((
(((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) >
0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM >
0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) > 0x3) << 1)) >> 1))
;
template <std::size_t... S>
BitfieldPack<
std::uintptr_t,
sizeof(std::uintptr_t) * CHAR_BIT - sizeof...(S), (S+1)/(S+1)...
>
guess_bitfield_type (std::index_sequence<S...>);
template <std::size_t PtrAlign>
using BaseBitfieldPackForPackedPointer = decltype(
guess_bitfield_type(
std::make_index_sequence<pointer_unused_bit_count<PtrAlign>>()
)
);
template <std::size_t PtrAlign>
using BaseIntPackForPackedPointer = BitfieldPack<
std::uintptr_t,
sizeof(std::uintptr_t) * CHAR_BIT - pointer_unused_bit_count<PtrAlign>,
pointer_unused_bit_count<PtrAlign>
>;
template <std::size_t PtrAlign, bool BitFlags>
using BaseBitfieldType = std::conditional_t<BitFlags,
BaseBitfieldPackForPackedPointer<PtrAlign>, BaseIntPackForPackedPointer<PtrAlign>
>;
template <typename T, typename I>
struct PackedPointerSetterGetter {
template <std::size_t Idx, std::size_t S1, std::size_t S2>
static void set (dhandy::BitfieldPack<std::uintptr_t, S1, S2>& out, I val) {
static_assert(0 == Idx);
dhandy::set<1>(out, val);
}
template <std::size_t Idx, std::size_t S1, std::size_t S2>
static I get (const dhandy::BitfieldPack<std::uintptr_t, S1, S2>& in) {
static_assert(0 == Idx);
return dhandy::get<1>(in);
}
};
template <typename T>
struct PackedPointerSetterGetter<T, bool> {
template <std::size_t Idx, std::size_t... S>
static void set (dhandy::BitfieldPack<std::uintptr_t, S...>& out, bool flag) {
static_assert(Idx < sizeof...(S) - 1, "Index out of range");
dhandy::set<Idx+1>(out, (flag ? 1u : 0u));
}
template <std::size_t Idx, std::size_t... S>
static bool get (const dhandy::BitfieldPack<std::uintptr_t, S...>& in) {
static_assert(Idx < sizeof...(S) - 1, "Index out of range");
return static_cast<bool>(dhandy::get<Idx+1>(in));
}
};
} //namespace detail
template <typename T, typename I=bool, std::size_t A=alignof(T)>
class PackedPointer : detail::BaseBitfieldType<A, std::is_same<I, bool>::value> {
static_assert(A >= 2, "Not enough space to pack anything");
static_assert(sizeof(T*) == sizeof(std::uintptr_t), "Mismatching int/pointer size");
typedef detail::BaseBitfieldType<A, std::is_same<I, bool>::value> parent_type;
static_assert(sizeof(parent_type) == sizeof(T*));
template <std::size_t Idx, typename U, typename J, std::size_t B> friend J get (const PackedPointer<U, J, B>&);
template <std::size_t Idx, typename U, typename J, std::size_t B> friend void set (PackedPointer<U, J, B>&, J);
public:
static constexpr std::size_t FlagsCount = parent_type::element_count - 1;
static constexpr auto max_payload =
static_cast<std::conditional_t<std::is_same_v<I,bool>, unsigned int, I>>(
parent_type::template max_at_index<1>
);
typedef I int_type;
PackedPointer();
PackedPointer (T* ptr);
PackedPointer (const PackedPointer& other);
PackedPointer (PackedPointer&& other);
~PackedPointer() = default;
operator T*();
operator const T*() const;
operator std::uintptr_t() const;
operator bool() const;
PackedPointer& operator= (const PackedPointer& other);
PackedPointer& operator= (PackedPointer&& other);
PackedPointer& operator= (T* ptr);
T* operator+ (std::integral auto diff);
const T* operator+ (std::integral auto diff) const;
T* operator- (std::integral auto diff);
const T* operator- (std::integral auto diff) const;
T* operator->();
const T* operator->() const;
T& operator*();
const T& operator*() const;
bool operator== (const PackedPointer& other) const;
bool operator== (T* other) const;
bool operator!= (const PackedPointer& other) const;
bool operator!= (T* other) const;
private:
//static const constexpr uintptr_t PtrMask = static_cast<uintptr_t>(-1) << FlagsCount;
};
template <std::size_t Idx=0, typename T=char, typename I=bool, std::size_t A=0>
inline void set (PackedPointer<T, I, A>& obj, I value) {
assert(value < (1u << A));
detail::PackedPointerSetterGetter<T, I>::template set<Idx>(obj, value);
}
template <std::size_t Idx=0, typename T=char, typename I=bool, std::size_t A>
inline I get (const PackedPointer<T, I, A>& obj) {
return detail::PackedPointerSetterGetter<T, I>::template get<Idx>(obj);
}
template <typename T, typename I, std::size_t A> const constexpr std::size_t PackedPointer<T, I, A>::FlagsCount;
//template <typename T> const constexpr std::uintptr_t PackedPointer<T>::PtrMask;
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::PackedPointer() :
PackedPointer(nullptr)
{
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::PackedPointer (T* ptr) :
parent_type(0)
{
(*this) = ptr;
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::PackedPointer (const PackedPointer& other) :
parent_type(other)
{
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::PackedPointer (PackedPointer&& other) :
parent_type(std::move(other))
{
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::operator T*() {
return reinterpret_cast<T*>(get<0>(*static_cast<parent_type*>(this)) << FlagsCount);
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::operator const T*() const {
return reinterpret_cast<const T*>(get<0>(*static_cast<const parent_type*>(this)) << FlagsCount);
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::operator std::uintptr_t() const {
return get<0>(*static_cast<const parent_type*>(this)) << FlagsCount;
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>::operator bool() const {
return static_cast<T*>(*this) != nullptr;
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>& PackedPointer<T, I, A>::operator= (const PackedPointer& other) {
*static_cast<parent_type*>(this) = static_cast<parent_type&>(other);
return *this;
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>& PackedPointer<T, I, A>::operator= (PackedPointer&& other) {
*static_cast<parent_type*>(this) = std::move(static_cast<parent_type&>(other));
return *this;
}
template <typename T, typename I, std::size_t A>
PackedPointer<T, I, A>& PackedPointer<T, I, A>::operator= (T* ptr) {
assert(reinterpret_cast<std::uintptr_t>(ptr) % A == 0);
set<0>(*static_cast<parent_type*>(this), reinterpret_cast<std::uintptr_t>(ptr) >> FlagsCount);
return *this;
}
template <typename T, typename I, std::size_t A>
T* PackedPointer<T, I, A>::operator+ (std::integral auto diff) {
return static_cast<T*>(*this) + diff;
}
template <typename T, typename I, std::size_t A>
const T* PackedPointer<T, I, A>::operator+ (std::integral auto diff) const {
return static_cast<const T*>(*this) + diff;
}
template <typename T, typename I, std::size_t A>
T* PackedPointer<T, I, A>::operator- (std::integral auto diff) {
return static_cast<T*>(*this) + diff;
}
template <typename T, typename I, std::size_t A>
const T* PackedPointer<T, I, A>::operator- (std::integral auto diff) const {
return static_cast<const T*>(*this) + diff;
}
template <typename T, typename I, std::size_t A>
T* PackedPointer<T, I, A>::operator->() {
return static_cast<T*>(*this);
}
template <typename T, typename I, std::size_t A>
const T* PackedPointer<T, I, A>::operator->() const {
return static_cast<T*>(*this);
}
template <typename T, typename I, std::size_t A>
T& PackedPointer<T, I, A>::operator*() {
return *static_cast<T*>(*this);
}
template <typename T, typename I, std::size_t A>
const T& PackedPointer<T, I, A>::operator*() const {
return *static_cast<T*>(*this);
}
template <typename T, typename I, std::size_t A>
bool PackedPointer<T, I, A>::operator== (const PackedPointer& other) const {
return this == &other or static_cast<T*>(*this) == static_cast<T*>(other);
}
template <typename T, typename I, std::size_t A>
bool PackedPointer<T, I, A>::operator== (T* other) const {
return static_cast<T*>(*this) == other;
}
template <typename T, typename I, std::size_t A>
bool PackedPointer<T, I, A>::operator!= (const PackedPointer& other) const {
return not this->operator==(other);
}
template <typename T, typename I, std::size_t A>
bool PackedPointer<T, I, A>::operator!= (T* other) const {
return not this->operator==(other);
}
} //namepace dhandy
#endif

View file

@ -3,6 +3,7 @@ add_project_link_arguments(['-lstdc++fs'], language: 'cpp')
private_incl = include_directories('src') private_incl = include_directories('src')
library_incl = include_directories('include') library_incl = include_directories('include')
duckhandy_incl = include_directories('lib/duckhandy/include')
memcard = shared_library('memcard', memcard = shared_library('memcard',
'src/memorycard.cpp', 'src/memorycard.cpp',
@ -16,11 +17,12 @@ memcard = shared_library('memcard',
'src/part_iterator.cpp', 'src/part_iterator.cpp',
'src/frame.cpp', 'src/frame.cpp',
'src/country_code.cpp', 'src/country_code.cpp',
'src/alignment.cpp',
install: true, install: true,
include_directories: [private_incl, library_incl], include_directories: [private_incl, library_incl, duckhandy_incl],
) )
memcard_dep = declare_dependency( memcard_dep = declare_dependency(
include_directories: library_incl, include_directories: [library_incl, duckhandy_incl],
link_with: memcard link_with: memcard
) )

View file

@ -0,0 +1,54 @@
/* Copyright 2020-2025, Michele Santullo
* This file is part of memoserv.
*
* Memoserv is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Memoserv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Memoserv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "memcard/alignment.hpp"
#include "memcard/block.hpp"
#include "duckhandy/packed_pointer.hpp"
namespace mc::psx {
std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) {
return reinterpret_cast<std::uint8_t*>(
(reinterpret_cast<std::uintptr_t>(ptr) + (Block::DataAlignment-1)) & ~(Block::DataAlignment-1)
) + base_offs + index * Block::BlockByteSize;
}
const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) {
return const_cast<const std::uint8_t*>(
to_block_aligned(const_cast<std::uint8_t*>(ptr), index, base_offs)
);
}
bool is_block_aligned (const std::uint8_t* ptr) {
return (0 == reinterpret_cast<std::uintptr_t>(ptr) % Block::DataAlignment);
}
template <typename T, std::size_t A>
bool is_block_aligned (const dhandy::PackedPointer<T, std::size_t, A>& ptr) {
return (0 == static_cast<std::uintptr_t>(ptr) % Block::DataAlignment);
}
template bool is_block_aligned<std::uint8_t, Block::DataAlignment> (
const dhandy::PackedPointer<std::uint8_t, std::size_t, Block::DataAlignment>&
);
template bool is_block_aligned<const std::uint8_t, Block::DataAlignment> (
const dhandy::PackedPointer<const std::uint8_t, std::size_t, Block::DataAlignment>&
);
std::unique_ptr<std::uint8_t[]> make_unique_for_block_aligned(std::size_t size) {
return std::make_unique<std::uint8_t[]>(size + Block::DataAlignment - 1);
}
} //namespace mc::psx

View file

@ -17,6 +17,7 @@
#include "memcard/block.hpp" #include "memcard/block.hpp"
#include "memcard/memorycard.hpp" #include "memcard/memorycard.hpp"
#include "memcard/alignment.hpp"
#include "shiftjis_to_utf8.hpp" #include "shiftjis_to_utf8.hpp"
#include <cassert> #include <cassert>
#include <iomanip> #include <iomanip>
@ -94,10 +95,12 @@ BasicBlock<Const>::BasicBlock (data_type* blk, std::size_t index) :
template <bool Const> template <bool Const>
BasicBlock<Const>::BasicBlock (data_type* blk, BasicFrame<Const> toc_entry, std::size_t index) : BasicBlock<Const>::BasicBlock (data_type* blk, BasicFrame<Const> toc_entry, std::size_t index) :
m_toc_entry(toc_entry), m_toc_entry(toc_entry),
m_index(index),
m_begin(blk) m_begin(blk)
{ {
assert(m_begin); assert(m_begin);
assert(is_block_aligned(m_begin));
assert(index <= m_begin.max_payload);
dhandy::set<0>(m_begin, index);
} }
template <bool Const> template <bool Const>

View file

@ -16,14 +16,16 @@
*/ */
#include "memcard/frame.hpp" #include "memcard/frame.hpp"
#include "memcard/alignment.hpp"
#include <cassert> #include <cassert>
namespace mc::psx { namespace mc::psx {
template <bool Const> template <bool Const>
BasicFrame<Const>::BasicFrame (data_type* data, std::size_t index) : BasicFrame<Const>::BasicFrame (data_type* data, std::size_t index) :
m_index(index),
m_data(data) m_data(data)
{ {
assert(is_block_aligned(m_data));
dhandy::set<0>(m_data, index);
} }
template <bool Const> template <bool Const>
@ -41,7 +43,6 @@ auto BasicFrame<Const>::operator[] (std::size_t index) const -> const data_type&
template <bool Const> template <bool Const>
template <bool Const2> template <bool Const2>
BasicFrame<Const>& BasicFrame<Const>::operator= (const BasicFrame<Const2>& other) { BasicFrame<Const>& BasicFrame<Const>::operator= (const BasicFrame<Const2>& other) {
m_index = other.m_index;
m_data = other.m_data; m_data = other.m_data;
return *this; return *this;
} }

View file

@ -18,6 +18,7 @@
#include "memcard/memorycard.hpp" #include "memcard/memorycard.hpp"
#include "memcard/block.hpp" #include "memcard/block.hpp"
#include "memcard/frame.hpp" #include "memcard/frame.hpp"
#include "memcard/alignment.hpp"
#include <iterator> #include <iterator>
#include <cassert> #include <cassert>
@ -39,7 +40,7 @@ namespace {
} //unnamed namespace } //unnamed namespace
MemoryCard::MemoryCard() : MemoryCard::MemoryCard() :
m_data(MemoryCardSize) m_data(MemoryCardSize + Block::DataAlignment - 1)
{ {
} }
@ -130,4 +131,8 @@ std::vector<std::size_t> block_group (const MemoryCard& mc, std::size_t index) {
} }
return {}; return {};
} }
const std::uint8_t* MemoryCard::data_ptr() const {
return to_block_aligned(m_data.data());
}
} //namespace mc::psx } //namespace mc::psx