diff --git a/DDCore/include/DDSegmentation/BitFieldCoder.h b/DDCore/include/DDSegmentation/BitFieldCoder.h new file mode 100644 index 0000000000000000000000000000000000000000..8e3b9acdea1a7c0a23fb502d2acfb8c6d3cb5850 --- /dev/null +++ b/DDCore/include/DDSegmentation/BitFieldCoder.h @@ -0,0 +1,281 @@ +#ifndef DDSegmentation_BitFieldCoder_H +#define DDSegmentation_BitFieldCoder_H 1 + + +#include <string> +#include <vector> +#include <map> +#include <sstream> + + +namespace dd4hep { + +typedef long long int long64 ; +typedef unsigned long long ulong64 ; + +namespace DDSegmentation { + + class BitFieldValue ; + class StringTokenizer ; + + + /** Helper class for decoding and encoding a bit field of 64bits for convenient declaration and + * manipulation of sub fields of various widths.<br> + * This is a thread safe re-implementation of the functionality in the deprected BitField64. + * + * Example:<br> + * BitFieldCoder bc("layer:7,system:-3,barrel:3,theta:32:11,phi:11" ) ; <br> + * bc.set( field, "layer" , 123 ); <br> + * bc.set( field, "system" , -4 ); <br> + * bc.set( field, "barrel" , 7 ); <br> + * bc.set( field, "theta" , 180 ); <br> + * bc.set( field, "phi" , 270 ); <br> + * ... <br> + * int theta = bc.get( field, "theta" ) ; <br> + * ... <br> + * unsigned phiIndex = bc.index("phi") ; <br> + * int phi = bc.get( field, phiIndex ) ; <br> + * + * @author F.Gaede, DESY + * @date 2017-09 + */ + class BitFieldCoder{ + + + /** Helper class for BitFieldCoder that corresponds to one field value. + */ + + class BitFieldValue{ + + public : + virtual ~BitFieldValue() {} + + /** The default c'tor. + * @param name name of the field + * @param offset offset of field + * @param signedWidth width of field, negative if field is signed + */ + BitFieldValue( const std::string& name, + unsigned offset, int signedWidth ) ; + + /// calculate this field's value given an external 64 bit bitmap + long64 value(long64 bitfield) const; + + + // assign the given value to the bit field + void set(long64& bitfield, long64 value) ; + + + /** The field's name */ + const std::string& name() const { return _name ; } + + /** The field's offset */ + unsigned offset() const { return _offset ; } + + /** The field's width */ + unsigned width() const { return _width ; } + + /** True if field is interpreted as signed */ + bool isSigned() const { return _isSigned ; } + + /** The field's mask */ + ulong64 mask() const { return _mask ; } + + /** Minimal value */ + int minValue() const { return _minVal; } + + /** Maximal value */ + int maxValue() const { return _maxVal; } + + protected: + + ulong64 _mask{} ; + unsigned _offset{} ; + unsigned _width{} ; + int _minVal{} ; + int _maxVal{} ; + bool _isSigned{} ; + std::string _name{} ; + + }; + + + public : + + typedef std::map<std::string, unsigned int> IndexMap ; + + /** No default c'tor */ + BitFieldCoder() = delete ; + + ~BitFieldCoder() { // clean up + for(unsigned i=0;i<_fields.size();i++){ + delete _fields[i] ; + } + } + + /** The c'tor takes an initialization string of the form:<br> + * \<fieldDesc\>[,\<fieldDesc\>...]<br> + * fieldDesc = name:[start]:[-]length<br> + * where:<br> + * name: The name of the field<br> + * start: The start bit of the field. If omitted assumed to start + * immediately following previous field, or at the least significant + * bit if the first field.<br> + * length: The number of bits in the field. If preceeded by '-' + * the field is signed, otherwise unsigned.<br> + * Bit numbering is from the least significant bit (bit 0) to the most + * significant (bit 63). <br> + * Example: "layer:7,system:-3,barrel:3,theta:32:11,phi:11" + */ + BitFieldCoder( const std::string& initString ) : _joined(0){ + + init( initString ) ; + } + + /** return a new 64bit value given as high and low 32bit words. + */ + static long64 toLong(unsigned low_Word, unsigned high_Word ) { + return ( ( low_Word & 0xffffffffULL ) | ( ( high_Word & 0xffffffffULL ) << 32 ) ) ; + } + + /** The low word, bits 0-31 + */ + static unsigned lowWord(long64 bitfield) { return unsigned( bitfield & 0xffffFFFFUL ) ; } + + /** The high word, bits 32-63 + */ + static unsigned highWord(long64 bitfield) { return unsigned( bitfield >> 32) ; } + + + /** get value of sub-field specified by index + */ + long64 get(long64 bitfield, size_t index) const { + return _fields.at(index)->value( bitfield ) ; + } + + /** Access to field through name . + */ + long64 get(long64 bitfield, const std::string& name) const { + + return _fields.at( index( name ) )->value( bitfield ) ; + } + + /** set value of sub-field specified by index + */ + void set(long64& bitfield, size_t index, ulong64 value) const { + _fields.at(index)->set( bitfield , value ) ; + } + + /** Access to field through name . + */ + void set(long64& bitfield, const std::string& name, ulong64 value) const { + + _fields.at( index( name ) )->set( bitfield, value ) ; + } + + + + /** Highest bit used in fields [0-63] + */ + unsigned highestBit() const ; + + + /** Number of values */ + size_t size() const { return _fields.size() ; } + + /** Index for field named 'name' + */ + size_t index( const std::string& name) const ; + + + /** Const Access to field through name . + */ + const BitFieldValue& operator[](const std::string& name) const { + + return *_fields[ index( name ) ] ; + } + + /** Return a valid description string of all fields + */ + std::string fieldDescription() const ; + + /** Return a string with a comma separated list of the current sub field values + */ + std::string valueString(ulong64 bitfield) const ; + + const std::vector<BitFieldValue*>& fields() const { + return _fields; + } + + protected: + + /** Add an additional field to the list + */ + void addField( const std::string& name, unsigned offset, int width ); + + /** Decode the initialization string as described in the constructor. + * @see BitFieldCoder( const std::string& initString ) + */ + void init( const std::string& initString) ; + + public: + + protected: + + // -------------- data members:-------------- + + std::vector<BitFieldValue*> _fields{} ; + IndexMap _map{} ; + long64 _joined{} ; + + + }; + + + /** Helper class for string tokenization. Usage:<br> + * std::vector<std::string> tokens ; <br> + * StringTokenizer t( tokens ,',') ; <br> + * std::for_each( aString.begin(), aString.end(), t ) ; <br> + * + * @author F.Gaede, DESY + * @date 2013-06 + */ + class StringTokenizer{ + + std::vector< std::string >& _tokens ; + char _del ; + char _last ; + + public: + + /** Only c'tor, give (empty) token vector and delimeter character */ + StringTokenizer( std::vector< std::string >& tokens, char del ) + : _tokens(tokens) + , _del(del), + _last(del) { + } + + /** Operator for use with algorithms, e.g. for_each */ + void operator()(const char& c) { + + if( c != _del ) { + + if( _last == _del ) { + _tokens.push_back("") ; + } + _tokens.back() += c ; + } + _last = c ; + } + + }; + +} // end namespace + +} // end namespace + +#endif + + + + diff --git a/DDCore/src/segmentations/BitFieldCoder.cpp b/DDCore/src/segmentations/BitFieldCoder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02f2e334e62593630bf5e8df3d8e7cf6d88b4ced --- /dev/null +++ b/DDCore/src/segmentations/BitFieldCoder.cpp @@ -0,0 +1,246 @@ +#include "DDSegmentation/BitFieldCoder.h" + +#include <cmath> +#include <algorithm> +#include <stdexcept> + +namespace dd4hep{ + +namespace DDSegmentation { + + BitFieldCoder::BitFieldValue::BitFieldValue( const std::string& fieldName, + unsigned fieldOffset, int signedWidth ) : + _mask(0), + _offset( fieldOffset ), + _width( abs( signedWidth ) ), + _minVal(0), + _maxVal(0), + _isSigned( signedWidth < 0 ), + _name( fieldName ) { + + // sanity check + if( _offset > 63 || _offset+_width > 64 ) { + + std::stringstream s ; + s << " BitFieldValue '" << _name << "': out of range - offset : " + << _offset << " width " << _width ; + + throw( std::runtime_error( s.str() ) ) ; + } + + _mask = ( ( 0x0001LL << _width ) - 1 ) << _offset ; + + + // compute extreme values for later checks + if( _isSigned ){ + + _minVal = ( 1LL << ( _width - 1 ) ) - ( 1LL << _width ) ; + _maxVal = ( 1LL << ( _width - 1 ) ) - 1 ; + + } else { + + _maxVal = 0x0001<<_width ; + } + + } + + + long64 BitFieldCoder::BitFieldValue::value(long64 id) const { + + if( _isSigned ) { + + long64 val = ( id & _mask ) >> _offset ; + + if( ( val & ( 1LL << ( _width - 1 ) ) ) != 0 ) { // negative value + + val -= ( 1LL << _width ); + } + + return val ; + + } else { + + return ( id & _mask ) >> _offset ; + } + } + + void BitFieldCoder::BitFieldValue::set(long64& field, long64 in) { + + // check range + if( in < _minVal || in > _maxVal ) { + + std::stringstream s ; + s << " BitFieldValue '" << _name << "': out of range : " << in + << " for width " << _width ; + + throw( std::runtime_error( s.str() ) ); + } + + field &= ~_mask ; // zero out the field's range + + field |= ( ( in << _offset ) & _mask ) ; + + } + + + + + size_t BitFieldCoder::index( const std::string& name) const { + + IndexMap::const_iterator it = _map.find( name ) ; + + if( it != _map.end() ) + + return it->second ; + + else + throw std::runtime_error(" BitFieldValue: unknown name: " + name ) ; + } + + unsigned BitFieldCoder::highestBit() const { + + unsigned hb(0) ; + + for(unsigned i=0;i<_fields.size();i++){ + + if( hb < ( _fields[i]->offset() + _fields[i]->width() ) ) + hb = _fields[i]->offset() + _fields[i]->width() ; + } + return hb ; + } + + + std::string BitFieldCoder::valueString(ulong64 bitfield) const { + + std::stringstream os ; + + for(unsigned i=0;i<_fields.size();i++){ + + if( i != 0 ) os << "," ; + + os << _fields[i]->name() << ":" << _fields[i]->value(bitfield) ; + + } + return os.str() ; + } + + std::string BitFieldCoder::fieldDescription() const { + + std::stringstream os ; + + for(unsigned i=0;i<_fields.size();i++){ + + if( i != 0 ) os << "," ; + + os << _fields[i]->name() << ":" + << _fields[i]->offset() << ":" ; + + if( _fields[i]->isSigned() ) + os << "-" ; + + os << _fields[i]->width() ; + + } +// for( IndexMap::const_iterator it = _map.begin() ; +// it != _map.end() ; ++it ){ + +// if( it != _map.begin() ) +// os << "," ; + +// os << it->first << ":" +// << _fields[ it->second ]->offset() << ":" ; + +// if( _fields[ it->second ]->isSigned() ) +// os << "-" ; + +// os << _fields[ it->second ]->width() ; + +// } + + return os.str() ; + } + + void BitFieldCoder::addField( const std::string& name, unsigned offset, int width ){ + + + BitFieldValue* bfv = new BitFieldValue( name, offset, width ) ; + + _fields.push_back( bfv ) ; + + _map[ name ] = _fields.size()-1 ; + + if( _joined & bfv->mask() ) { + + std::stringstream s ; + s << " BitFieldValue::addField(" << name << "): bits already used " << std::hex << _joined + << " for mask " << bfv->mask() ; + + throw( std::runtime_error( s.str() ) ) ; + + } + + _joined |= _fields.back()->mask() ; + + } + + void BitFieldCoder::init( const std::string& initString) { + + unsigned offset = 0 ; + + // need to compute bit field masks and offsets ... + std::vector<std::string> fieldDescriptors ; + StringTokenizer t( fieldDescriptors ,',') ; + + std::for_each( initString.begin(), initString.end(), t ) ; + + for(unsigned i=0; i< fieldDescriptors.size() ; i++ ){ + + std::vector<std::string> subfields ; + StringTokenizer ts( subfields ,':') ; + + std::for_each( fieldDescriptors[i].begin(), fieldDescriptors[i].end(), ts ); + + std::string name ; + int width ; + unsigned thisOffset ; + + switch( subfields.size() ){ + + case 2: + + name = subfields[0] ; + width = atol( subfields[1].c_str() ) ; + thisOffset = offset ; + + offset += abs( width ) ; + + break ; + + case 3: + name = subfields[0] ; + thisOffset = atol( subfields[1].c_str() ) ; + width = atol( subfields[2].c_str() ) ; + + offset = thisOffset + abs( width ) ; + + break ; + + default: + + std::stringstream s ; + s << " BitFieldCoder: invalid number of subfields " + << fieldDescriptors[i] ; + + throw( std::runtime_error( s.str() ) ) ; + } + + addField( name , thisOffset, width ) ; + } + } + + + + +} // namespace + +} // namespace diff --git a/DDTest/CMakeLists.txt b/DDTest/CMakeLists.txt index 9f164540288e94acbce66cfaae815bb4c57d0419..52859d728fe6560667bb1986aee52f7429fe84e2 100644 --- a/DDTest/CMakeLists.txt +++ b/DDTest/CMakeLists.txt @@ -21,6 +21,7 @@ dd4hep_add_test_reg ( test_surface BUILD_EXEC REGEX_FAIL "TEST_FAILE EXEC_ARGS file:${CMAKE_CURRENT_SOURCE_DIR}/units.xml ) dd4hep_add_test_reg ( test_bitfield64 BUILD_EXEC REGEX_FAIL "TEST_FAILED" ) +dd4hep_add_test_reg ( test_bitfieldcoder BUILD_EXEC REGEX_FAIL "TEST_FAILED" ) dd4hep_add_test_reg ( test_DetType BUILD_EXEC REGEX_FAIL "TEST_FAILED" ) dd4hep_add_test_reg ( test_PolarGridRPhi2 BUILD_EXEC REGEX_FAIL "TEST_FAILED" ) dd4hep_add_test_reg ( test_cellDimensions BUILD_EXEC REGEX_FAIL "TEST_FAILED" )