mirror of https://github.com/SeanOMik/libki.git
pclass: Start implementing TypeSystem
This commit is contained in:
parent
8f7d9bc896
commit
8fbb9ba906
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* The type used to store a type/property hash.
|
||||
*/
|
||||
typedef uint32_t hash_t;
|
||||
|
||||
/**
|
||||
* A base class for type/property hash calculators.
|
||||
*/
|
||||
class HashCalculator
|
||||
{
|
||||
public:
|
||||
virtual ~HashCalculator() {};
|
||||
|
||||
/**
|
||||
* Calculate a type hash from the type's name.
|
||||
* @param name The name of the type.
|
||||
*/
|
||||
virtual hash_t calculate_type_hash(const std::string &name) const = 0;
|
||||
|
||||
/**
|
||||
* Calculate a property hash from the property's name and type.
|
||||
* @param name The name of the property.
|
||||
* @param type_hash The hash of the property's type.
|
||||
*/
|
||||
virtual hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hash calculator that uses the algorithms found and used in
|
||||
* Wizard101.
|
||||
*/
|
||||
class WizardHashCalculator : public HashCalculator
|
||||
{
|
||||
public:
|
||||
hash_t calculate_type_hash(const std::string& name) const override;
|
||||
hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const override;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
class Type;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class PropertyClass
|
||||
{
|
||||
explicit PropertyClass(const Type &type) : m_type(type) {}
|
||||
virtual ~PropertyClass() {};
|
||||
|
||||
const Type &get_type();
|
||||
|
||||
virtual void on_created();
|
||||
private:
|
||||
const Type &m_type;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "ki/pclass/HashCalculator.h"
|
||||
#include "ki/pclass/Value.h"
|
||||
#include "ki/pclass/PropertyClass.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
class BitStream;
|
||||
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
friend class TypeSystem;
|
||||
|
||||
public:
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
enum class kind
|
||||
{
|
||||
NONE,
|
||||
PRIMITIVE,
|
||||
CLASS,
|
||||
ENUM
|
||||
};
|
||||
|
||||
Type(std::string name, hash_t hash);
|
||||
virtual ~Type() {};
|
||||
|
||||
std::string get_name() const;
|
||||
hash_t get_hash() const;
|
||||
kind get_kind() const;
|
||||
|
||||
virtual PropertyClass *instantiate() const;
|
||||
virtual void write_to(BitStream &stream, const Value &value) const = 0;
|
||||
virtual void read_from(BitStream &stream, Value &value) const = 0;
|
||||
|
||||
protected:
|
||||
kind m_kind;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
hash_t m_hash;
|
||||
|
||||
void set_hash(hash_t hash);
|
||||
};
|
||||
|
||||
typedef std::vector<Type *> TypeList;
|
||||
typedef std::map<std::string, Type *> TypeNameMap;
|
||||
typedef std::map<hash_t, Type *> TypeHashMap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "Type.h"
|
||||
#include "PrimitiveType.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class TypeSystem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @return A singleton instance of TypeSystem with all C++ primitives defined.
|
||||
*/
|
||||
static TypeSystem &get_singleton();
|
||||
|
||||
explicit TypeSystem(HashCalculator *hash_calculator);
|
||||
~TypeSystem();
|
||||
|
||||
void set_hash_calculator(HashCalculator *hash_calculator);
|
||||
|
||||
Type &get_type(const std::string &name) const;
|
||||
Type &get_type(hash_t hash) const;
|
||||
|
||||
template <typename ValueT>
|
||||
Type &define_primitive(const std::string &name)
|
||||
{
|
||||
// TODO: Create primitive types
|
||||
}
|
||||
|
||||
template <class ClassT>
|
||||
Type &define_class(const std::string &name)
|
||||
{
|
||||
// Ensure that ClassT inherits PropertyClass
|
||||
static_assert(std::is_base_of<Object, ClassT>::value, "ClassT must inherit PropertyClass!");
|
||||
|
||||
// TODO: Create class types
|
||||
}
|
||||
|
||||
template <typename EnumT>
|
||||
Type &define_enum(const std::string &name)
|
||||
{
|
||||
// Ensure that EnumT is an enum
|
||||
static_assert(std::is_enum<EnumT>::value, "EnumT must be an enum!");
|
||||
|
||||
// TODO: Create enum types
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeSystem *s_instance;
|
||||
|
||||
TypeList m_types;
|
||||
TypeNameMap m_type_name_lookup;
|
||||
TypeHashMap m_type_hash_lookup;
|
||||
HashCalculator *m_hash_calculator;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* A wrapper around a void pointer that ensures type safety.
|
||||
*/
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
Value(T &value)
|
||||
{
|
||||
m_value_ptr = value;
|
||||
m_type_hash = typeid(value).hash_code();
|
||||
}
|
||||
|
||||
Value(Value &&o) noexcept
|
||||
: m_value_ptr(std::move(m_value_ptr))
|
||||
, m_type_hash(std::move(o.m_type_hash))
|
||||
{}
|
||||
|
||||
/**
|
||||
* @return Whether or not the value being held is of type T.
|
||||
*/
|
||||
template <typename T>
|
||||
bool is() const
|
||||
{
|
||||
// Do the type hashes match?
|
||||
return m_type_hash == typeid(T).hash_code();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A reference to the value being held as the specified type.
|
||||
*/
|
||||
template <typename T>
|
||||
const T &get() const
|
||||
{
|
||||
// Make sure that this is allowed
|
||||
if (!is<T>())
|
||||
throw std::runtime_error("Type mismatch in Value::get<T>() call.");
|
||||
return *(T *)m_value_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A reference to the value being held as the specified type.
|
||||
*/
|
||||
template <typename T>
|
||||
T &get()
|
||||
{
|
||||
// Make sure that this is allowed
|
||||
if (!is<T>())
|
||||
throw std::runtime_error("Type mismatch in Value::get<T>() call.");
|
||||
return *(T *)m_value_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void *m_value_ptr;
|
||||
std::size_t m_type_hash;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/HashCalculator.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/Type.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/TypeSystem.cpp
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
#include "ki/pclass/HashCalculator.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
hash_t WizardHashCalculator::calculate_type_hash(const std::string& name) const
|
||||
{
|
||||
// Declare variables
|
||||
auto result = 0;
|
||||
auto a = 0;
|
||||
auto b = 32;
|
||||
|
||||
// Iterate through the characters in the string
|
||||
for (auto it = name.begin(); it != name.end(); ++it)
|
||||
{
|
||||
result ^= (*it - 32) << a;
|
||||
if (a > 24)
|
||||
{
|
||||
result ^= (*it - 32) >> b;
|
||||
if (a >= 27)
|
||||
{
|
||||
a -= 32;
|
||||
b += 32;
|
||||
}
|
||||
}
|
||||
|
||||
a += 5;
|
||||
b -= 5;
|
||||
}
|
||||
|
||||
// Make the result an absolute value
|
||||
if (result < 0)
|
||||
result = -result;
|
||||
return result;
|
||||
}
|
||||
|
||||
hash_t WizardHashCalculator::calculate_property_hash(const std::string& name, const hash_t type_hash) const
|
||||
{
|
||||
// Find the hash of the property name
|
||||
hash_t result = 0x1505;
|
||||
for (auto it = name.begin(); it != name.end(); ++it)
|
||||
result = (0x21 * result) + *it;
|
||||
result &= 0x7FFFFFFF;
|
||||
|
||||
// Add the type hash onto it
|
||||
return result + type_hash;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#include "ki/pclass/Type.h"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
Type::Type(const std::string name, const hash_t hash)
|
||||
{
|
||||
m_name = name;
|
||||
m_hash = hash;
|
||||
m_kind = kind::NONE;
|
||||
}
|
||||
|
||||
std::string Type::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void Type::set_hash(const hash_t hash)
|
||||
{
|
||||
m_hash = hash;
|
||||
}
|
||||
|
||||
hash_t Type::get_hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
Type::kind Type::get_kind() const
|
||||
{
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
PropertyClass* Type::instantiate() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Type '" << m_name << "' cannot be instantiated.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
#include "ki/pclass/TypeSystem.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#define DEFINE_INTEGER_PRIMTIIVE(st, ut, n) \
|
||||
s_instance->define_primitive<st>(n); \
|
||||
s_instance->define_primitive<st>("signed " n); \
|
||||
s_instance->define_primitive<ut>("unsigned " n)
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
TypeSystem& TypeSystem::get_singleton()
|
||||
{
|
||||
if (s_instance == nullptr)
|
||||
{
|
||||
// Create the static instance with the default hash calculator
|
||||
s_instance = new TypeSystem(new WizardHashCalculator());
|
||||
|
||||
// Define integer types
|
||||
s_instance->define_primitive<bool>("bool");
|
||||
DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "char");
|
||||
DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "__int8");
|
||||
s_instance->define_primitive<int8_t>("int8_t");
|
||||
s_instance->define_primitive<uint8_t>("uint8_t");
|
||||
|
||||
DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short");
|
||||
DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16");
|
||||
s_instance->define_primitive<int16_t>("int16_t");
|
||||
s_instance->define_primitive<uint16_t>("uint16_t");
|
||||
|
||||
DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "int");
|
||||
DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "__int32");
|
||||
s_instance->define_primitive<int32_t>("int32_t");
|
||||
s_instance->define_primitive<uint32_t>("uint32_t");
|
||||
|
||||
DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "long");
|
||||
DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64");
|
||||
s_instance->define_primitive<int64_t>("int64_t");
|
||||
s_instance->define_primitive<uint64_t>("uint64_t");
|
||||
s_instance->define_primitive<uint64_t>("gid");
|
||||
|
||||
// TODO: Define bit integer types
|
||||
|
||||
// Define floating point types
|
||||
s_instance->define_primitive<float>("float");
|
||||
s_instance->define_primitive<double>("double");
|
||||
|
||||
// TODO: Define bit floating point types
|
||||
|
||||
// Define string types
|
||||
s_instance->define_primitive<std::string>("std::string");
|
||||
s_instance->define_primitive<std::wstring>("std::wstring");
|
||||
}
|
||||
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
TypeSystem::TypeSystem(HashCalculator* hash_calculator)
|
||||
{
|
||||
m_hash_calculator = hash_calculator;
|
||||
}
|
||||
|
||||
|
||||
TypeSystem::~TypeSystem()
|
||||
{
|
||||
// Delete all type declarations
|
||||
for (auto it = m_types.begin(); it != m_types.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
// Clear lookups
|
||||
m_type_name_lookup.clear();
|
||||
m_type_hash_lookup.clear();
|
||||
|
||||
// Delete the hash calculator
|
||||
delete m_hash_calculator;
|
||||
}
|
||||
|
||||
void TypeSystem::set_hash_calculator(HashCalculator* hash_calculator)
|
||||
{
|
||||
// Update the hash calculator
|
||||
m_hash_calculator = hash_calculator;
|
||||
|
||||
// Iterate through all types and recalculate their hash
|
||||
m_type_hash_lookup.clear();
|
||||
for (auto it = m_types.begin(); it != m_types.end(); ++it)
|
||||
{
|
||||
// Calculate the new hash and update the type
|
||||
auto *type = *it;
|
||||
const auto new_hash = m_hash_calculator->calculate_type_hash(type->get_name());
|
||||
type->set_hash(new_hash);
|
||||
|
||||
// Add the new hash to lookup
|
||||
m_type_hash_lookup[new_hash] = type;
|
||||
|
||||
// Is this type a class?
|
||||
if (type->get_kind() == Type::kind::CLASS)
|
||||
{
|
||||
// TODO: Recalculate property hashes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type& TypeSystem::get_type(const std::string &name) const
|
||||
{
|
||||
const auto it = m_type_name_lookup.find(name);
|
||||
if (it == m_type_name_lookup.end())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find type with name '" << name << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
return *(it->second);
|
||||
}
|
||||
|
||||
Type& TypeSystem::get_type(const hash_t hash) const
|
||||
{
|
||||
const auto it = m_type_hash_lookup.find(hash);
|
||||
if (it == m_type_hash_lookup.end())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find type with hash: " <<
|
||||
std::hex << std::setw(8) << std::setfill('0') <<
|
||||
std::uppercase << hash << ".";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
return *(it->second);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#include <exception>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -168,7 +168,7 @@ namespace ki
|
|||
void BitStream::expand_buffer()
|
||||
{
|
||||
// Work out a new buffer size
|
||||
auto new_size = (2 << (uint64_t)log2(m_position.get_byte()) + 1) + 2;
|
||||
auto new_size = (2 << (uint64_t)std::log2(m_position.get_byte()) + 1) + 2;
|
||||
if (new_size < m_buffer_size)
|
||||
new_size = std::numeric_limits<size_t>::max();
|
||||
|
||||
|
|
Loading…
Reference in New Issue