// -*- C++ -*- (c) 2012 Petr Rockai #include #include #ifndef DIVINE_TOOLKIT_LENS_H #define DIVINE_TOOLKIT_LENS_H /* * Lens provide a type-safe interface to acessing variable-sized objects, * stored in possibly disjoint address spaces. The addressing scheme for * sub-objects consists of 1) indexes and 2) offsets. Aggregate objects * (basically tuples and arrays) can be stored both in a linear and non-linear * fashion. Moreover, due to existence of arrays, the components need not have * a fixed size, making variable-size, type-safe layouts possible. */ namespace divine { namespace lens { template< typename T > struct LinearSize { template< typename X > static int check( X ); template< typename Addr, typename U > static auto _get( brick::types::Preferred, Addr a ) -> decltype( check( a.template as< U >().advance( a, 0 ) ) ) { T &instance = a.template as< T >(); return a.distance( instance.advance( a, instance.end() ) ); } template< typename Addr, typename U > static auto _get( brick::types::NotPreferred, Addr ) -> int { return std::is_empty< T >::value ? 0 : sizeof( T ); } template< typename Addr > static auto get( Addr a ) -> int { return _get< Addr, T >( brick::types::Preferred(), a ); } }; struct LinearAddress { char *b; int offset; LinearAddress( LinearAddress base, int /* index */, int offset ) : b( base.b ), offset( base.offset + offset ) {} LinearAddress( char *b, int offset ) : b( b ), offset( offset ) {} LinearAddress() : b( nullptr ), offset( 0 ) { } char *dereference() { ASSERT( b ); return b + offset; } template< typename T > T &as() { return *reinterpret_cast< T * >( dereference() ); } int distance( LinearAddress b ) { return b.offset - offset; } // TODO: Generalize to non-linear targets... LinearAddress copy( LinearAddress to, int size ) { std::copy( dereference(), dereference() + size, to.dereference() ); return LinearAddress( to.b, to.offset + size ); } void advance( int i ) { offset += i; } }; #define LENS_FRIEND \ template< typename __Addr, typename __T > friend struct Lens; \ template< typename __S, typename... __T > friend struct TypeAt; \ template< typename __T > friend struct LinearSize; template< typename Structure, typename... Path > struct TypeAt; template< typename Structure > struct TypeAt< Structure > { typedef Structure T; }; template< typename Structure, typename P, typename... Path > struct TypeAt< Structure, P, Path... > { typedef typename TypeAt< decltype( Structure::type( P() ) ), Path... >::T T; }; /* * A lens projects an Address -- usually an untyped memory block or a generic * non-linear memory structure -- as Structure. */ template< typename _Address, typename Structure > struct Lens { typedef _Address Address; Address start; Lens( Address a ) : start( a ) {} template< typename > static Address _address( Address base ) { return base; } template< typename T, typename P, typename... Ps > static Address _address( Address base, P p, Ps... ps ) { T &instance = base.template as< T >(); return _address< decltype( T::type( p ) ) >( instance.advance( base, T::index( p ) ), ps... ); } template< typename... Ps > auto get( Ps... ps ) -> typename TypeAt< Structure, Ps... >::T & { return address( ps... ).template as< typename TypeAt< Structure, Ps... >::T >(); } template< typename... Ps > auto address( Ps... ps ) -> Address { return _address< Structure >( start, ps... ); } template< typename... Ps > auto sub( Ps... ps ) -> Lens< Address, typename TypeAt< Structure, Ps... >::T > { return Lens< Address, typename TypeAt< Structure, Ps... >::T >( address( ps... ) ); } size_t size() { return LinearSize< Structure >::get( this->address() ); } template< typename Addr > Addr copy( Addr to ) { return start.copy( to, size() ); } }; /* A type-indexed tuple. */ template< int, typename... > struct _Tuple; template< int Idx > struct _Tuple< Idx > { int _length() { return Idx; } template< typename Addr > static Addr _advance( Addr base, int i ) { ASSERT_EQ( i, Idx ); return base; } }; template< int _Idx, typename _Head, typename... _Tail > struct _Tuple< _Idx, _Head, _Tail... > : _Tuple< _Idx + 1, _Tail... > { static const int Idx = _Idx; typedef _Head Head; typedef _Tuple< _Idx + 1, _Tail... > Tail; template< typename Addr > static Addr _advance( Addr base, int i ) { if ( _Idx < i ) return Tail::_advance( Addr( base, 1, LinearSize< Head >::get( base ) ), i ); else return base; /* found our index */ } }; template< typename... Ts > class Tuple { typedef _Tuple< 0, Ts... > Real; Real real; template< typename T, typename Tu > static auto _index() -> typename std::enable_if< std::is_same< T, typename Tu::Head >::value, int >::type { return Tu::Idx; } template< typename T, typename Tu > static auto _index() -> typename std::enable_if< !std::is_same< T, typename Tu::Head >::value, int >::type { return _index< T, typename Tu::Tail >(); } template< typename T > static int index( T ) { return _index< T, Real >(); } int end() { return real._length(); } template< typename Addr > Addr advance( Addr base, int idx ) { return Real::_advance( base, idx ); } template< typename T > static T type( T t ) { return t; } LENS_FRIEND }; template< typename T > class FixedArray { int _length; static T type( int ) { return T(); } static int index( int i ) { return i; } int end() { return length(); } template< typename Addr > Addr advance( Addr base, int i ) { return Addr( base, i, sizeof( int ) + i * sizeof( T ) ); } LENS_FRIEND public: int &length() { return _length; } }; /* * TODO This could be made more efficient, maybe? */ template< typename T, typename Self > struct _Array { int _length; Self &self() { return *static_cast< Self * >( this ); } static T type( int ) { return T(); } static int index( int i ) { return i; } int end() { return _length; } }; template< typename T > class Array: _Array< T, Array< T > > { template< typename Addr > Addr advance( Addr a, int i, int base = sizeof( int ) ) { ASSERT_LEQ( 0, i ); if ( !i ) return Addr( a, -1, base ); Addr previous = advance( a, i - 1, base ); return Addr( previous, i, LinearSize< T >::get( previous ) ); } LENS_FRIEND public: int &length() { return this->_length; } }; #if 0 /* * TODO This is not very useful yet, since the index is not built * automatically, nor updated. We need an update interface first. */ template< typename T > class IndexedArray : _Array< T, IndexedArray< T > > { int _offsets[0]; int offset( int i ) { return sizeof( int ) * ( length() + 1 ) + _offsets[i]; } LENS_FRIEND public: int &length() { return this->_length; } }; #endif } } namespace divine_test { using namespace divine::lens; struct TestLens { divine::Pool pool; TEST(tuple) { divine::Blob blob = pool.allocate( 3 * sizeof( int ) + sizeof( float ) ); pool.clear( blob ); struct IntA { int i; IntA( int i = 0 ) : i( i ) {} }; struct IntB { int i; IntB( int i = 0 ) : i( i ) {} }; struct IntC { int i; IntC( int i = 0 ) : i( i ) {} }; typedef Tuple< IntA, IntB, IntC, float > Foo; LinearAddress a( pool.dereference( blob ), 0 ); Lens< LinearAddress, Foo > lens( a ); lens.get( IntA() ) = 1; lens.get( IntB() ) = 2; lens.get( IntC() ) = 3; ASSERT_EQ( divine::fmtblob( pool, blob ), "[ 1, 2, 3, 0 ]" ); } TEST(fixedArray) { divine::Blob blob = pool.allocate( sizeof( int ) * 10 ); pool.clear( blob ); typedef FixedArray< int > Array; LinearAddress a( pool.dereference( blob ), 0 ); Lens< LinearAddress, Array > lens( a ); lens.get().length() = 8; for ( int i = 0; i < 8; ++i ) lens.get( i ) = i + 1; ASSERT_EQ( divine::fmtblob( pool, blob ), "[ 8, 1, 2, 3, 4, 5, 6, 7, 8, 0 ]" ); } TEST(basic) { divine::Blob blob = pool.allocate( 200 ); pool.clear( blob ); struct IntArray1 : FixedArray< int > {}; struct IntArray2 : FixedArray< int > {}; struct Matrix : Array< FixedArray< int > > {}; struct Witch : Tuple< IntArray1, IntArray2 > {}; struct Doctor: Tuple< IntArray1, int, Witch, float, Matrix > {}; LinearAddress a( pool.dereference( blob ), 0 ); Lens< LinearAddress, Doctor > lens( a ); lens.get( IntArray1() ).length() = 5; lens.get( Witch(), IntArray1() ).length() = 3; lens.get( Witch(), IntArray2() ).length() = 4; lens.get( float() ) = -1; lens.get( Matrix() ).length() = 4; lens.get( Matrix(), 0 ).length() = 4; lens.get( Matrix(), 1 ).length() = 4; lens.get( Matrix(), 2 ).length() = 4; lens.get( Matrix(), 3 ).length() = 4; for ( int i = 0; i < 4; ++i ) for ( int j = 0; j < 4; ++j ) lens.get( Matrix(), i, j ) = 100 + j + i * j; lens.get( int() ) = int( 365 ); ASSERT_EQ( divine::fmtblob( pool, blob ), "[ 5, 0, 0, 0, 0, 0, 365, 3, 0, 0, 0, 4, 0, 0, 0, 0," " 3212836864, 4, 4, 100, 101, 102, 103, 4, 100, 102," " 104, 106, 4, 100, 103, 106, 109, 4, 100, 104, 108," " 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]" ); for ( int i = 0; i < lens.get( IntArray1() ).length(); ++ i ) lens.get( IntArray1(), i ) = i + 10; for ( int i = 0; i < lens.get( Witch(), IntArray1() ).length(); ++ i ) lens.get( Witch(), IntArray1(), i ) = i + 20; for ( int i = 0; i < lens.get( Witch(), IntArray2() ).length(); ++ i ) lens.get( Witch(), IntArray2(), i ) = i + 30; Lens< LinearAddress, Witch > lens2 UNUSED = lens.sub( Witch() ); ASSERT_EQ( lens.get( int() ), 365 ); ASSERT_EQ( lens.get( IntArray1() ).length(), 5 ); ASSERT_EQ( lens.get( IntArray1(), 0 ), 10 ); ASSERT_EQ( lens2.get( IntArray1(), 1 ), 21 ); ASSERT_EQ( lens.get( Witch(), IntArray2(), 0 ), 30 ); ASSERT_EQ( divine::fmtblob( pool, blob ), "[ 5, 10, 11, 12, 13, 14, 365, 3, 20, 21, 22, 4, 30, 31, 32, 33, 3212836864," " 4, 4, 100, 101, 102, 103, 4, 100, 102, 104, 106, 4, 100, 103, 106, 109, 4," " 100, 104, 108, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]" ); } TEST(copy) { int size = 2 * sizeof( int ); divine::Blob blob = pool.allocate( size ), copy = pool.allocate( size ); pool.clear( blob ); pool.clear( copy ); struct IntA { int i; IntA( int i = 0 ) : i( i ) {} }; struct IntB { int i; IntB( int i = 0 ) : i( i ) {} }; typedef Tuple< IntA, IntB > Foo; LinearAddress a( pool.dereference( blob ), 0 ); LinearAddress b( pool.dereference( copy ), 0 ); Lens< LinearAddress, Foo > lens( a ); lens.get( IntA() ) = 1; lens.get( IntB() ) = 2; lens.sub( IntA() ).copy( b ); ASSERT_EQ( divine::fmtblob( pool, copy ), "[ 1, 0 ]" ); pool.clear( copy ); lens.copy( b ); ASSERT_EQ( divine::fmtblob( pool, copy ), "[ 1, 2 ]" ); } }; } #endif