Code cleanup: moving poly/ into xenia/base/

This commit is contained in:
Ben Vanik
2015-05-02 03:42:51 -07:00
parent 99816056be
commit e3220f7ae6
223 changed files with 1758 additions and 1881 deletions

25
src/xenia/base/README.md Normal file
View File

@@ -0,0 +1,25 @@
A lightweight cross-platform/compiler compatibility library.
This library presupposes C++11/14 support. As more compilers get C++14 it will
assume that.
Other parts of the project use this to avoid creating spaghetti linkage. Code
specific to the emulator should be kept out, as not all of the projects that
depend on this need it.
Where possible, C++11/14 STL should be used instead of adding any code in here,
and the code should be kept as small as possible (by reusing STL/etc). Third
party dependencies should be kept to a minimum.
Target compilers:
* MSVC++ 2013+
* Clang 3.4+
* GCC 4.8+.
Target platforms:
* Windows 8+ (`_win.cc` suffix)
* Mac OSX 10.9+ (`_mac.cc` suffix, falling back to `_posix.cc`)
* Linux ? (`_posix.cc` suffix)
Avoid the use of platform-specific #ifdefs and instead try to put all
platform-specific code in the appropriately suffixed cc files.

103
src/xenia/base/arena.cc Normal file
View File

@@ -0,0 +1,103 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/arena.h"
#include <memory>
#include "xenia/base/assert.h"
namespace xe {
Arena::Arena(size_t chunk_size)
: chunk_size_(chunk_size), head_chunk_(nullptr), active_chunk_(nullptr) {}
Arena::~Arena() {
Reset();
Chunk* chunk = head_chunk_;
while (chunk) {
Chunk* next = chunk->next;
delete chunk;
chunk = next;
}
head_chunk_ = nullptr;
}
void Arena::Reset() {
active_chunk_ = head_chunk_;
if (active_chunk_) {
active_chunk_->offset = 0;
}
}
void Arena::DebugFill() {
auto chunk = head_chunk_;
while (chunk) {
std::memset(chunk->buffer, 0xCD, chunk->capacity);
chunk = chunk->next;
}
}
void* Arena::Alloc(size_t size) {
if (active_chunk_) {
if (active_chunk_->capacity - active_chunk_->offset < size + 4096) {
Chunk* next = active_chunk_->next;
if (!next) {
assert_true(size < chunk_size_, "need to support larger chunks");
next = new Chunk(chunk_size_);
active_chunk_->next = next;
}
next->offset = 0;
active_chunk_ = next;
}
} else {
head_chunk_ = active_chunk_ = new Chunk(chunk_size_);
}
uint8_t* p = active_chunk_->buffer + active_chunk_->offset;
active_chunk_->offset += size;
return p;
}
void* Arena::CloneContents() {
size_t total_length = 0;
Chunk* chunk = head_chunk_;
while (chunk) {
total_length += chunk->offset;
if (chunk == active_chunk_) {
break;
}
chunk = chunk->next;
}
void* result = malloc(total_length);
uint8_t* p = (uint8_t*)result;
chunk = head_chunk_;
while (chunk) {
std::memcpy(p, chunk->buffer, chunk->offset);
p += chunk->offset;
if (chunk == active_chunk_) {
break;
}
chunk = chunk->next;
}
return result;
}
Arena::Chunk::Chunk(size_t chunk_size)
: next(nullptr), capacity(chunk_size), buffer(0), offset(0) {
buffer = reinterpret_cast<uint8_t*>(malloc(capacity));
}
Arena::Chunk::~Chunk() {
if (buffer) {
free(buffer);
}
}
} // namespace xe

55
src/xenia/base/arena.h Normal file
View File

@@ -0,0 +1,55 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_ARENA_H_
#define XENIA_BASE_ARENA_H_
#include <cstddef>
#include <cstdint>
namespace xe {
class Arena {
public:
Arena(size_t chunk_size = 4 * 1024 * 1024);
~Arena();
void Reset();
void DebugFill();
void* Alloc(size_t size);
template <typename T>
T* Alloc() {
return reinterpret_cast<T*>(Alloc(sizeof(T)));
}
void* CloneContents();
private:
class Chunk {
public:
Chunk(size_t chunk_size);
~Chunk();
Chunk* next;
size_t capacity;
uint8_t* buffer;
size_t offset;
};
private:
size_t chunk_size_;
Chunk* head_chunk_;
Chunk* active_chunk_;
};
} // namespace xe
#endif // XENIA_BASE_ARENA_H_

77
src/xenia/base/assert.h Normal file
View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_ASSERT_H_
#define XENIA_BASE_ASSERT_H_
#include <assert.h>
#include "xenia/base/platform.h"
namespace xe {
#define static_assert_size(type, size) \
static_assert(sizeof(type) == size, \
"bad definition for " #type ": must be " #size " bytes")
// We rely on assert being compiled out in NDEBUG.
#define xenia_assert assert
#define __XENIA_EXPAND(x) x
#define __XENIA_ARGC(...) \
__XENIA_EXPAND(__XENIA_ARGC_IMPL(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, \
7, 6, 5, 4, 3, 2, 1, 0))
#define __XENIA_ARGC_IMPL(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, N, ...) \
N
#define __XENIA_MACRO_DISPATCH(func, ...) \
__XENIA_MACRO_DISPATCH_(func, __XENIA_ARGC(__VA_ARGS__))
#define __XENIA_MACRO_DISPATCH_(func, nargs) \
__XENIA_MACRO_DISPATCH__(func, nargs)
#define __XENIA_MACRO_DISPATCH__(func, nargs) func##nargs
#define assert_always(...) xenia_assert(false)
#define assert_true(...) \
__XENIA_MACRO_DISPATCH(assert_true, __VA_ARGS__)(__VA_ARGS__)
#define assert_true1(expr) xenia_assert(expr)
#define assert_true2(expr, message) xenia_assert((expr) || !message)
#define assert_false(...) \
__XENIA_MACRO_DISPATCH(assert_false, __VA_ARGS__)(__VA_ARGS__)
#define assert_false1(expr) xenia_assert(!(expr))
#define assert_false2(expr, message) xenia_assert(!(expr) || !message)
#define assert_zero(...) \
__XENIA_MACRO_DISPATCH(assert_zero, __VA_ARGS__)(__VA_ARGS__)
#define assert_zero1(expr) xenia_assert((expr) == 0)
#define assert_zero2(expr, message) xenia_assert((expr) == 0 || !message)
#define assert_not_zero(...) \
__XENIA_MACRO_DISPATCH(assert_not_zero, __VA_ARGS__)(__VA_ARGS__)
#define assert_not_zero1(expr) xenia_assert((expr) != 0)
#define assert_not_zero2(expr, message) xenia_assert((expr) != 0 || !message)
#define assert_null(...) \
__XENIA_MACRO_DISPATCH(assert_null, __VA_ARGS__)(__VA_ARGS__)
#define assert_null1(expr) xenia_assert((expr) == nullptr)
#define assert_null2(expr, message) xenia_assert((expr) == nullptr || !message)
#define assert_not_null(...) \
__XENIA_MACRO_DISPATCH(assert_not_null, __VA_ARGS__)(__VA_ARGS__)
#define assert_not_null1(expr) xenia_assert((expr) != nullptr)
#define assert_not_null2(expr, message) \
xenia_assert((expr) != nullptr || !message)
#define assert_unhandled_case(variable) \
assert_always("unhandled switch(" #variable ") case")
} // namespace xe
#endif // XENIA_BASE_ASSERT_H_

184
src/xenia/base/atomic.h Normal file
View File

@@ -0,0 +1,184 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_ATOMIC_H_
#define XENIA_BASE_ATOMIC_H_
#include <cstdint>
#include "xenia/base/platform.h"
#if XE_PLATFORM_MAC
#include <libkern/OSAtomic.h>
#endif // XE_PLATFORM_MAC
namespace xe {
// These functions are modeled off of the Apple OSAtomic routines
// http://developer.apple.com/library/mac/#documentation/DriversKernelHardware/Reference/libkern_ref/OSAtomic_h/
#if XE_PLATFORM_MAC
inline int32_t atomic_inc(volatile int32_t* value) {
return OSAtomicIncrement32Barrier(reinterpret_cast<volatile int32_t*>(value));
}
inline int32_t atomic_dec(volatile int32_t* value) {
return OSAtomicDecrement32Barrier(reinterpret_cast<volatile int32_t*>(value));
}
inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) {
return OSAtomicCompareAndSwap32Barrier(*value, new_value, value);
}
inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
return OSAtomicCompareAndSwap64Barrier(*value, new_value, value);
}
inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
return OSAtomicAdd32Barrier(amount, value) - amount;
}
inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
return OSAtomicAdd64Barrier(amount, value) - amount;
}
inline bool atomic_cas(int32_t old_value, int32_t new_value,
volatile int32_t* value) {
return OSAtomicCompareAndSwap32Barrier(
old_value, new_value, reinterpret_cast<volatile int32_t*>(value));
}
inline bool atomic_cas(int64_t old_value, int64_t new_value,
volatile int64_t* value) {
return OSAtomicCompareAndSwap64Barrier(
old_value, new_value, reinterpret_cast<volatile int64_t*>(value));
}
#elif XE_PLATFORM_WIN32
inline int32_t atomic_inc(volatile int32_t* value) {
return InterlockedIncrement(reinterpret_cast<volatile LONG*>(value));
}
inline int32_t atomic_dec(volatile int32_t* value) {
return InterlockedDecrement(reinterpret_cast<volatile LONG*>(value));
}
inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) {
return InterlockedExchange(reinterpret_cast<volatile LONG*>(value),
new_value);
}
inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
return InterlockedExchange64(reinterpret_cast<volatile LONGLONG*>(value),
new_value);
}
inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
return InterlockedExchangeAdd(reinterpret_cast<volatile LONG*>(value),
amount);
}
inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
return InterlockedExchangeAdd64(reinterpret_cast<volatile LONGLONG*>(value),
amount);
}
inline bool atomic_cas(int32_t old_value, int32_t new_value,
volatile int32_t* value) {
return InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(value),
new_value, old_value) == old_value;
}
inline bool atomic_cas(int64_t old_value, int64_t new_value,
volatile int64_t* value) {
return InterlockedCompareExchange64(reinterpret_cast<volatile LONG64*>(value),
new_value, old_value) == old_value;
}
#elif XE_PLATFORM_LINUX
inline int32_t atomic_inc(volatile int32_t* value) {
return __sync_add_and_fetch(value, 1);
}
inline int32_t atomic_dec(volatile int32_t* value) {
return __sync_sub_and_fetch(value, 1);
}
inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) {
return __sync_val_compare_and_swap(*value, value, new_value);
}
inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
return __sync_val_compare_and_swap(*value, value, new_value);
}
inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
return __sync_fetch_and_add(amount, value);
}
inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
return __sync_fetch_and_add(amount, value);
}
inline bool atomic_cas(int32_t old_value, int32_t new_value,
volatile int32_t* value) {
return __sync_bool_compare_and_swap(
reinterpret_cast<volatile int32_t*>(value), old_value, new_value);
}
inline bool atomic_cas(int64_t old_value, int64_t new_value,
volatile int64_t* value) {
return __sync_bool_compare_and_swap(
reinterpret_cast<volatile int64_t*>(value), old_value, new_value);
}
#else
#error No atomic primitives defined for this platform/cpu combination.
#endif // OSX
inline uint32_t atomic_inc(volatile uint32_t* value) {
return static_cast<uint32_t>(
atomic_inc(reinterpret_cast<volatile int32_t*>(value)));
}
inline uint32_t atomic_dec(volatile uint32_t* value) {
return static_cast<uint32_t>(
atomic_dec(reinterpret_cast<volatile int32_t*>(value)));
}
inline uint32_t atomic_exchange(uint32_t new_value, volatile uint32_t* value) {
return static_cast<uint32_t>(
atomic_exchange(static_cast<int32_t>(new_value),
reinterpret_cast<volatile int32_t*>(value)));
}
inline uint64_t atomic_exchange(uint64_t new_value, volatile uint64_t* value) {
return static_cast<uint64_t>(
atomic_exchange(static_cast<int64_t>(new_value),
reinterpret_cast<volatile int64_t*>(value)));
}
inline uint32_t atomic_exchange_add(uint32_t amount, volatile uint32_t* value) {
return static_cast<uint32_t>(
atomic_exchange_add(static_cast<int32_t>(amount),
reinterpret_cast<volatile int32_t*>(value)));
}
inline uint64_t atomic_exchange_add(uint64_t amount, volatile uint64_t* value) {
return static_cast<uint64_t>(
atomic_exchange_add(static_cast<int64_t>(amount),
reinterpret_cast<volatile int64_t*>(value)));
}
inline bool atomic_cas(uint32_t old_value, uint32_t new_value,
volatile uint32_t* value) {
return atomic_cas(static_cast<int32_t>(old_value),
static_cast<int32_t>(new_value),
reinterpret_cast<volatile int32_t*>(value));
}
inline bool atomic_cas(uint64_t old_value, uint64_t new_value,
volatile uint64_t* value) {
return atomic_cas(static_cast<int64_t>(old_value),
static_cast<int64_t>(new_value),
reinterpret_cast<volatile int64_t*>(value));
}
} // namespace xe
#endif // XENIA_BASE_ATOMIC_H_

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_BYTE_ORDER_H_
#define XENIA_BASE_BYTE_ORDER_H_
#include <cstdint>
#include "xenia/base/platform.h"
#if XE_PLATFORM_MAC
#include <libkern/OSByteOrder.h>
#endif // XE_PLATFORM_MAC
namespace xe {
#if XE_COMPILER_MSVC
#define XENIA_BASE_BYTE_SWAP_16 _byteswap_ushort
#define XENIA_BASE_BYTE_SWAP_32 _byteswap_ulong
#define XENIA_BASE_BYTE_SWAP_64 _byteswap_uint64
#elif XE_PLATFORM_MAC
#define XENIA_BASE_BYTE_SWAP_16 OSSwapInt16
#define XENIA_BASE_BYTE_SWAP_32 OSSwapInt32
#define XENIA_BASE_BYTE_SWAP_64 OSSwapInt64
#else
#define XENIA_BASE_BYTE_SWAP_16 __bswap_16
#define XENIA_BASE_BYTE_SWAP_32 __bswap_32
#define XENIA_BASE_BYTE_SWAP_64 __bswap_64
#endif // XE_COMPILER_MSVC
inline int8_t byte_swap(int8_t value) { return value; }
inline uint8_t byte_swap(uint8_t value) { return value; }
inline int16_t byte_swap(int16_t value) {
return static_cast<int16_t>(
XENIA_BASE_BYTE_SWAP_16(static_cast<int16_t>(value)));
}
inline uint16_t byte_swap(uint16_t value) {
return XENIA_BASE_BYTE_SWAP_16(value);
}
inline uint16_t byte_swap(wchar_t value) {
return static_cast<wchar_t>(XENIA_BASE_BYTE_SWAP_16(value));
}
inline int32_t byte_swap(int32_t value) {
return static_cast<int32_t>(
XENIA_BASE_BYTE_SWAP_32(static_cast<int32_t>(value)));
}
inline uint32_t byte_swap(uint32_t value) {
return XENIA_BASE_BYTE_SWAP_32(value);
}
inline int64_t byte_swap(int64_t value) {
return static_cast<int64_t>(
XENIA_BASE_BYTE_SWAP_64(static_cast<int64_t>(value)));
}
inline uint64_t byte_swap(uint64_t value) {
return XENIA_BASE_BYTE_SWAP_64(value);
}
inline float byte_swap(float value) {
uint32_t temp = byte_swap(*reinterpret_cast<uint32_t *>(&value));
return *reinterpret_cast<float *>(&temp);
}
inline double byte_swap(double value) {
uint64_t temp = byte_swap(*reinterpret_cast<uint64_t *>(&value));
return *reinterpret_cast<double *>(&temp);
}
template <typename T>
struct be {
be() = default;
be(const T &src) : value(xe::byte_swap(src)) {}
be(const be &other) { value = other.value; }
operator T() const { return xe::byte_swap(value); }
T value;
};
} // namespace xe
#endif // XENIA_BASE_BYTE_ORDER_H_

View File

@@ -0,0 +1,51 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_CXX_COMPAT_H_
#define XENIA_BASE_CXX_COMPAT_H_
#include <memory>
#include "xenia/base/platform.h"
// C++11 thread local storage.
// http://en.cppreference.com/w/cpp/language/storage_duration
#if XE_COMPILER_MSVC
// VC++2014 may have this.
#define _ALLOW_KEYWORD_MACROS 1
#define thread_local __declspec(thread)
#elif XE_PLATFORM_MAC
// Clang supports it on OSX but the runtime doesn't.
#define thread_local __thread
#endif // XE_COMPILER_MSVC
// C++11 alignas keyword.
// This will hopefully be coming soon, as most of the alignment spec is in the
// latest CTP.
#if XE_COMPILER_MSVC
#define alignas(N) __declspec(align(N))
#endif // XE_COMPILER_MSVC
#if !XE_COMPILER_MSVC
// C++1y make_unique.
// http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/
// This is present in clang with -std=c++1y, but not otherwise.
#if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 4)
namespace std {
template <typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args) {
return unique_ptr<T>(new T(forward<Args>(args)...));
}
} // namespace std
#endif // clang < 3.4
#endif // !XE_COMPILER_MSVC
namespace xe {} // namespace xe
#endif // XENIA_BASE_CXX_COMPAT_H_

View File

@@ -0,0 +1,31 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_DEBUGGING_H_
#define XENIA_BASE_DEBUGGING_H_
#include <cstdint>
namespace xe {
namespace debugging {
// Returns true if a debugger is attached to this process.
// The state may change at any time (attach after launch, etc), so do not
// cache this value. Determining if the debugger is attached is expensive,
// though, so avoid calling it frequently.
bool IsDebuggerAttached();
// Breaks into the debugger if it is attached.
// If no debugger is present, a signal will be raised.
void Break();
} // namespace debugging
} // namespace xe
#endif // XENIA_BASE_DEBUGGING_H_

View File

@@ -0,0 +1,35 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/debugging.h"
#include <sys/sysctl.h>
#include <unistd.h>
namespace xe {
namespace debugging {
bool IsDebuggerAttached() {
// https://developer.apple.com/library/mac/qa/qa1361/_index.html
kinfo_proc info;
info.kp_proc.p_flag = 0;
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
size_t size = sizeof(info);
sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
// TODO(benvanik): find a more reliable way.
void Break() {
// __asm__("int $3");
__builtin_debugtrap();
}
} // namespace debugging
} // namespace xe

View File

@@ -0,0 +1,22 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/debugging.h"
#include <Windows.h>
namespace xe {
namespace debugging {
bool IsDebuggerAttached() { return IsDebuggerPresent() ? true : false; }
void Break() { __debugbreak(); }
} // namespace debugging
} // namespace xe

50
src/xenia/base/delegate.h Normal file
View File

@@ -0,0 +1,50 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_DELEGATE_H_
#define XENIA_BASE_DELEGATE_H_
#include <functional>
#include <mutex>
#include <vector>
namespace xe {
// TODO(benvanik): go lockfree, and don't hold the lock while emitting.
template <typename... Args>
class Delegate {
public:
typedef std::function<void(Args&...)> Listener;
void AddListener(Listener const& listener) {
std::lock_guard<std::mutex> guard(lock_);
listeners_.push_back(listener);
}
void RemoveAllListeners() {
std::lock_guard<std::mutex> guard(lock_);
listeners_.clear();
}
void operator()(Args&... args) {
std::lock_guard<std::mutex> guard(lock_);
for (auto& listener : listeners_) {
listener(args...);
}
}
private:
std::mutex lock_;
std::vector<Listener> listeners_;
};
} // namespace xe
#endif // XENIA_BASE_DELEGATE_H_

182
src/xenia/base/fs.cc Normal file
View File

@@ -0,0 +1,182 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/fs.h"
#include <string>
#include <vector>
#include <algorithm>
namespace xe {
namespace fs {
std::string CanonicalizePath(const std::string& original_path) {
char path_sep('\\');
std::string path(xe::fix_path_separators(original_path, path_sep));
std::vector<std::string::size_type> path_breaks;
std::string::size_type pos(path.find_first_of(path_sep));
std::string::size_type pos_n(std::string::npos);
while (pos != std::string::npos) {
if ((pos_n = path.find_first_of(path_sep, pos + 1)) == std::string::npos) {
pos_n = path.size();
}
auto diff(pos_n - pos);
switch (diff) {
case 0:
pos_n = std::string::npos;
break;
case 1:
// Duplicate separators
path.erase(pos, 1);
pos_n -= 1;
break;
case 2:
// Potential marker for current directory
if (path[pos + 1] == '.') {
path.erase(pos, 2);
pos_n -= 2;
} else {
path_breaks.push_back(pos);
}
break;
case 3:
// Potential marker for parent directory
if (path[pos + 1] == '.' && path[pos + 2] == '.') {
if (path_breaks.empty()) {
// Ensure we don't override the device name
std::string::size_type loc(path.find_first_of(':'));
auto req(pos + 3);
if (loc == std::string::npos || loc > req) {
path.erase(0, req);
pos_n -= req;
} else {
path.erase(loc + 1, req - (loc + 1));
pos_n -= req - (loc + 1);
}
} else {
auto last(path_breaks.back());
auto last_diff((pos + 3) - last);
path.erase(last, last_diff);
pos_n = last;
// Also remove path reference
path_breaks.erase(path_breaks.end() - 1);
}
} else {
path_breaks.push_back(pos);
}
break;
default:
path_breaks.push_back(pos);
break;
}
pos = pos_n;
}
// Remove trailing seperator
if (!path.empty() && path.back() == path_sep) {
path.erase(path.size() - 1);
}
// Final sanity check for dead paths
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) ||
(path.size() == 2 && path[0] == '.' && path[1] == '.')) {
return "";
}
return path;
}
WildcardFlags WildcardFlags::FIRST(true, false);
WildcardFlags WildcardFlags::LAST(false, true);
WildcardFlags::WildcardFlags() : FromStart(false), ToEnd(false) {}
WildcardFlags::WildcardFlags(bool start, bool end)
: FromStart(start), ToEnd(end) {}
WildcardRule::WildcardRule(const std::string& str_match,
const WildcardFlags& flags)
: match(str_match), rules(flags) {
std::transform(match.begin(), match.end(), match.begin(), tolower);
}
bool WildcardRule::Check(const std::string& str_lower,
std::string::size_type& offset) const {
if (match.empty()) {
return true;
}
if ((str_lower.size() - offset) < match.size()) {
return false;
}
std::string::size_type result(str_lower.find(match, offset));
if (result != std::string::npos) {
if (rules.FromStart && result != offset) {
return false;
}
if (rules.ToEnd && result != (str_lower.size() - match.size())) {
return false;
}
offset = (result + match.size());
return true;
}
return false;
}
void WildcardEngine::PreparePattern(const std::string& pattern) {
rules.clear();
WildcardFlags flags(WildcardFlags::FIRST);
size_t n = 0;
size_t last = 0;
while ((n = pattern.find_first_of('*', last)) != pattern.npos) {
if (last != n) {
std::string str_str(pattern.substr(last, n - last));
rules.push_back(WildcardRule(str_str, flags));
}
last = n + 1;
flags = WildcardFlags();
}
if (last != pattern.size()) {
std::string str_str(pattern.substr(last));
rules.push_back(WildcardRule(str_str, WildcardFlags::LAST));
}
}
void WildcardEngine::SetRule(const std::string& pattern) {
PreparePattern(pattern);
}
bool WildcardEngine::Match(const std::string& str) const {
std::string str_lc;
std::transform(str.begin(), str.end(), std::back_inserter(str_lc), tolower);
std::string::size_type offset(0);
for (const auto& rule : rules) {
if (!(rule.Check(str_lc, offset))) {
return false;
}
}
return true;
}
} // namespace fs
} // namespace xe

79
src/xenia/base/fs.h Normal file
View File

@@ -0,0 +1,79 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_FS_H_
#define XENIA_BASE_FS_H_
#include <string>
#include "xenia/base/string.h"
#include <vector>
#include <iterator>
namespace xe {
namespace fs {
bool PathExists(const std::wstring& path);
bool CreateFolder(const std::wstring& path);
bool DeleteFolder(const std::wstring& path);
struct FileInfo {
enum class Type {
kFile,
kDirectory,
};
Type type;
std::wstring name;
size_t total_size;
};
std::vector<FileInfo> ListFiles(const std::wstring& path);
std::string CanonicalizePath(const std::string& original_path);
class WildcardFlags {
public:
bool FromStart : 1, ToEnd : 1;
WildcardFlags();
WildcardFlags(bool start, bool end);
static WildcardFlags FIRST;
static WildcardFlags LAST;
};
class WildcardRule {
public:
WildcardRule(const std::string& str_match, const WildcardFlags& flags);
bool Check(const std::string& str_lower,
std::string::size_type& offset) const;
private:
std::string match;
WildcardFlags rules;
};
class WildcardEngine {
public:
void SetRule(const std::string& pattern);
// Always ignoring case
bool Match(const std::string& str) const;
private:
std::vector<WildcardRule> rules;
void PreparePattern(const std::string& pattern);
};
} // namespace fs
} // namespace xe
#endif // XENIA_BASE_FS_H_

77
src/xenia/base/fs_win.cc Normal file
View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/fs.h"
#include <shellapi.h>
#include <string>
#include "xenia/base/platform.h"
namespace xe {
namespace fs {
bool PathExists(const std::wstring& path) {
DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES;
}
bool CreateFolder(const std::wstring& path) {
wchar_t folder[MAX_PATH] = {0};
auto end = std::wcschr(path.c_str(), L'\\');
while (end) {
wcsncpy(folder, path.c_str(), end - path.c_str() + 1);
CreateDirectory(folder, NULL);
end = wcschr(++end, L'\\');
}
return PathExists(path);
}
bool DeleteFolder(const std::wstring& path) {
auto double_null_path = path + L"\0";
SHFILEOPSTRUCT op = {0};
op.wFunc = FO_DELETE;
op.pFrom = double_null_path.c_str();
op.fFlags = FOF_NO_UI;
return SHFileOperation(&op) == 0;
}
std::vector<FileInfo> ListFiles(const std::wstring& path) {
std::vector<FileInfo> result;
WIN32_FIND_DATA ffd;
HANDLE handle = FindFirstFile((path + L"\\*").c_str(), &ffd);
if (handle == INVALID_HANDLE_VALUE) {
return result;
}
do {
if (std::wcscmp(ffd.cFileName, L".") == 0 ||
std::wcscmp(ffd.cFileName, L"..") == 0) {
continue;
}
FileInfo info;
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
info.type = FileInfo::Type::kDirectory;
info.total_size = 0;
} else {
info.type = FileInfo::Type::kFile;
info.total_size =
(ffd.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + ffd.nFileSizeLow;
}
info.name = ffd.cFileName;
result.push_back(info);
} while (FindNextFile(handle, &ffd) != 0);
FindClose(handle);
return result;
}
} // namespace fs
} // namespace xe

133
src/xenia/base/logging.cc Normal file
View File

@@ -0,0 +1,133 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/logging.h"
#include <gflags/gflags.h>
#include <mutex>
#include "xenia/base/cxx_compat.h"
#include "xenia/base/main.h"
#include "xenia/base/math.h"
#include "xenia/base/threading.h"
DEFINE_bool(fast_stdout, false,
"Don't lock around stdout/stderr. May introduce weirdness.");
DEFINE_bool(flush_stdout, true, "Flush stdout after each log line.");
DEFINE_bool(log_filenames, false,
"Log filenames/line numbers in log statements.");
namespace xe {
std::mutex log_lock;
void format_log_line(char* buffer, size_t buffer_count, const char* file_path,
const uint32_t line_number, const char level_char,
const char* fmt, va_list args) {
char* buffer_ptr;
if (FLAGS_log_filenames) {
// Strip out just the filename from the path.
const char* filename = strrchr(file_path, xe::path_separator);
if (filename) {
// Slash - skip over it.
filename++;
} else {
// No slash, entire thing is filename.
filename = file_path;
}
// Format string - add a trailing newline if required.
const char* outfmt = "%c> %.2X %s:%d: ";
buffer_ptr = buffer + snprintf(buffer, buffer_count - 1, outfmt, level_char,
xe::threading::current_thread_id(), filename,
line_number);
} else {
buffer_ptr = buffer;
*(buffer_ptr++) = level_char;
*(buffer_ptr++) = '>';
*(buffer_ptr++) = ' ';
buffer_ptr +=
sprintf(buffer_ptr, "%.4X", xe::threading::current_thread_id());
*(buffer_ptr++) = ' ';
}
// Scribble args into the print buffer.
buffer_ptr = buffer_ptr + vsnprintf(buffer_ptr,
buffer_count - (buffer_ptr - buffer) - 1,
fmt, args);
// Add a trailing newline.
if (buffer_ptr[-1] != '\n') {
buffer_ptr[0] = '\n';
buffer_ptr[1] = 0;
}
}
thread_local char log_buffer[2048];
void log_line(const char* file_path, const uint32_t line_number,
const char level_char, const char* fmt, ...) {
// SCOPE_profile_cpu_i("emu", "log_line");
va_list args;
va_start(args, fmt);
format_log_line(log_buffer, xe::countof(log_buffer), file_path, line_number,
level_char, fmt, args);
va_end(args);
if (!FLAGS_fast_stdout) {
log_lock.lock();
}
#if 0 // defined(OutputDebugString)
OutputDebugStringA(log_buffer);
#else
fprintf(stdout, "%s", log_buffer);
if (FLAGS_flush_stdout) {
fflush(stdout);
}
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
}
void handle_fatal(const char* file_path, const uint32_t line_number,
const char* fmt, ...) {
char buffer[2048];
va_list args;
va_start(args, fmt);
format_log_line(buffer, xe::countof(buffer), file_path, line_number, 'X', fmt,
args);
va_end(args);
if (!FLAGS_fast_stdout) {
log_lock.lock();
}
#if defined(OutputDebugString)
OutputDebugStringA(buffer);
#else
fprintf(stderr, "%s", buffer);
fflush(stderr);
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) {
MessageBoxA(NULL, buffer, "Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
}
#endif // WIN32
exit(1);
}
} // namespace xe

108
src/xenia/base/logging.h Normal file
View File

@@ -0,0 +1,108 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_LOGGING_H_
#define XENIA_LOGGING_H_
#include <cstdint>
#include "xenia/base/string.h"
namespace xe {
#define XE_OPTION_ENABLE_LOGGING 1
#define XE_OPTION_LOG_ERROR 1
#define XE_OPTION_LOG_WARNING 1
#define XE_OPTION_LOG_INFO 1
#define XE_OPTION_LOG_DEBUG 1
#define XE_OPTION_LOG_CPU 1
#define XE_OPTION_LOG_APU 1
#define XE_OPTION_LOG_GPU 1
#define XE_OPTION_LOG_KERNEL 1
#define XE_OPTION_LOG_FS 1
#define XE_EMPTY_MACRO \
do { \
} while (false)
#if XE_COMPILER_GNUC
#define XE_LOG_LINE_ATTRIBUTE __attribute__((format(printf, 5, 6)))
#else
#define XE_LOG_LINE_ATTRIBUTE
#endif // XE_COMPILER_GNUC
void log_line(const char* file_path, const uint32_t line_number,
const char level_char, const char* fmt,
...) XE_LOG_LINE_ATTRIBUTE;
#undef XE_LOG_LINE_ATTRIBUTE
void handle_fatal(const char* file_path, const uint32_t line_number,
const char* fmt, ...);
#if XE_OPTION_ENABLE_LOGGING
#define XELOGCORE(level, fmt, ...) \
xe::log_line(__FILE__, __LINE__, level, fmt, ##__VA_ARGS__)
#else
#define XELOGCORE(level, fmt, ...) XE_EMPTY_MACRO
#endif // ENABLE_LOGGING
#define XEFATAL(fmt, ...) \
do { \
xe::handle_fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__); \
} while (false)
#if XE_OPTION_LOG_ERROR
#define XELOGE(fmt, ...) XELOGCORE('!', fmt, ##__VA_ARGS__)
#else
#define XELOGE(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_WARNING
#define XELOGW(fmt, ...) XELOGCORE('w', fmt, ##__VA_ARGS__)
#else
#define XELOGW(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_INFO
#define XELOGI(fmt, ...) XELOGCORE('i', fmt, ##__VA_ARGS__)
#else
#define XELOGI(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_DEBUG
#define XELOGD(fmt, ...) XELOGCORE('d', fmt, ##__VA_ARGS__)
#else
#define XELOGD(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_CPU
#define XELOGCPU(fmt, ...) XELOGCORE('C', fmt, ##__VA_ARGS__)
#else
#define XELOGCPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_APU
#define XELOGAPU(fmt, ...) XELOGCORE('A', fmt, ##__VA_ARGS__)
#else
#define XELOGAPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_GPU
#define XELOGGPU(fmt, ...) XELOGCORE('G', fmt, ##__VA_ARGS__)
#else
#define XELOGGPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_KERNEL
#define XELOGKERNEL(fmt, ...) XELOGCORE('K', fmt, ##__VA_ARGS__)
#else
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_FS
#define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__)
#else
#define XELOGFS(fmt, ...) XE_EMPTY_MACRO
#endif
} // namespace xe
#endif // XENIA_LOGGING_H_

39
src/xenia/base/main.h Normal file
View File

@@ -0,0 +1,39 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_MAIN_H_
#define XENIA_BASE_MAIN_H_
#include <string>
#include <vector>
#include "xenia/base/platform.h"
namespace xe {
// Returns true if there is a user-visible console attached to receive stdout.
bool has_console_attached();
// Extern defined by user code. This must be present for the application to
// launch.
struct EntryInfo {
std::wstring name;
std::wstring usage;
int (*entry_point)(std::vector<std::wstring>& args);
};
EntryInfo GetEntryInfo();
#define DEFINE_ENTRY_POINT(name, usage, entry_point) \
xe::EntryInfo xe::GetEntryInfo() { \
return xe::EntryInfo({name, usage, entry_point}); \
}
} // namespace xe
#endif // XENIA_BASE_MAIN_H_

View File

@@ -0,0 +1,40 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/main.h"
#include <gflags/gflags.h>
#include "xenia/base/string.h"
namespace xe {
bool has_console_attached() { return true; }
} // namespace xe
extern "C" int main(int argc, char** argv) {
auto entry_info = xe::GetEntryInfo();
google::SetUsageMessage(std::string("usage: ") +
xe::to_string(entry_info.usage));
google::SetVersionString("1.0");
google::ParseCommandLineFlags(&argc, &argv, true);
std::vector<std::wstring> args;
for (int n = 0; n < argc; n++) {
args.push_back(xe::to_wstring(argv[n]));
}
// Call app-provided entry point.
int result = entry_info.entry_point(args);
google::ShutDownCommandLineFlags();
return result;
}

127
src/xenia/base/main_win.cc Normal file
View File

@@ -0,0 +1,127 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/main.h"
#include <fcntl.h>
#include <io.h>
#include <shellapi.h>
#include <gflags/gflags.h>
#include "xenia/base/string.h"
namespace xe {
bool has_console_attached_ = true;
bool has_console_attached() { return has_console_attached_; }
void AttachConsole() {
bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
if (!has_console) {
// We weren't launched from a console, so just return.
// We could alloc our own console, but meh:
// has_console = AllocConsole() == TRUE;
has_console_attached_ = false;
return;
}
has_console_attached_ = true;
auto std_handle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
auto con_handle = _open_osfhandle(std_handle, _O_TEXT);
auto fp = _fdopen(con_handle, "w");
*stdout = *fp;
setvbuf(stdout, nullptr, _IONBF, 0);
std_handle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
con_handle = _open_osfhandle(std_handle, _O_TEXT);
fp = _fdopen(con_handle, "w");
*stderr = *fp;
setvbuf(stderr, nullptr, _IONBF, 0);
}
} // namespace xe
// Used in console mode apps; automatically picked based on subsystem.
int wmain(int argc, wchar_t* argv[]) {
auto entry_info = xe::GetEntryInfo();
google::SetUsageMessage(std::string("usage: ") +
xe::to_string(entry_info.usage));
google::SetVersionString("1.0");
// Convert all args to narrow, as gflags doesn't support wchar.
int argca = argc;
char** argva = (char**)alloca(sizeof(char*) * argca);
for (int n = 0; n < argca; n++) {
size_t len = wcslen(argv[n]);
argva[n] = (char*)alloca(len + 1);
wcstombs_s(nullptr, argva[n], len + 1, argv[n], _TRUNCATE);
}
// Parse flags; this may delete some of them.
google::ParseCommandLineFlags(&argc, &argva, true);
// Widen all remaining flags and convert to usable strings.
std::vector<std::wstring> args;
for (int n = 0; n < argc; n++) {
args.push_back(xe::to_wstring(argva[n]));
}
// Setup COM on the main thread.
// NOTE: this may fail if COM has already been initialized - that's OK.
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
// Call app-provided entry point.
int result = entry_info.entry_point(args);
google::ShutDownCommandLineFlags();
return result;
}
// Used in windowed apps; automatically picked based on subsystem.
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR command_line, int) {
// Attach a console so we can write output to stdout. If the user hasn't
// redirected output themselves it'll pop up a window.
xe::AttachConsole();
auto entry_info = xe::GetEntryInfo();
// Convert to an argv-like format so we can share code/use gflags.
std::wstring buffer = entry_info.name + L" " + command_line;
int argc;
wchar_t** argv = CommandLineToArgvW(buffer.c_str(), &argc);
if (!argv) {
return 1;
}
// Run normal entry point.
int result = wmain(argc, argv);
LocalFree(argv);
return result;
}
#if defined _M_IX86
#pragma comment( \
linker, \
"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment( \
linker, \
"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment( \
linker, \
"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment( \
linker, \
"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif

View File

@@ -0,0 +1,46 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_MAPPED_MEMORY_H_
#define XENIA_BASE_MAPPED_MEMORY_H_
#include <memory>
#include <string>
namespace xe {
class MappedMemory {
public:
enum class Mode {
kRead,
kReadWrite,
};
virtual ~MappedMemory() = default;
static std::unique_ptr<MappedMemory> Open(const std::wstring& path, Mode mode,
size_t offset = 0,
size_t length = 0);
uint8_t* data() const { return reinterpret_cast<uint8_t*>(data_); }
size_t size() const { return size_; }
protected:
MappedMemory(const std::wstring& path, Mode mode)
: path_(path), mode_(mode), data_(nullptr), size_(0) {}
std::wstring path_;
Mode mode_;
void* data_;
size_t size_;
};
} // namespace xe
#endif // XENIA_BASE_MAPPED_MEMORY_H_

View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/mapped_memory.h"
#include <sys/mman.h>
#include <cstdio>
#include "xenia/base/string.h"
namespace xe {
class PosixMappedMemory : public MappedMemory {
public:
PosixMappedMemory(const std::wstring& path, Mode mode)
: MappedMemory(path, mode), file_handle(nullptr) {}
~PosixMappedMemory() override {
if (data_) {
munmap(data_, size_);
}
if (file_handle) {
fclose(file_handle);
}
}
FILE* file_handle;
};
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
Mode mode, size_t offset,
size_t length) {
const char* mode_str;
int prot;
switch (mode) {
case Mode::READ:
mode_str = "rb";
prot = PROT_READ;
break;
case Mode::READ_WRITE:
mode_str = "r+b";
prot = PROT_READ | PROT_WRITE;
break;
}
auto mm = std::make_unique<PosixMappedMemory>(path, mode);
mm->file_handle = fopen(xe::to_string(path).c_str(), mode_str);
if (!mm->file_handle) {
return nullptr;
}
size_t map_length;
map_length = length;
if (!length) {
fseeko(mm->file_handle, 0, SEEK_END);
map_length = ftello(mm->file_handle);
fseeko(mm->file_handle, 0, SEEK_SET);
}
mm->size_ = map_length;
mm->data_ =
mmap(0, map_length, prot, MAP_SHARED, fileno(mm->file_handle), offset);
if (!mm->data_) {
return nullptr;
}
return std::move(mm);
}
} // namespace xe

View File

@@ -0,0 +1,105 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/mapped_memory.h"
#include <Windows.h>
namespace xe {
class Win32MappedMemory : public MappedMemory {
public:
Win32MappedMemory(const std::wstring& path, Mode mode)
: MappedMemory(path, mode),
file_handle(nullptr),
mapping_handle(nullptr) {}
~Win32MappedMemory() override {
if (data_) {
UnmapViewOfFile(data_);
}
if (mapping_handle) {
CloseHandle(mapping_handle);
}
if (file_handle) {
CloseHandle(file_handle);
}
}
HANDLE file_handle;
HANDLE mapping_handle;
};
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
Mode mode, size_t offset,
size_t length) {
DWORD file_access = 0;
DWORD file_share = 0;
DWORD create_mode = 0;
DWORD mapping_protect = 0;
DWORD view_access = 0;
switch (mode) {
case Mode::kRead:
file_access |= GENERIC_READ;
file_share |= FILE_SHARE_READ;
create_mode |= OPEN_EXISTING;
mapping_protect |= PAGE_READONLY;
view_access |= FILE_MAP_READ;
break;
case Mode::kReadWrite:
file_access |= GENERIC_READ | GENERIC_WRITE;
file_share |= 0;
create_mode |= OPEN_EXISTING;
mapping_protect |= PAGE_READWRITE;
view_access |= FILE_MAP_READ | FILE_MAP_WRITE;
break;
}
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
const size_t aligned_offset =
offset & ~static_cast<size_t>(systemInfo.dwAllocationGranularity - 1);
const size_t aligned_length = length + (offset - aligned_offset);
auto mm = std::make_unique<Win32MappedMemory>(path, mode);
mm->file_handle = CreateFile(path.c_str(), file_access, file_share, nullptr,
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!mm->file_handle) {
return nullptr;
}
mm->mapping_handle = CreateFileMapping(mm->file_handle, nullptr,
mapping_protect, 0, 0, nullptr);
//(DWORD)(aligned_length >> 32), (DWORD)(aligned_length & 0xFFFFFFFF), NULL);
if (!mm->mapping_handle) {
return nullptr;
}
mm->data_ = reinterpret_cast<uint8_t*>(MapViewOfFile(
mm->mapping_handle, view_access, static_cast<DWORD>(aligned_offset >> 32),
static_cast<DWORD>(aligned_offset & 0xFFFFFFFF), aligned_length));
if (!mm->data_) {
return nullptr;
}
if (length) {
mm->size_ = aligned_length;
} else {
DWORD length_high;
size_t map_length = GetFileSize(mm->file_handle, &length_high);
map_length |= static_cast<uint64_t>(length_high) << 32;
mm->size_ = map_length - aligned_offset;
}
return std::move(mm);
}
} // namespace xe

69
src/xenia/base/math.cc Normal file
View File

@@ -0,0 +1,69 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/math.h"
namespace xe {
// TODO(benvanik): replace with alternate implementation.
// XMConvertFloatToHalf
// Copyright (c) Microsoft Corporation. All rights reserved.
uint16_t float_to_half(float value) {
uint32_t Result;
uint32_t IValue = ((uint32_t *)(&value))[0];
uint32_t Sign = (IValue & 0x80000000U) >> 16U;
IValue = IValue & 0x7FFFFFFFU; // Hack off the sign
if (IValue > 0x47FFEFFFU) {
// The number is too large to be represented as a half. Saturate to
// infinity.
Result = 0x7FFFU;
} else {
if (IValue < 0x38800000U) {
// The number is too small to be represented as a normalized half.
// Convert it to a denormalized value.
uint32_t Shift = 113U - (IValue >> 23U);
IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift;
} else {
// Rebias the exponent to represent the value as a normalized half.
IValue += 0xC8000000U;
}
Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU;
}
return (uint16_t)(Result | Sign);
}
// TODO(benvanik): replace with alternate implementation.
// XMConvertHalfToFloat
// Copyright (c) Microsoft Corporation. All rights reserved.
float half_to_float(uint16_t value) {
uint32_t Mantissa = (uint32_t)(value & 0x03FF);
uint32_t Exponent;
if ((value & 0x7C00) != 0) {
// The value is normalized
Exponent = (uint32_t)((value >> 10) & 0x1F);
} else if (Mantissa != 0) {
// The value is denormalized
// Normalize the value in the resulting float
Exponent = 1;
do {
Exponent--;
Mantissa <<= 1;
} while ((Mantissa & 0x0400) == 0);
Mantissa &= 0x03FF;
} else {
// The value is zero
Exponent = (uint32_t)-112;
}
uint32_t Result = ((value & 0x8000) << 16) | // Sign
((Exponent + 112) << 23) | // Exponent
(Mantissa << 13); // Mantissa
return *(float *)&Result;
}
} // namespace xe

225
src/xenia/base/math.h Normal file
View File

@@ -0,0 +1,225 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_MATH_H_
#define XENIA_BASE_MATH_H_
#include <xmmintrin.h>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include "xenia/base/platform.h"
namespace xe {
template <typename T, size_t N>
size_t countof(T (&arr)[N]) {
return std::extent<T[N]>::value;
}
// Rounds up the given value to the given alignment.
template <typename T>
T align(T value, T alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
// Rounds the given number up to the next highest multiple.
template <typename T, typename V>
T round_up(T value, V multiple) {
return value ? (((value + multiple - 1) / multiple) * multiple) : multiple;
}
inline float saturate(float value) {
return std::max(std::min(1.0f, value), -1.0f);
}
// Gets the next power of two value that is greater than or equal to the given
// value.
template <typename T>
T next_pow2(T value) {
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value++;
return value;
}
// lzcnt instruction, typed for integers of all sizes.
// The number of leading zero bits in the value parameter. If value is zero, the
// return value is the size of the input operand (8, 16, 32, or 64). If the most
// significant bit of value is one, the return value is zero.
#if XE_COMPILER_MSVC
#if 1
inline uint8_t lzcnt(uint8_t v) {
return static_cast<uint8_t>(__lzcnt16(v) - 8);
}
inline uint8_t lzcnt(uint16_t v) { return static_cast<uint8_t>(__lzcnt16(v)); }
inline uint8_t lzcnt(uint32_t v) { return static_cast<uint8_t>(__lzcnt(v)); }
inline uint8_t lzcnt(uint64_t v) { return static_cast<uint8_t>(__lzcnt64(v)); }
#else
inline uint8_t lzcnt(uint8_t v) {
DWORD index;
DWORD mask = v;
BOOLEAN is_nonzero = _BitScanReverse(&index, mask);
return static_cast<uint8_t>(is_nonzero ? int8_t(index - 24) ^ 0x7 : 8);
}
inline uint8_t lzcnt(uint16_t v) {
DWORD index;
DWORD mask = v;
BOOLEAN is_nonzero = _BitScanReverse(&index, mask);
return static_cast<uint8_t>(is_nonzero ? int8_t(index - 16) ^ 0xF : 16);
}
inline uint8_t lzcnt(uint32_t v) {
DWORD index;
DWORD mask = v;
BOOLEAN is_nonzero = _BitScanReverse(&index, mask);
return static_cast<uint8_t>(is_nonzero ? int8_t(index) ^ 0x1F : 32);
}
inline uint8_t lzcnt(uint64_t v) {
DWORD index;
DWORD64 mask = v;
BOOLEAN is_nonzero = _BitScanReverse64(&index, mask);
return static_cast<uint8_t>(is_nonzero ? int8_t(index) ^ 0x3F : 64);
}
#endif // LZCNT supported
#else
inline uint8_t lzcnt(uint8_t v) {
return static_cast<uint8_t>(__builtin_clzs(v) - 8);
}
inline uint8_t lzcnt(uint16_t v) {
return static_cast<uint8_t>(__builtin_clzs(v));
}
inline uint8_t lzcnt(uint32_t v) {
return static_cast<uint8_t>(__builtin_clz(v));
}
inline uint8_t lzcnt(uint64_t v) {
return static_cast<uint8_t>(__builtin_clzll(v));
}
#endif // XE_COMPILER_MSVC
inline uint8_t lzcnt(int8_t v) { return lzcnt(static_cast<uint8_t>(v)); }
inline uint8_t lzcnt(int16_t v) { return lzcnt(static_cast<uint16_t>(v)); }
inline uint8_t lzcnt(int32_t v) { return lzcnt(static_cast<uint32_t>(v)); }
inline uint8_t lzcnt(int64_t v) { return lzcnt(static_cast<uint64_t>(v)); }
// BitScanForward (bsf).
// Search the value from least significant bit (LSB) to the most significant bit
// (MSB) for a set bit (1).
// Returns false if no bits are set and the output index is invalid.
#if XE_COMPILER_MSVC
inline bool bit_scan_forward(uint32_t v, uint32_t* out_first_set_index) {
return _BitScanForward(reinterpret_cast<unsigned long*>(out_first_set_index),
v) != 0;
}
inline bool bit_scan_forward(uint64_t v, uint32_t* out_first_set_index) {
return _BitScanForward64(
reinterpret_cast<unsigned long*>(out_first_set_index), v) != 0;
}
#else
inline bool bit_scan_forward(uint32_t v, uint32_t* out_first_set_index) {
int i = ffs(v);
*out_first_set_index = i;
return i != 0;
}
inline bool bit_scan_forward(uint64_t v, uint32_t* out_first_set_index) {
int i = ffsll(v);
*out_first_set_index = i;
return i != 0;
}
#endif // XE_COMPILER_MSVC
inline bool bit_scan_forward(int32_t v, uint32_t* out_first_set_index) {
return bit_scan_forward(static_cast<uint32_t>(v), out_first_set_index);
}
inline bool bit_scan_forward(int64_t v, uint32_t* out_first_set_index) {
return bit_scan_forward(static_cast<uint64_t>(v), out_first_set_index);
}
template <typename T>
inline T log2_floor(T v) {
return sizeof(T) * 8 - 1 - lzcnt(v);
}
template <typename T>
inline T log2_ceil(T v) {
return sizeof(T) * 8 - lzcnt(v - 1);
}
template <typename T>
inline T rotate_left(T v, uint8_t sh) {
return (T(v) << sh) | (T(v) >> ((sizeof(T) * 8) - sh));
}
#if XE_COMPILER_MSVC
template <>
inline uint8_t rotate_left(uint8_t v, uint8_t sh) {
return _rotl8(v, sh);
}
template <>
inline uint16_t rotate_left(uint16_t v, uint8_t sh) {
return _rotl16(v, sh);
}
template <>
inline uint32_t rotate_left(uint32_t v, uint8_t sh) {
return _rotl(v, sh);
}
template <>
inline uint64_t rotate_left(uint64_t v, uint8_t sh) {
return _rotl64(v, sh);
}
#endif // XE_COMPILER_MSVC
// Utilities for SSE values.
template <int N>
float m128_f32(const __m128& v) {
float ret;
_mm_store_ss(&ret, _mm_shuffle_ps(v, v, _MM_SHUFFLE(N, N, N, N)));
return ret;
}
template <int N>
int32_t m128_i32(const __m128& v) {
union {
float f;
int32_t i;
} ret;
_mm_store_ss(&ret.f, _mm_shuffle_ps(v, v, _MM_SHUFFLE(N, N, N, N)));
return ret.i;
}
template <int N>
double m128_f64(const __m128d& v) {
double ret;
_mm_store_sd(&ret, _mm_shuffle_pd(v, v, _MM_SHUFFLE2(N, N)));
return ret;
}
template <int N>
double m128_f64(const __m128& v) {
return m128_f64<N>(_mm_castps_pd(v));
}
template <int N>
int64_t m128_i64(const __m128d& v) {
union {
double f;
int64_t i;
} ret;
_mm_store_sd(&ret.f, _mm_shuffle_pd(v, v, _MM_SHUFFLE2(N, N)));
return ret.i;
}
template <int N>
int64_t m128_i64(const __m128& v) {
return m128_i64<N>(_mm_castps_pd(v));
}
uint16_t float_to_half(float value);
float half_to_float(uint16_t value);
} // namespace xe
#endif // XENIA_BASE_MATH_H_

321
src/xenia/base/memory.h Normal file
View File

@@ -0,0 +1,321 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_MEMORY_H_
#define XENIA_BASE_MEMORY_H_
#include <functional>
#include <string>
#include "xenia/base/assert.h"
#include "xenia/base/byte_order.h"
namespace xe {
inline size_t hash_combine(size_t seed) { return seed; }
template <typename T, typename... Ts>
size_t hash_combine(size_t seed, const T& v, const Ts&... vs) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9E3779B9 + (seed << 6) + (seed >> 2);
return hash_combine(seed, vs...);
}
size_t page_size();
void copy_and_swap_16_aligned(uint16_t* dest, const uint16_t* src,
size_t count);
void copy_and_swap_16_unaligned(uint16_t* dest, const uint16_t* src,
size_t count);
void copy_and_swap_32_aligned(uint32_t* dest, const uint32_t* src,
size_t count);
void copy_and_swap_32_unaligned(uint32_t* dest, const uint32_t* src,
size_t count);
void copy_and_swap_64_aligned(uint64_t* dest, const uint64_t* src,
size_t count);
void copy_and_swap_64_unaligned(uint64_t* dest, const uint64_t* src,
size_t count);
template <typename T>
void copy_and_swap(T* dest, const T* src, size_t count) {
bool is_aligned = reinterpret_cast<uintptr_t>(dest) % 32 == 0 &&
reinterpret_cast<uintptr_t>(src) % 32 == 0;
if (sizeof(T) == 1) {
std::memcpy(dest, src, count);
} else if (sizeof(T) == 2) {
auto ps = reinterpret_cast<const uint16_t*>(src);
auto pd = reinterpret_cast<uint16_t*>(dest);
if (is_aligned) {
copy_and_swap_16_aligned(pd, ps, count);
} else {
copy_and_swap_16_unaligned(pd, ps, count);
}
} else if (sizeof(T) == 4) {
auto ps = reinterpret_cast<const uint32_t*>(src);
auto pd = reinterpret_cast<uint32_t*>(dest);
if (is_aligned) {
copy_and_swap_32_aligned(pd, ps, count);
} else {
copy_and_swap_32_unaligned(pd, ps, count);
}
} else if (sizeof(T) == 8) {
auto ps = reinterpret_cast<const uint64_t*>(src);
auto pd = reinterpret_cast<uint64_t*>(dest);
if (is_aligned) {
copy_and_swap_64_aligned(pd, ps, count);
} else {
copy_and_swap_64_unaligned(pd, ps, count);
}
} else {
assert_always("Invalid xe::copy_and_swap size");
}
}
template <typename T>
T load(const void* mem);
template <>
inline int8_t load<int8_t>(const void* mem) {
return *reinterpret_cast<const int8_t*>(mem);
}
template <>
inline uint8_t load<uint8_t>(const void* mem) {
return *reinterpret_cast<const uint8_t*>(mem);
}
template <>
inline int16_t load<int16_t>(const void* mem) {
return *reinterpret_cast<const int16_t*>(mem);
}
template <>
inline uint16_t load<uint16_t>(const void* mem) {
return *reinterpret_cast<const uint16_t*>(mem);
}
template <>
inline int32_t load<int32_t>(const void* mem) {
return *reinterpret_cast<const int32_t*>(mem);
}
template <>
inline uint32_t load<uint32_t>(const void* mem) {
return *reinterpret_cast<const uint32_t*>(mem);
}
template <>
inline int64_t load<int64_t>(const void* mem) {
return *reinterpret_cast<const int64_t*>(mem);
}
template <>
inline uint64_t load<uint64_t>(const void* mem) {
return *reinterpret_cast<const uint64_t*>(mem);
}
template <>
inline float load<float>(const void* mem) {
return *reinterpret_cast<const float*>(mem);
}
template <>
inline double load<double>(const void* mem) {
return *reinterpret_cast<const double*>(mem);
}
template <typename T>
inline T load(const void* mem) {
if (sizeof(T) == 1) {
return static_cast<T>(load<uint8_t>(mem));
} else if (sizeof(T) == 2) {
return static_cast<T>(load<uint16_t>(mem));
} else if (sizeof(T) == 4) {
return static_cast<T>(load<uint32_t>(mem));
} else if (sizeof(T) == 8) {
return static_cast<T>(load<uint64_t>(mem));
} else {
assert_always("Invalid xe::load size");
}
}
template <typename T>
T load_and_swap(const void* mem);
template <>
inline int8_t load_and_swap<int8_t>(const void* mem) {
return *reinterpret_cast<const int8_t*>(mem);
}
template <>
inline uint8_t load_and_swap<uint8_t>(const void* mem) {
return *reinterpret_cast<const uint8_t*>(mem);
}
template <>
inline int16_t load_and_swap<int16_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const int16_t*>(mem));
}
template <>
inline uint16_t load_and_swap<uint16_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const uint16_t*>(mem));
}
template <>
inline int32_t load_and_swap<int32_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const int32_t*>(mem));
}
template <>
inline uint32_t load_and_swap<uint32_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const uint32_t*>(mem));
}
template <>
inline int64_t load_and_swap<int64_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const int64_t*>(mem));
}
template <>
inline uint64_t load_and_swap<uint64_t>(const void* mem) {
return byte_swap(*reinterpret_cast<const uint64_t*>(mem));
}
template <>
inline float load_and_swap<float>(const void* mem) {
return byte_swap(*reinterpret_cast<const float*>(mem));
}
template <>
inline double load_and_swap<double>(const void* mem) {
return byte_swap(*reinterpret_cast<const double*>(mem));
}
template <>
inline std::string load_and_swap<std::string>(const void* mem) {
std::string value;
for (int i = 0;; ++i) {
auto c =
xe::load_and_swap<uint8_t>(reinterpret_cast<const uint8_t*>(mem) + i);
if (!c) {
break;
}
value.push_back(static_cast<char>(c));
}
return value;
}
template <>
inline std::wstring load_and_swap<std::wstring>(const void* mem) {
std::wstring value;
for (int i = 0;; ++i) {
auto c =
xe::load_and_swap<uint16_t>(reinterpret_cast<const uint16_t*>(mem) + i);
if (!c) {
break;
}
value.push_back(static_cast<wchar_t>(c));
}
return value;
}
template <typename T>
void store(void* mem, T value);
template <>
inline void store<int8_t>(void* mem, int8_t value) {
*reinterpret_cast<int8_t*>(mem) = value;
}
template <>
inline void store<uint8_t>(void* mem, uint8_t value) {
*reinterpret_cast<uint8_t*>(mem) = value;
}
template <>
inline void store<int16_t>(void* mem, int16_t value) {
*reinterpret_cast<int16_t*>(mem) = value;
}
template <>
inline void store<uint16_t>(void* mem, uint16_t value) {
*reinterpret_cast<uint16_t*>(mem) = value;
}
template <>
inline void store<int32_t>(void* mem, int32_t value) {
*reinterpret_cast<int32_t*>(mem) = value;
}
template <>
inline void store<uint32_t>(void* mem, uint32_t value) {
*reinterpret_cast<uint32_t*>(mem) = value;
}
template <>
inline void store<int64_t>(void* mem, int64_t value) {
*reinterpret_cast<int64_t*>(mem) = value;
}
template <>
inline void store<uint64_t>(void* mem, uint64_t value) {
*reinterpret_cast<uint64_t*>(mem) = value;
}
template <>
inline void store<float>(void* mem, float value) {
*reinterpret_cast<float*>(mem) = value;
}
template <>
inline void store<double>(void* mem, double value) {
*reinterpret_cast<double*>(mem) = value;
}
template <typename T>
inline void store(const void* mem, T value) {
if (sizeof(T) == 1) {
store<uint8_t>(mem, static_cast<uint8_t>(value));
} else if (sizeof(T) == 2) {
store<uint8_t>(mem, static_cast<uint16_t>(value));
} else if (sizeof(T) == 4) {
store<uint8_t>(mem, static_cast<uint32_t>(value));
} else if (sizeof(T) == 8) {
store<uint8_t>(mem, static_cast<uint64_t>(value));
} else {
assert_always("Invalid xe::store size");
}
}
template <typename T>
void store_and_swap(void* mem, T value);
template <>
inline void store_and_swap<int8_t>(void* mem, int8_t value) {
*reinterpret_cast<int8_t*>(mem) = value;
}
template <>
inline void store_and_swap<uint8_t>(void* mem, uint8_t value) {
*reinterpret_cast<uint8_t*>(mem) = value;
}
template <>
inline void store_and_swap<int16_t>(void* mem, int16_t value) {
*reinterpret_cast<int16_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<uint16_t>(void* mem, uint16_t value) {
*reinterpret_cast<uint16_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<int32_t>(void* mem, int32_t value) {
*reinterpret_cast<int32_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<uint32_t>(void* mem, uint32_t value) {
*reinterpret_cast<uint32_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<int64_t>(void* mem, int64_t value) {
*reinterpret_cast<int64_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<uint64_t>(void* mem, uint64_t value) {
*reinterpret_cast<uint64_t*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<float>(void* mem, float value) {
*reinterpret_cast<float*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<double>(void* mem, double value) {
*reinterpret_cast<double*>(mem) = byte_swap(value);
}
template <>
inline void store_and_swap<std::string>(void* mem, std::string value) {
for (auto i = 0; i < value.size(); ++i) {
xe::store_and_swap<uint8_t>(reinterpret_cast<uint8_t*>(mem) + i, value[i]);
}
}
template <>
inline void store_and_swap<std::wstring>(void* mem, std::wstring value) {
for (auto i = 0; i < value.size(); ++i) {
xe::store_and_swap<uint16_t>(reinterpret_cast<uint16_t*>(mem) + i,
value[i]);
}
}
} // namespace xe
#endif // XENIA_BASE_MEMORY_H_

View File

@@ -0,0 +1,75 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/memory.h"
#include <algorithm>
#if !XE_PLATFORM_WIN32
#include <unistd.h>
#endif // !XE_PLATFORM_WIN32
namespace xe {
size_t page_size() {
static size_t value = 0;
if (!value) {
#if XE_PLATFORM_WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
value = si.dwPageSize;
#else
value = getpagesize();
#endif // XE_PLATFORM_WIN32
}
return value;
}
// TODO(benvanik): fancy AVX versions.
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/cb32b70b79f430456208a2cd521d028e0ece5d5b/entry/volk/kernels/volk/volk_16u_byteswap.h
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/f2bc76cc65ffba51a141950f98e75364e49df874/entry/volk/kernels/volk/volk_32u_byteswap.h
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/2c4c371885c31222362f70a1cd714415d1398021/entry/volk/kernels/volk/volk_64u_byteswap.h
void copy_and_swap_16_aligned(uint16_t* dest, const uint16_t* src,
size_t count) {
return copy_and_swap_16_unaligned(dest, src, count);
}
void copy_and_swap_16_unaligned(uint16_t* dest, const uint16_t* src,
size_t count) {
for (size_t i = 0; i < count; ++i) {
dest[i] = byte_swap(src[i]);
}
}
void copy_and_swap_32_aligned(uint32_t* dest, const uint32_t* src,
size_t count) {
return copy_and_swap_32_unaligned(dest, src, count);
}
void copy_and_swap_32_unaligned(uint32_t* dest, const uint32_t* src,
size_t count) {
for (size_t i = 0; i < count; ++i) {
dest[i] = byte_swap(src[i]);
}
}
void copy_and_swap_64_aligned(uint64_t* dest, const uint64_t* src,
size_t count) {
return copy_and_swap_64_unaligned(dest, src, count);
}
void copy_and_swap_64_unaligned(uint64_t* dest, const uint64_t* src,
size_t count) {
for (size_t i = 0; i < count; ++i) {
dest[i] = byte_swap(src[i]);
}
}
} // namespace xe

76
src/xenia/base/platform.h Normal file
View File

@@ -0,0 +1,76 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_PLATFORM_H_
#define XENIA_BASE_PLATFORM_H_
// NOTE: ordering matters here as sometimes multiple flags are defined on
// certain platforms.
// Great resource on predefined macros: http://predef.sourceforge.net/preos.html
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
#define XE_PLATFORM_MAC 1
#elif defined(WIN32) || defined(_WIN32)
#define XE_PLATFORM_WIN32 1
#else
#define XE_PLATFORM_LINUX 1
#endif
#if defined(__clang__)
#define XE_COMPILER_CLANG 1
#elif defined(__GNUC__)
#define XE_COMPILER_GNUC 1
#elif defined(_MSC_VER)
#define XE_COMPILER_MSVC 1
#elif defined(__MINGW32)
#define XE_COMPILER_MINGW32 1
#elif defined(__INTEL_COMPILER)
#define XE_COMPILER_INTEL 1
#else
#define XE_COMPILER_UNKNOWN 1
#endif
#if XE_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <SDKDDKVer.h>
#include <windows.h>
#include <ObjBase.h>
#undef min
#undef max
#define strdup _strdup
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif // XE_PLATFORM_WIN32
#if XE_COMPILER_MSVC
#include <intrin.h>
#else
#include <x86intrin.h>
#endif // XE_COMPILER_MSVC
namespace xe {
#if XE_PLATFORM_WIN32
const char path_separator = '\\';
const size_t max_path = _MAX_PATH;
#else
const char path_separator = '/';
const size_t max_path = 1024; // PATH_MAX
#endif // XE_PLATFORM_WIN32
} // namespace xe
#endif // XENIA_BASE_PLATFORM_H_

View File

@@ -0,0 +1,43 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_RESET_SCOPE_H_
#define XENIA_BASE_RESET_SCOPE_H_
#include <mutex>
namespace xe {
template <typename T>
class ResetScope {
public:
ResetScope(T* value) : value_(value) {}
~ResetScope() {
if (value_) {
value_->Reset();
}
}
private:
T* value_;
};
template <typename T>
inline ResetScope<T> make_reset_scope(T* value) {
return ResetScope<T>(value);
}
template <typename T>
inline ResetScope<T> make_reset_scope(const std::unique_ptr<T>& value) {
return ResetScope<T>(value.get());
}
} // namespace xe
#endif // XENIA_BASE_RESET_SCOPE_H_

View File

@@ -0,0 +1,65 @@
# Copyright 2014 Ben Vanik. All Rights Reserved.
{
'sources': [
'arena.cc',
'arena.h',
'assert.h',
'atomic.h',
'byte_order.h',
'debugging.h',
'delegate.h',
'cxx_compat.h',
'fs.h',
'fs.cc',
'logging.cc',
'logging.h',
'main.h',
'mapped_memory.h',
'math.cc',
'math.h',
'memory_generic.cc',
'memory.h',
'platform.h',
'reset_scope.h',
'string.cc',
'string.h',
'string_buffer.cc',
'string_buffer.h',
'threading.cc',
'threading.h',
'type_pool.h',
'vec128.h',
],
'conditions': [
['OS == "mac" or OS == "linux"', {
'sources': [
'main_posix.cc',
'mapped_memory_posix.cc',
],
}],
['OS == "linux"', {
'sources': [
'threading_posix.cc',
],
}],
['OS == "mac"', {
'sources': [
'debugging_mac.cc',
'threading_mac.cc',
],
}],
['OS == "win"', {
'sources': [
'debugging_win.cc',
'fs_win.cc',
'main_win.cc',
'mapped_memory_win.cc',
'threading_win.cc',
],
}],
],
'includes': [
],
}

169
src/xenia/base/string.cc Normal file
View File

@@ -0,0 +1,169 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/string.h"
#include <codecvt>
#include <locale>
namespace xe {
std::string to_string(const std::wstring& source) {
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(source);
}
std::wstring to_wstring(const std::string& source) {
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(source);
}
std::string::size_type find_first_of_case(const std::string& target,
const std::string& search) {
const char* str = target.c_str();
while (*str) {
if (!strncasecmp(str, search.c_str(), search.size())) {
break;
}
str++;
}
if (*str) {
return str - target.c_str();
} else {
return std::string::npos;
}
}
std::wstring to_absolute_path(const std::wstring& path) {
#if XE_PLATFORM_WIN32
wchar_t buffer[xe::max_path];
_wfullpath(buffer, path.c_str(), sizeof(buffer) / sizeof(wchar_t));
return buffer;
#else
char buffer[xe::max_path];
realpath(xe::to_string(path).c_str(), buffer);
return xe::to_wstring(buffer);
#endif // XE_PLATFORM_WIN32
}
std::vector<std::string> split_path(const std::string& path) {
std::vector<std::string> parts;
size_t n = 0;
size_t last = 0;
while ((n = path.find_first_of("\\/", last)) != path.npos) {
if (last != n) {
parts.push_back(path.substr(last, n - last));
}
last = n + 1;
}
if (last != path.size()) {
parts.push_back(path.substr(last));
}
return parts;
}
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
wchar_t sep) {
if (!left.size()) {
return right;
} else if (!right.size()) {
return left;
}
if (left[left.size() - 1] == sep) {
return left + right;
} else {
return left + sep + right;
}
}
std::wstring fix_path_separators(const std::wstring& source, wchar_t new_sep) {
// Swap all separators to new_sep.
wchar_t old_sep = new_sep == '\\' ? '/' : '\\';
std::wstring::size_type pos = 0;
std::wstring dest = source;
while ((pos = source.find_first_of(old_sep, pos)) != std::wstring::npos) {
dest[pos] = new_sep;
++pos;
}
// Replace redundant separators.
pos = 0;
while ((pos = dest.find_first_of(new_sep, pos)) != std::wstring::npos) {
if (pos < dest.size() - 1) {
if (dest[pos + 1] == new_sep) {
dest.erase(pos + 1, 1);
}
}
++pos;
}
return dest;
}
std::string fix_path_separators(const std::string& source, char new_sep) {
// Swap all separators to new_sep.
char old_sep = new_sep == '\\' ? '/' : '\\';
std::string::size_type pos = 0;
std::string dest = source;
while ((pos = source.find_first_of(old_sep, pos)) != std::string::npos) {
dest[pos] = new_sep;
++pos;
}
// Replace redundant separators.
pos = 0;
while ((pos = dest.find_first_of(new_sep, pos)) != std::string::npos) {
if (pos < dest.size() - 1) {
if (dest[pos + 1] == new_sep) {
dest.erase(pos + 1, 1);
}
}
++pos;
}
return dest;
}
std::string find_name_from_path(const std::string& path) {
std::string name(path);
if (!path.empty()) {
std::string::size_type from(std::string::npos);
if (path.back() == '\\') {
from = path.size() - 2;
}
auto pos(path.find_last_of('\\', from));
if (pos != std::string::npos) {
if (from == std::string::npos) {
name = path.substr(pos + 1);
} else {
auto len(from - pos);
name = path.substr(pos + 1, len);
}
}
}
return name;
}
std::wstring find_name_from_path(const std::wstring& path) {
std::wstring name(path);
if (!path.empty()) {
std::wstring::size_type from(std::wstring::npos);
if (path.back() == '\\') {
from = path.size() - 2;
}
auto pos(path.find_last_of('\\', from));
if (pos != std::wstring::npos) {
if (from == std::wstring::npos) {
name = path.substr(pos + 1);
} else {
auto len(from - pos);
name = path.substr(pos + 1, len);
}
}
}
return name;
}
} // namespace xe

51
src/xenia/base/string.h Normal file
View File

@@ -0,0 +1,51 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_STRING_H_
#define XENIA_BASE_STRING_H_
#include <cstdio>
#include <string>
#include <vector>
#include "xenia/base/platform.h"
namespace xe {
std::string to_string(const std::wstring& source);
std::wstring to_wstring(const std::string& source);
// find_first_of string, case insensitive.
std::string::size_type find_first_of_case(const std::string& target,
const std::string& search);
// Converts the given path to an absolute path based on cwd.
std::wstring to_absolute_path(const std::wstring& path);
// Splits the given path on any valid path separator and returns all parts.
std::vector<std::string> split_path(const std::string& path);
// Joins two path segments with the given separator.
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
wchar_t sep = xe::path_separator);
// Replaces all path separators with the given value and removes redundant
// separators.
std::wstring fix_path_separators(const std::wstring& source,
wchar_t new_sep = xe::path_separator);
std::string fix_path_separators(const std::string& source,
char new_sep = xe::path_separator);
// Find the top directory name or filename from a path
std::string find_name_from_path(const std::string& path);
std::wstring find_name_from_path(const std::wstring& path);
} // namespace xe
#endif // XENIA_BASE_STRING_H_

View File

@@ -0,0 +1,71 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/string_buffer.h"
#include <algorithm>
#include <cstdarg>
namespace xe {
StringBuffer::StringBuffer(size_t initial_capacity) {
buffer_.reserve(std::max(initial_capacity, static_cast<size_t>(1024)));
}
StringBuffer::~StringBuffer() = default;
void StringBuffer::Reset() { buffer_.resize(0); }
void StringBuffer::Grow(size_t additional_length) {
size_t old_capacity = buffer_.capacity();
if (buffer_.size() + additional_length <= old_capacity) {
return;
}
size_t new_capacity =
std::max(buffer_.size() + additional_length, old_capacity * 2);
buffer_.reserve(new_capacity);
}
void StringBuffer::Append(const std::string& value) {
AppendBytes(reinterpret_cast<const uint8_t*>(value.data()), value.size());
}
void StringBuffer::Append(const char* format, ...) {
va_list args;
va_start(args, format);
AppendVarargs(format, args);
va_end(args);
}
void StringBuffer::AppendVarargs(const char* format, va_list args) {
int length = vsnprintf(nullptr, 0, format, args);
auto offset = buffer_.size();
Grow(length + 1);
buffer_.resize(buffer_.size() + length);
vsnprintf(buffer_.data() + offset, buffer_.capacity(), format, args);
buffer_[buffer_.size()] = 0;
}
void StringBuffer::AppendBytes(const uint8_t* buffer, size_t length) {
auto offset = buffer_.size();
Grow(length + 1);
buffer_.resize(buffer_.size() + length);
memcpy(buffer_.data() + offset, buffer, length);
buffer_[buffer_.size()] = 0;
}
const char* StringBuffer::GetString() const { return buffer_.data(); }
std::string StringBuffer::to_string() {
return std::string(buffer_.data(), buffer_.size());
}
char* StringBuffer::ToString() { return strdup(buffer_.data()); }
} // namespace xe

View File

@@ -0,0 +1,46 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_STRING_BUFFER_H_
#define XENIA_BASE_STRING_BUFFER_H_
#include <cstdint>
#include <string>
#include <vector>
namespace xe {
class StringBuffer {
public:
StringBuffer(size_t initial_capacity = 0);
~StringBuffer();
size_t length() const { return buffer_.size(); }
void Reset();
void Append(const std::string& value);
void Append(const char* format, ...);
void AppendVarargs(const char* format, va_list args);
void AppendBytes(const uint8_t* buffer, size_t length);
const char* GetString() const;
std::string to_string();
char* ToString();
char* EncodeBase64();
private:
void Grow(size_t additional_length);
std::vector<char> buffer_;
};
} // namespace xe
#endif // XENIA_BASE_STRING_BUFFER_H_

View File

@@ -0,0 +1,18 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/threading.h"
namespace xe {
namespace threading {
//
} // namespace threading
} // namespace xe

View File

@@ -0,0 +1,73 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_THREADING_H_
#define XENIA_BASE_THREADING_H_
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
namespace xe {
namespace threading {
class Fence {
public:
Fence() : signaled_(false) {}
void Signal() {
std::unique_lock<std::mutex> lock(mutex_);
signaled_.store(true);
cond_.notify_all();
}
void Wait() {
std::unique_lock<std::mutex> lock(mutex_);
while (!signaled_.load()) {
cond_.wait(lock);
}
}
private:
std::mutex mutex_;
std::condition_variable cond_;
std::atomic<bool> signaled_;
};
// Gets the current high-performance tick count.
uint64_t ticks();
uint64_t ticks_per_second();
// TODO(benvanik): processor info API.
// Gets a stable thread-specific ID, but may not be. Use for informative
// purposes only.
uint32_t current_thread_id();
// Sets the current thread name.
void set_name(const std::string& name);
// Sets the target thread name.
void set_name(std::thread::native_handle_type handle, const std::string& name);
// Yields the current thread to the scheduler. Maybe.
void MaybeYield();
// Sleeps the current thread for at least as long as the given duration.
void Sleep(std::chrono::microseconds duration);
template <typename Rep, typename Period>
void Sleep(std::chrono::duration<Rep, Period> duration) {
Sleep(std::chrono::duration_cast<std::chrono::microseconds>(duration));
}
} // namespace threading
} // namespace xe
#endif // XENIA_BASE_THREADING_H_

View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/threading.h"
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <pthread.h>
#include <time.h>
namespace xe {
namespace threading {
uint64_t ticks() { return mach_absolute_time(); }
uint32_t current_thread_id() {
mach_port_t tid = pthread_mach_thread_np(pthread_self());
return static_cast<uint32_t>(tid);
}
void set_name(const std::string& name) { pthread_setname_np(name.c_str()); }
void set_name(std::thread::native_handle_type handle, const std::string& name) {
// ?
}
void MaybeYield() { pthread_yield_np(); }
void Sleep(std::chrono::microseconds duration) {
timespec rqtp = {duration.count() / 1000000, duration.count() % 1000};
nanosleep(&rqtp, nullptr);
// TODO(benvanik): spin while rmtp >0?
}
} // namespace threading
} // namespace xe

View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/threading.h"
#include <pthread.h>
#include <time.h>
namespace xe {
namespace threading {
// uint64_t ticks() { return mach_absolute_time(); }
// uint32_t current_thread_id() {
// mach_port_t tid = pthread_mach_thread_np(pthread_self());
// return static_cast<uint32_t>(tid);
// }
void set_name(const std::string& name) {
pthread_setname_np(pthread_self(), name.c_str());
}
void set_name(std::thread::native_handle_type handle, const std::string& name) {
pthread_setname_np(pthread_self(), name.c_str());
}
void MaybeYield() { pthread_yield_np(); }
void Sleep(std::chrono::microseconds duration) {
timespec rqtp = {duration.count() / 1000000, duration.count() % 1000};
nanosleep(&rqtp, nullptr);
// TODO(benvanik): spin while rmtp >0?
}
} // namespace threading
} // namespace xe

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/threading.h"
#include "xenia/base/platform.h"
namespace xe {
namespace threading {
uint64_t ticks() {
LARGE_INTEGER counter;
uint64_t time = 0;
if (QueryPerformanceCounter(&counter)) {
time = counter.QuadPart;
}
return time;
}
uint64_t ticks_per_second() {
static LARGE_INTEGER freq = {0};
if (!freq.QuadPart) {
QueryPerformanceFrequency(&freq);
}
return freq.QuadPart;
}
uint32_t current_thread_id() {
return static_cast<uint32_t>(GetCurrentThreadId());
}
// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push, 8)
struct THREADNAME_INFO {
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
};
#pragma pack(pop)
void set_name(DWORD thread_id, const std::string& name) {
if (!IsDebuggerPresent()) {
return;
}
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = thread_id;
info.dwFlags = 0;
__try {
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR),
reinterpret_cast<ULONG_PTR*>(&info));
}
__except(EXCEPTION_EXECUTE_HANDLER) {}
}
void set_name(const std::string& name) {
set_name(static_cast<DWORD>(-1), name);
}
void set_name(std::thread::native_handle_type handle, const std::string& name) {
set_name(GetThreadId(handle), name);
}
void MaybeYield() { SwitchToThread(); }
void Sleep(std::chrono::microseconds duration) {
if (duration.count() < 100) {
SwitchToThread();
} else {
::Sleep(static_cast<DWORD>(duration.count() / 1000));
}
}
} // namespace threading
} // namespace xe

View File

@@ -0,0 +1,59 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_TYPE_POOL_H_
#define XENIA_BASE_TYPE_POOL_H_
#include <mutex>
#include <vector>
namespace xe {
template <class T, typename A>
class TypePool {
public:
~TypePool() { Reset(); }
void Reset() {
std::lock_guard<std::mutex> guard(lock_);
for (auto it = list_.begin(); it != list_.end(); ++it) {
T* value = *it;
delete value;
}
list_.clear();
}
T* Allocate(A arg0) {
T* result = 0;
{
std::lock_guard<std::mutex> guard(lock_);
if (list_.size()) {
result = list_.back();
list_.pop_back();
}
}
if (!result) {
result = new T(arg0);
}
return result;
}
void Release(T* value) {
std::lock_guard<std::mutex> guard(lock_);
list_.push_back(value);
}
private:
std::mutex lock_;
std::vector<T*> list_;
};
} // namespace xe
#endif // XENIA_BASE_TYPE_POOL_H_

199
src/xenia/base/vec128.h Normal file
View File

@@ -0,0 +1,199 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_VEC128_H_
#define XENIA_BASE_VEC128_H_
#include <cstddef>
#include "xenia/base/math.h"
#include "xenia/base/platform.h"
namespace xe {
// The first rule of vector programming is to only rely on exact positions
// when absolutely required - prefer dumb loops to exact offsets.
// Vectors in memory are laid out as in AVX registers on little endian
// machines. Note that little endian is dumb, so the byte at index 0 in
// the vector is is really byte 15 (or the high byte of short 7 or int 3).
// Because of this, all byte access should be via the accessors instead of
// the direct array.
// Altivec big endian layout: AVX little endian layout:
// +---------+---------+---------+ +---------+---------+---------+
// | int32 0 | int16 0 | int8 0 | | int32 3 | int16 7 | int8 15 |
// | | +---------+ | | +---------+
// | | | int8 1 | | | | int8 14 |
// | +---------+---------+ | +---------+---------+
// | | int16 1 | int8 2 | | | int16 6 | int8 13 |
// | | +---------+ | | +---------+
// | | | int8 3 | | | | int8 12 |
// +---------+---------+---------+ +---------+---------+---------+
// | int32 1 | int16 2 | int8 4 | | int32 2 | int16 5 | int8 11 |
// | | +---------+ | | +---------+
// | | | int8 5 | | | | int8 10 |
// | +---------+---------+ | +---------+---------+
// | | int16 3 | int8 6 | | | int16 4 | int8 9 |
// | | +---------+ | | +---------+
// | | | int8 7 | | | | int8 8 |
// +---------+---------+---------+ +---------+---------+---------+
// | int32 2 | int16 4 | int8 8 | | int32 1 | int16 3 | int8 7 |
// | | +---------+ | | +---------+
// | | | int8 9 | | | | int8 6 |
// | +---------+---------+ | +---------+---------+
// | | int16 5 | int8 10 | | | int16 2 | int8 5 |
// | | +---------+ | | +---------+
// | | | int8 11 | | | | int8 4 |
// +---------+---------+---------+ +---------+---------+---------+
// | int32 3 | int16 6 | int8 12 | | int32 0 | int16 1 | int8 3 |
// | | +---------+ | | +---------+
// | | | int8 13 | | | | int8 2 |
// | +---------+---------+ | +---------+---------+
// | | int16 7 | int8 14 | | | int16 0 | int8 1 |
// | | +---------+ | | +---------+
// | | | int8 15 | | | | int8 0 |
// +---------+---------+---------+ +---------+---------+---------+
//
// Logical order:
// +-----+-----+-----+-----+ +-----+-----+-----+-----+
// | X | Y | Z | W | | W | Z | Y | X |
// +-----+-----+-----+-----+ +-----+-----+-----+-----+
//
// Mapping indices is easy:
// int32[i ^ 0x3]
// int16[i ^ 0x7]
// int8[i ^ 0xF]
typedef struct alignas(16) vec128_s {
union {
struct {
float x;
float y;
float z;
float w;
};
struct {
int32_t ix;
int32_t iy;
int32_t iz;
int32_t iw;
};
struct {
uint32_t ux;
uint32_t uy;
uint32_t uz;
uint32_t uw;
};
float f32[4];
int8_t i8[16];
uint8_t u8[16];
int16_t i16[8];
uint16_t u16[8];
int32_t i32[4];
uint32_t u32[4];
int64_t i64[2];
uint64_t u64[2];
struct {
uint64_t low;
uint64_t high;
};
};
bool operator==(const vec128_s& b) const {
return low == b.low && high == b.high;
}
bool operator!=(const vec128_s& b) const {
return low != b.low || high != b.high;
}
} vec128_t;
static inline vec128_t vec128i(uint32_t src) {
vec128_t v;
for (auto i = 0; i < 4; ++i) {
v.u32[i] = src;
}
return v;
}
static inline vec128_t vec128i(uint32_t x, uint32_t y, uint32_t z, uint32_t w) {
vec128_t v;
v.u32[0] = x;
v.u32[1] = y;
v.u32[2] = z;
v.u32[3] = w;
return v;
}
static inline vec128_t vec128f(float src) {
vec128_t v;
for (auto i = 0; i < 4; ++i) {
v.f32[i] = src;
}
return v;
}
static inline vec128_t vec128f(float x, float y, float z, float w) {
vec128_t v;
v.f32[0] = x;
v.f32[1] = y;
v.f32[2] = z;
v.f32[3] = w;
return v;
}
static inline vec128_t vec128s(uint16_t src) {
vec128_t v;
for (auto i = 0; i < 8; ++i) {
v.u16[i] = src;
}
return v;
}
static inline vec128_t vec128s(uint16_t x0, uint16_t x1, uint16_t y0,
uint16_t y1, uint16_t z0, uint16_t z1,
uint16_t w0, uint16_t w1) {
vec128_t v;
v.u16[0] = x1;
v.u16[1] = x0;
v.u16[2] = y1;
v.u16[3] = y0;
v.u16[4] = z1;
v.u16[5] = z0;
v.u16[6] = w1;
v.u16[7] = w0;
return v;
}
static inline vec128_t vec128b(uint8_t src) {
vec128_t v;
for (auto i = 0; i < 16; ++i) {
v.u8[i] = src;
}
return v;
}
static inline vec128_t vec128b(uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3,
uint8_t y0, uint8_t y1, uint8_t y2, uint8_t y3,
uint8_t z0, uint8_t z1, uint8_t z2, uint8_t z3,
uint8_t w0, uint8_t w1, uint8_t w2, uint8_t w3) {
vec128_t v;
v.u8[0] = x3;
v.u8[1] = x2;
v.u8[2] = x1;
v.u8[3] = x0;
v.u8[4] = y3;
v.u8[5] = y2;
v.u8[6] = y1;
v.u8[7] = y0;
v.u8[8] = z3;
v.u8[9] = z2;
v.u8[10] = z1;
v.u8[11] = z0;
v.u8[12] = w3;
v.u8[13] = w2;
v.u8[14] = w1;
v.u8[15] = w0;
return v;
}
} // namespace xe
#endif // XENIA_BASE_VEC128_H_