pclass: Start implementing TypeSystem

This commit is contained in:
Joshua Scott 2018-10-25 11:39:16 +01:00
parent 8f7d9bc896
commit 8fbb9ba906
10 changed files with 491 additions and 2 deletions

View File

@ -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;
};
}
}

View File

@ -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;
};
}
}

59
include/ki/pclass/Type.h Normal file
View File

@ -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;
}
}

View File

@ -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;
};
}
}

67
include/ki/pclass/Value.h Normal file
View File

@ -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;
};
}
}

View File

@ -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
)

View File

@ -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;
}
}
}

43
src/pclass/Type.cpp Normal file
View File

@ -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());
}
}
}

131
src/pclass/TypeSystem.cpp Normal file
View File

@ -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);
}
}
}

View File

@ -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();