Created
September 3, 2014 09:36
-
-
Save chrismdp/3f6d1e3487f383ef6231 to your computer and use it in GitHub Desktop.
A reasonably naïve Blob implementation in C++, complete with Array and Hash packed data types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Blob.h" | |
namespace sol { | |
unsigned Object::used() const { | |
BlobType type = (BlobType)_blob.getByte(0); | |
switch (type) { | |
case BL_INT: | |
return Integer(_blob).used(); | |
case BL_ARRAY: | |
return Array::wrap(_blob).used(); | |
case BL_HASH: | |
return Hash::wrap(_blob).used(); | |
default: | |
std::cerr << "ERROR: Cannot figure out size of type " << type << std::endl; | |
throw; | |
} | |
} | |
unsigned Object::hashStart() const { | |
BlobType type = (BlobType)_blob.getByte(0); | |
switch (type) { | |
case BL_INT: | |
return Integer(_blob).hashStart(); | |
case BL_ARRAY: | |
return Array::wrap(_blob).hashStart(); | |
case BL_HASH: | |
return Hash::wrap(_blob).hashStart(); | |
default: | |
std::cerr << "ERROR: Cannot figure out hashStart of type " << type << std::endl; | |
throw; | |
} | |
} | |
unsigned Object::hash() const { | |
unsigned long hash = 5381; | |
unsigned start = hashStart(); | |
unsigned length = used(); | |
for(unsigned i = start; i < length; i++) { | |
hash = ((hash << 5) + hash) + _blob.getByte(i); | |
} | |
return hash; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CP_BLOB_H | |
#define CP_BLOB_H | |
#include <string.h> //memcpy | |
#include <iostream> | |
#include "Hash.h" | |
#include "HexDump.h" | |
namespace sol { | |
enum BlobType { | |
BL_INT = 1, | |
BL_POINTER_DEPRECATED = 2, | |
BL_ARRAY = 3, | |
BL_HASH = 4 | |
}; | |
class Blob { | |
char *_data; | |
public: | |
Blob() : _data(NULL) {} | |
Blob(void* data) : _data((char*)data) {} | |
bool uninitialised() const { return _data == NULL; } | |
bool operator==(Blob const& other) const { return _data == other._data; } | |
void copy(unsigned offset, void* data, unsigned length) { | |
memcpy(_data + offset, data, length); | |
} | |
void set1(unsigned offset, BlobType value) { | |
*(char*)(_data + offset) = value; | |
} | |
void set4(unsigned offset, unsigned value) { | |
*(unsigned*)(_data + offset) = value; | |
} | |
void set8(unsigned offset, long long value) { | |
*(long long*)(_data + offset) = value; | |
} | |
Blob at(unsigned offset) const { | |
return Blob(_data + offset); | |
} | |
template <typename T> T* get(unsigned offset) const { | |
return (T*)(_data + offset); | |
} | |
char getByte(unsigned offset) const { | |
return *(_data + offset); | |
} | |
}; | |
class Object { | |
Blob _blob; | |
public: | |
Object(Blob blob) : _blob(blob) {} | |
unsigned used() const; | |
unsigned hash() const; | |
unsigned hashStart() const; | |
}; | |
class Integer { | |
Blob _blob; | |
public: | |
Integer(Blob blob, unsigned capacity = 0) : _blob(blob) {} | |
Integer set(int value) { | |
_blob.set1(0, BL_INT); | |
_blob.set4(1, value); | |
return *this; | |
} | |
unsigned used() const { | |
return 5; // 1 for type plus 4 for integer | |
} | |
unsigned hashStart() const { | |
return 1; // start just after type | |
} | |
int value() const { | |
return *(int*)_blob.get<int>(1); | |
} | |
}; | |
template <class A, size_t PAD> class BlobIterator { | |
A _target; | |
unsigned _cursor; | |
public: | |
BlobIterator() : _target(A::blank()), _cursor(0) {} | |
BlobIterator(A target, unsigned cursor) : _target(target), _cursor(cursor) {} | |
Blob operator*() { return _target.blob().at(_cursor); } | |
bool operator==(BlobIterator const& other) const { return _target == other._target && _cursor == other._cursor; } | |
bool operator!=(BlobIterator const& other) const { return !operator==(other); } | |
BlobIterator& operator++() { _cursor += (PAD + Object(_target.blob().at(_cursor)).used()); return *this; } | |
BlobIterator operator++ ( int ) | |
{ | |
BlobIterator clone( *this ); | |
operator++(); | |
return clone; | |
} | |
}; | |
class Hash { | |
static const unsigned CAPACITY_OFFSET = 1; | |
static const unsigned USED_OFFSET = 5; | |
static const unsigned FIRST_KEY = 9; | |
static const unsigned KEY_SIZE = 4; | |
static const unsigned TYPE_SIZE = 1; | |
Blob _blob; | |
Hash(Blob blob) : _blob(blob) {} | |
Hash() {} | |
public: | |
static Hash blank() { return Hash(); } | |
static Hash wrap(Blob blob) { return Hash(blob); } | |
Hash(Blob blob, unsigned capacity) : _blob(blob) { | |
_blob.set1(0, BL_HASH); | |
_blob.set4(CAPACITY_OFFSET, capacity); | |
clear(); | |
} | |
Blob blob() const { return _blob; } | |
bool operator==(Hash const& other) const { return _blob == other._blob; } | |
unsigned hashStart() const { | |
return FIRST_KEY; | |
} | |
void inspect(char const* label = "Hash") const { | |
hexDump(label, _blob.get<void>(0), used()); | |
} | |
unsigned hash() const { return Object(_blob).hash(); } | |
unsigned capacity() const { | |
return *(_blob.get<unsigned>(CAPACITY_OFFSET)); | |
} | |
unsigned used() const { | |
return *(_blob.get<unsigned>(USED_OFFSET)); | |
} | |
void clear() { | |
_blob.set4(USED_OFFSET, FIRST_KEY); | |
} | |
bool empty() const { | |
return used() == FIRST_KEY; | |
} | |
void set(char const* key, int value) { set(sol::hash(key), value); } | |
template <class T> T place(char const* key) { return place<T>(sol::hash(key)); } | |
void set(char const* key, Blob value) { set(sol::hash(key), value); } | |
Blob blobAt(char const* key) const { return blobAt(sol::hash(key)); } | |
template <class T> bool value(char const* key, T* out) const { return value(sol::hash(key), out); } | |
void set(unsigned key, int value) { | |
Blob blob = blobAt(key); | |
if (blob.uninitialised()) | |
fix(place<Integer>(key).set(value)); | |
else | |
Integer(blob).set(value); | |
} | |
// WARNING: Unless you use a value of the same size, this will stamp | |
// over your Hash's memory if you replace an existing key with a new | |
// value. | |
void set(unsigned key, Blob blob) { | |
Blob entry = blobAt(key); | |
bool notSeenBefore = entry.uninitialised(); | |
if (notSeenBefore) | |
entry = newEntry(key); | |
Object o(blob); | |
entry.copy(0, blob.get<void>(0), o.used()); | |
if (notSeenBefore) | |
fix(o); | |
} | |
template <class T> T place(unsigned key) { | |
return T(newEntry(key), capacity() - used() - KEY_SIZE); | |
} | |
template <class T> void fix(T inner) { | |
unsigned newUsed = used() + KEY_SIZE + inner.used(); | |
if (newUsed >= capacity()) { | |
std::cerr << "ERROR: used " << newUsed << " of Hash, capacity is only " << capacity() << std::endl; | |
throw; | |
} | |
_blob.set4(USED_OFFSET, newUsed); | |
} | |
Blob newEntry(unsigned key) { | |
unsigned cursor = used(); | |
_blob.set4(cursor, key); | |
return _blob.get<void>(cursor + KEY_SIZE); | |
} | |
Blob blobAt(unsigned key) const { | |
unsigned cursor = FIRST_KEY; | |
while(cursor < used()) { | |
if (*_blob.get<unsigned>(cursor) == key) { | |
return _blob.at(cursor + KEY_SIZE); | |
} | |
cursor += KEY_SIZE + Object(_blob.at(cursor + KEY_SIZE)).used(); | |
} | |
return Blob(); | |
} | |
template <class T> bool value(unsigned key, T* out) const { | |
Blob value = blobAt(key); | |
if (value.uninitialised()) | |
return false; | |
*out = *value.get<T>(1); | |
return true; | |
} | |
void cloneFrom(Hash const& other) { | |
_blob.copy(USED_OFFSET, other.blob().get<void>(USED_OFFSET), other.used() - USED_OFFSET); | |
} | |
typedef BlobIterator<Hash, 4> iterator; | |
iterator begin() { return iterator(*this, FIRST_KEY + KEY_SIZE); } | |
iterator end() { return iterator(*this, used() + KEY_SIZE); } | |
typedef BlobIterator<const Hash, 4> const_iterator; | |
const_iterator begin() const { return const_iterator(*this, FIRST_KEY + KEY_SIZE); } | |
const_iterator end() const { return const_iterator(*this, used() + KEY_SIZE); } | |
}; | |
class Array { | |
static const unsigned CAPACITY_OFFSET = 1; | |
static const unsigned USED_OFFSET = 5; | |
static const unsigned SIZE_OFFSET = 9; | |
static const unsigned FIRST_VALUE = 13; | |
static const unsigned TYPE_SIZE = 1; | |
Blob _blob; | |
Array(Blob blob) : _blob(blob) {} | |
Array() {} | |
public: | |
static Array blank() { return Array(); } | |
static Array wrap(Blob blob) { return Array(blob); } | |
Array(Blob blob, unsigned capacity) : _blob(blob) { | |
_blob.set1(0, BL_ARRAY); | |
_blob.set4(CAPACITY_OFFSET, capacity); | |
clear(); | |
} | |
void clear() { | |
_blob.set4(USED_OFFSET, FIRST_VALUE); | |
_blob.set4(SIZE_OFFSET, 0); | |
} | |
unsigned hashStart() const { | |
return FIRST_VALUE; | |
} | |
Blob blob() const { return _blob; } | |
void inspect(char const* label = "Array") const { | |
hexDump(label, _blob.get<void>(0), used()); | |
} | |
void cloneFrom(Array const& other) { | |
_blob.copy(USED_OFFSET, other.blob().get<void>(USED_OFFSET), other.used() - USED_OFFSET); | |
} | |
unsigned capacity() const { | |
return *(_blob.get<unsigned>(CAPACITY_OFFSET)); | |
} | |
unsigned used() const { | |
return *(_blob.get<unsigned>(USED_OFFSET)); | |
} | |
unsigned size() const { | |
return *(_blob.get<unsigned>(SIZE_OFFSET)); | |
} | |
bool empty() const { | |
return size() == 0; | |
} | |
bool operator==(Array const& other) const { return _blob == other._blob; } | |
void push_back(int value) { | |
fix(place<Integer>().set(value)); | |
} | |
template <class T> T place() { | |
unsigned cursor = used(); | |
Blob innerBlob(_blob.get<void>(cursor)); | |
return T(innerBlob, capacity() - cursor); | |
} | |
template <class T> void fix(T inner) { | |
unsigned cursor = used(); | |
unsigned newUsed = cursor + inner.used(); | |
if (newUsed >= capacity()) { | |
std::cerr << "ERROR: used " << newUsed << " of Array, capacity is only " << capacity() << std::endl; | |
throw; | |
} | |
_blob.set4(USED_OFFSET, newUsed); | |
_blob.set4(SIZE_OFFSET, size() + 1); | |
} | |
Blob newEntry() { | |
return _blob.get<void>(used()); | |
} | |
void push_back(Blob blob) { | |
Object o(blob); | |
newEntry().copy(0, blob.get<void>(0), o.used()); | |
fix(o); | |
} | |
Blob blobAt(unsigned index) const { | |
unsigned cursor = FIRST_VALUE; | |
if (index >= size()) | |
return Blob(); | |
while(index > 0) { | |
cursor += Object(_blob.at(cursor)).used(); | |
index--; | |
} | |
return _blob.at(cursor); | |
} | |
template <class T> bool value(unsigned index, T* out) const { | |
Blob value = blobAt(index); | |
if (value.uninitialised()) | |
return false; | |
*out = *value.get<T>(1); | |
return true; | |
} | |
typedef BlobIterator<Array, 0> iterator; | |
iterator begin() { return iterator(*this, FIRST_VALUE); } | |
iterator end() { return iterator(*this, used()); } | |
typedef BlobIterator<const Array, 0> const_iterator; | |
const_iterator begin() const { return const_iterator(*this, FIRST_VALUE); } | |
const_iterator end() const { return const_iterator(*this, used()); } | |
}; | |
} | |
#endif // CP_BLOB_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment