Skip to content

Instantly share code, notes, and snippets.

@chrismdp
Created September 3, 2014 09:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrismdp/3f6d1e3487f383ef6231 to your computer and use it in GitHub Desktop.
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.
#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;
}
}
#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