#include #include "compression.hpp" using namespace std; struct binary { binary(signed long x) : value(x) { } template binary(Int x) : value(x) { } signed long value; }; ostream& operator<<(ostream& os, binary b) { for(int i = (sizeof(signed long) * 8) - 1; i >= 0; --i, b.value <<= 1) { os << (b.value < 0 ? 1 : 0); if(!(i % 4)) os << " "; } return os; } // This traits class contains type information for allocation and alignment. struct buffer_traits { typedef unsigned long unit_type; static std::size_t const unit_size = sizeof(unit_type); }; // A buffer reference maps the memory pointed to by an iterator onto an object. template struct buffer_reference { typedef typename buffer_traits::unit_type Unit; static std::size_t const Width = compressed_size::value; static Unit const Mask = compression_mask::value; buffer_reference(Unit* p, std::size_t o) : _ptr(p), _off(o) { } buffer_reference& operator=(T const& x) { // Clear the bits occupying the space where T will go. Then, merge // the new bits (of x) into the same space. // Note that we might want to consider replacing the reinterpret cast // with an opportunity to specialize the encoding of the T. This is // just the general purpose encoder. cout << "before: " << binary(*_ptr) << endl; *_ptr &= ~(Mask << _off); *_ptr |= ((*reinterpret_cast(&x) & Mask) << _off); cout << "after: " << binary(*_ptr) << endl; return *this; } operator T() const { return (T)((*_ptr & (Mask << _off)) >> _off); } Unit* _ptr; std::size_t _off; }; // A buffer iterator references an offset within a unit of a particular width. // For simplicity, we are assuming that all compressable types are template struct buffer_iterator { typedef typename buffer_traits::unit_type Unit; static std::size_t const Width = compressed_size::value; typedef T value_type; typedef buffer_reference reference; buffer_iterator() : _ptr(0), _off(0) { } buffer_iterator(Unit* p, std::size_t o) : _ptr(p), _off(o) { } bool operator==(buffer_iterator const& x) const { return _ptr == x._ptr && _off == x._off; } reference operator*() { return reference(_ptr, _off); } buffer_iterator& operator++() { _off += Width; return *this; } Unit* _ptr; std::size_t _off; }; template class buffer { // Define the compressed buffer in terms of typedef typename buffer_traits::unit_type Unit; static std::size_t const Size = buffer_traits::unit_size; static std::size_t const Width = compressed_size::value; // For now, disallow non-aligned compression. static_assert(Size % Width == 0, "Compression of T is not unit-aligned"); // This isn't necessarily true. It might be the T can be compressed in // multiples of the unit_type. We just don't handle this yet. static_assert(Width <= Size * 8, "T cannot be compressed"); public: typedef T value_type; typedef buffer_iterator iterator; buffer() { } private: iterator _begin; iterator _end; }; // Main program stuff. // A lovely little helper function that can be used to build arbitrary n-bit // integral values (like the elements of bitfields). template struct int_n { typedef T value_type; static std::size_t const value_width = Bits; int_n() : value() { } int_n(T const& x) : value(x) { } // This is really where things are breaking. We need to supply a generic // cast/construct operator for arbitrarily encoded expressions. Something // like compress cast. See the nasty reinterpret cast in reference above. T get() const { return value; } T value; }; template ostream& operator<<(ostream& os, int_n const& x) { return os << x.get(); } // Specialization of compression data for n-bit values. template struct compression_traits> : compressable { }; int main() { buffer b; // Just fucking around, build a bit-iterator on an int. unsigned long data = 0xff; buffer_iterator> i(&data, 0); *i = 5; cout << "test: " << int_n<4>(*i) << endl; ++i; *i = 10; cout << "test: " << int_n<4>(*i) << endl; return 0; }