Skip to content
Snippets Groups Projects
memory.h 8.93 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vicki Pfau's avatar
    Vicki Pfau committed
    #pragma once
    
    #include "gtest/gtest.h"
    
    #include <functional>
    #include <map>
    #include <string>
    
    #include <fcntl.h>
    
    #ifndef _WIN32
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    #include <sys/mman.h>
    
    #else
    #include <windows.h>
    #endif
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    #include <unistd.h>
    
    namespace Retro {
    
    template<typename T = uint8_t>
    class MemoryView {
    public:
    	MemoryView() {}
    	MemoryView(const MemoryView<T>&) = delete;
    	~MemoryView();
    
    	bool open(const std::string& file, size_t bytes = 0);
    	void open(void* buffer, size_t bytes);
    	void open(size_t bytes);
    	void open(std::initializer_list<T>);
    	void close();
    
    	bool ok() const;
    	void clone(const void* buffer, size_t bytes);
    	void clone(const MemoryView<T>&);
    	void clone();
    
    	T& operator[](size_t);
    	const T& operator[](size_t) const;
    	MemoryView<T>& operator=(MemoryView<T>&&);
    
    	void* offset(size_t);
    	const void* offset(size_t) const;
    
    	size_t size() const;
    
    private:
    	T* m_buffer = nullptr;
    	int m_backingFd = -1;
    	bool m_managed = false;
    	size_t m_size = 0;
    
    #ifdef _WIN32
    	HANDLE m_mapView;
    #endif
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    };
    
    template<typename T>
    MemoryView<T>::~MemoryView() {
    	close();
    }
    
    template<typename T>
    bool MemoryView<T>::open(const std::string& file, size_t bytes) {
    	if (ok()) {
    		close();
    	}
    	int flags = O_RDWR;
    	if (bytes) {
    		flags |= O_CREAT;
    	}
    	m_backingFd = ::open(file.c_str(), flags, 0600);
    	if (m_backingFd < 0) {
    		return false;
    	}
    	if (bytes) {
    		ftruncate(m_backingFd, bytes);
    		m_size = bytes;
    	} else {
    		m_size = lseek(m_backingFd, 0, SEEK_END);
    	}
    	m_managed = true;
    
    #ifdef _WIN32
    	m_mapView = CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(m_backingFd)), 0, PAGE_READWRITE, 0, m_size, 0);
    	m_buffer = reinterpret_cast<T*>(static_cast<uint8_t*>(MapViewOfFile(m_mapView, FILE_MAP_WRITE, 0, 0, m_size)));
    #else
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	m_buffer = reinterpret_cast<T*>(static_cast<uint8_t*>(mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_backingFd, 0)));
    
    #endif
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	if (m_buffer == reinterpret_cast<T*>(-1)) {
    		m_buffer = nullptr;
    		m_managed = false;
    		::close(m_backingFd);
    		return false;
    	}
    	return true;
    }
    
    template<typename T>
    void MemoryView<T>::open(void* buffer, size_t bytes) {
    	if (ok()) {
    		close();
    	}
    	m_backingFd = -1;
    	m_size = bytes;
    	m_managed = false;
    	m_buffer = static_cast<T*>(buffer);
    }
    
    template<typename T>
    void MemoryView<T>::open(size_t bytes) {
    	if (ok()) {
    		close();
    	}
    	m_backingFd = -1;
    	m_size = bytes;
    	m_managed = true;
    
    #ifdef _WIN32
    	m_buffer = static_cast<T*>(VirtualAlloc(nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
    #else
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	m_buffer = static_cast<T*>(mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0));
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    }
    
    template<typename T>
    void MemoryView<T>::open(std::initializer_list<T> list) {
    	open(list.size());
    	std::copy(list.begin(), list.end(), m_buffer);
    }
    
    template<typename T>
    void MemoryView<T>::close() {
    	if (!ok()) {
    		return;
    	}
    	if (m_managed) {
    		if (m_buffer) {
    
    #ifdef _WIN32
    			if (m_backingFd >= 0) {
    				UnmapViewOfFile(m_buffer);
    				CloseHandle(m_mapView);
    			} else {
    				VirtualFree(m_buffer, 0, MEM_RELEASE);
    			}
    #else
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    			munmap(m_buffer, m_size);
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    		}
    		if (m_backingFd >= 0) {
    			::close(m_backingFd);
    			m_backingFd = -1;
    		}
    	}
    	m_buffer = nullptr;
    	m_size = 0;
    	m_managed = false;
    }
    
    template<typename T>
    bool MemoryView<T>::ok() const {
    	return m_buffer && m_size;
    }
    
    template<typename T>
    void MemoryView<T>::clone() {
    	if (!ok() || m_managed) {
    		return;
    	}
    
    	clone(static_cast<void*>(m_buffer), m_size);
    }
    
    template<typename T>
    void MemoryView<T>::clone(const void* buffer, size_t bytes) {
    	if (m_managed && bytes == m_size) {
    		memmove(m_buffer, buffer, bytes);
    		return;
    	}
    	if (static_cast<void*>(m_buffer) != buffer || !m_managed) {
    		close();
    	}
    
    
    #ifdef _WIN32
    	T* newBuffer = static_cast<T*>(VirtualAlloc(nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
    #else
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	T* newBuffer = static_cast<T*>(mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0));
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	memcpy(newBuffer, buffer, bytes);
    	m_buffer = newBuffer;
    	m_size = bytes;
    	m_managed = true;
    }
    
    template<typename T>
    void MemoryView<T>::clone(const MemoryView<T>& other) {
    	clone(static_cast<const void*>(other.m_buffer), other.m_size);
    }
    
    template<typename T>
    T& MemoryView<T>::operator[](size_t index) {
    	return m_buffer[index];
    }
    
    template<typename T>
    const T& MemoryView<T>::operator[](size_t index) const {
    	return m_buffer[index];
    }
    
    template<typename T>
    MemoryView<T>& MemoryView<T>::operator=(MemoryView<T>&& other) {
    	close();
    	m_buffer = other.m_buffer;
    	m_backingFd = other.m_backingFd;
    	m_managed = other.m_managed;
    	m_size = other.m_size;
    	other.m_managed = false;
    	return *this;
    }
    
    template<typename T>
    void* MemoryView<T>::offset(size_t index) {
    	return reinterpret_cast<void*>(&m_buffer[index]);
    }
    
    template<typename T>
    const void* MemoryView<T>::offset(size_t index) const {
    	return reinterpret_cast<const void*>(&m_buffer[index]);
    }
    
    template<typename T>
    size_t MemoryView<T>::size() const {
    	return m_size;
    }
    
    enum class Endian : char {
    	BIG = 0b01,
    	LITTLE = 0b10,
    	NATIVE = 0b11,
    	MIXED_BL = 0b1001,
    	MIXED_LB = 0b0110,
    	MIXED_BN = 0b1101,
    	MIXED_LN = 0b1110,
    #if defined(__LITTLE_ENDIAN__) || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    	REAL_NATIVE = LITTLE,
    	REAL_MIXED_BN = MIXED_BL,
    	REAL_MIXED_LN = LITTLE,
    #else
    	REAL_NATIVE = BIG,
    	REAL_MIXED_BN = BIG,
    	REAL_MIXED_LN = MIXED_LN,
    #endif
    	UNDEF = 0
    };
    
    Endian reduce(Endian);
    bool reduceCompare(Endian, Endian);
    
    enum class Repr : char {
    	SIGNED = 'i',
    	UNSIGNED = 'u',
    	BCD = 'd',
    	LN_BCD = 'n'
    };
    
    class Datum;
    class MemoryOverlay;
    class DataType {
    public:
    	DataType(const char*);
    	DataType(const std::string&);
    	DataType(const DataType&) = default;
    
    	Datum operator()(void*) const;
    	Datum operator()(void*, size_t offset, const MemoryOverlay&) const;
    	bool operator==(const DataType&) const;
    	bool operator!=(const DataType&) const;
    
    	void encode(void* buffer, int64_t value) const;
    	int64_t decode(const void* buffer) const;
    
    	const size_t width;
    	const Endian endian;
    	const Repr repr;
    
    	const char type[5];
    
    private:
    	FRIEND_TEST(DataTypeShift, 1);
    	FRIEND_TEST(DataTypeShift, 2);
    	FRIEND_TEST(DataTypeShift, 3);
    	FRIEND_TEST(DataTypeShift, 4);
    	FRIEND_TEST(DataTypeShift, 5);
    	FRIEND_TEST(DataTypeShift, 6);
    	FRIEND_TEST(DataTypeShift, 7);
    	FRIEND_TEST(DataTypeShift, 8);
    
    	const uint8_t maskLo;
    	const uint8_t maskHi;
    	const unsigned cvt;
    	int64_t shift[8]{};
    };
    
    struct Variable {
    	Variable(const DataType&, size_t address, uint64_t mask = UINT64_MAX);
    	Variable(const Variable&) = default;
    	bool operator==(const Variable&) const;
    
    	const DataType type;
    	const size_t address;
    	const uint64_t mask = UINT64_MAX;
    };
    
    class MemoryOverlay {
    public:
    	MemoryOverlay(Endian backing = Endian::NATIVE, Endian real = Endian::NATIVE, size_t width = 1);
    	MemoryOverlay(char backing, char real, size_t width = 1);
    
    	void* parse(const void* in, size_t offset, void* out, size_t size) const;
    	void unparse(void* out, size_t offset, const void* in, size_t size) const;
    
    	const size_t width;
    
    private:
    	DataType m_backing;
    	DataType m_real;
    };
    
    class Datum {
    public:
    	Datum() {}
    	Datum(void*, const DataType&);
    	Datum(void* base, const Variable&, const MemoryOverlay& overlay = {});
    	Datum(void* base, size_t offset, const DataType&, const MemoryOverlay& overlay = {});
    
    	Datum& operator=(int64_t);
    	operator int64_t() const;
    	bool operator==(int64_t);
    
    private:
    	void* const m_base = nullptr;
    	const size_t m_offset = 0;
    	const DataType m_type{ "|u1" };
    	const uint64_t m_mask = UINT64_MAX;
    	const MemoryOverlay m_overlay{};
    };
    
    class DynamicMemoryView {
    public:
    	DynamicMemoryView(void* buffer, size_t bytes, const DataType&, const MemoryOverlay& = {});
    
    	Datum operator[](size_t);
    	int64_t operator[](size_t) const;
    
    	const DataType dtype;
    	const MemoryOverlay overlay;
    
    private:
    	MemoryView<> m_mem;
    };
    
    class AddressSpace {
    public:
    	void addBlock(size_t offset, size_t size, void* data = nullptr);
    	void addBlock(size_t offset, size_t size, const void* data);
    	void addBlock(size_t offset, const MemoryView<>& base);
    	void updateBlock(size_t offset, void* data);
    	void updateBlock(size_t offset, const void* data);
    	void updateBlock(size_t offset, const MemoryView<>& base);
    
    	bool hasBlock(size_t offset) const;
    	const MemoryView<>& block(size_t offset) const;
    	MemoryView<>& block(size_t offset);
    
    	const std::map<size_t, MemoryView<>>& blocks() const { return m_blocks; }
    	std::map<size_t, MemoryView<>>& blocks() { return m_blocks; }
    
    	bool ok() const;
    	void reset();
    	void clone(const AddressSpace&);
    	void clone();
    
    	void setOverlay(const MemoryOverlay& overlay);
    	const MemoryOverlay& overlay() const { return *m_overlay; };
    
    	Datum operator[](size_t);
    	Datum operator[](const Variable&);
    	uint8_t operator[](size_t) const;
    	int64_t operator[](const Variable&) const;
    
    	AddressSpace& operator=(AddressSpace&&);
    
    private:
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	static const DataType s_type;
    	;
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	std::map<size_t, MemoryView<>> m_blocks;
    	std::unique_ptr<MemoryOverlay> m_overlay = std::make_unique<MemoryOverlay>();
    };
    
    int64_t toBcd(int64_t);
    int64_t toLNBcd(int64_t);
    bool isBcd(uint64_t);
    }
    
    namespace std {
    template<>
    struct hash<Retro::DataType> {
    	size_t operator()(const Retro::DataType&) const;
    };
    }