1 #ifndef libjmmcg_core_bitfield_map_hpp 2 #define libjmmcg_core_bitfield_map_hpp 3 /****************************************************************************** 4 ** $Header: svn+ssh://jmmcg@svn.code.sf.net/p/libjmmcg/code/trunk/libjmmcg/core/bitfield_map.hpp 2150 2017-09-16 16:35:01Z jmmcg $ 5 ** 6 ** Copyright (c) 2014 by J.M.McGuiness, coder@hussar.me.uk 7 ** 8 ** This library is free software; you can redistribute it and/or 9 ** modify it under the terms of the GNU Lesser General Public 10 ** License as published by the Free Software Foundation; either 11 ** version 2.1 of the License, or (at your option) any later version. 12 ** 13 ** This library is distributed in the hope that it will be useful, 14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 ** Lesser General Public License for more details. 17 ** 18 ** You should have received a copy of the GNU Lesser General Public 19 ** License along with this library; if not, write to the Free Software 20 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 #include "bit_fiddling.hpp" 24 #include "count_setbits.hpp" 25 #include "memops.hpp" 26 #include "non_copyable.hpp" 27 28 #include <boost/mpl/accumulate.hpp> 29 #include <boost/mpl/assert.hpp> 30 #include <boost/mpl/at.hpp> 31 #include <boost/mpl/begin.hpp> 32 #include <boost/mpl/deref.hpp> 33 #include <boost/mpl/empty.hpp> 34 #include <boost/mpl/find_if.hpp> 35 #include <boost/mpl/int.hpp> 36 #include <boost/mpl/next.hpp> 37 #include <boost/mpl/not.hpp> 38 #include <boost/mpl/placeholders.hpp> 39 #include <boost/mpl/plus.hpp> 40 #include <boost/mpl/size.hpp> 41 #include <boost/mpl/sizeof.hpp> 42 #include <boost/mpl/transform_view.hpp> 43 44 #include <cassert> 45 #include <limits> 46 #include <cstdint> 47 #include <sstream> 48 #include <stdexcept> 49 #include <type_traits> 50 51 namespace jmmcg { 52 53 struct a_zero_sized_class {}; 54 55 namespace private_ { 56 57 /** 58 Avoid sizeof(void) warnings, give it a size of zero. 59 */ 60 template<class T> 61 struct give_void_a_size_of { 62 enum : std::size_t { 63 value=sizeof(T) 64 }; 65 }; 66 template<> 67 struct give_void_a_size_of<void> { 68 enum : std::size_t { 69 value=std::size_t() 70 }; 71 }; 72 template<> 73 struct give_void_a_size_of<a_zero_sized_class> { 74 enum : std::size_t { 75 value=std::size_t() 76 }; 77 }; 78 79 } 80 81 /// This container is an associative collection of types. The domain is a bit-map of the mapped_types that are selected in the range. 82 /** 83 This container packs a selection of objects of the types that are given in mapped_types into an internal buffer of contiguous memory. If few types are selected, then the size() returns much less than the size of the object (which must be large enough to holds all of the mapped_types). It could be useful for passing assorted large mapped_types along a slow network link, when, usually, not all are needed. 84 Note that the types placed in mapped_types must be able to support arbitrary alignment - no guarantee of correct alignment is made by this class. These types are packed into the internal buffer such that there is no padding used, hence no guarantee of correct alignment. This restriction implies that only PODs are likely to be supported, in general. 85 */ 86 template< 87 class BFSM, 88 std::size_t BFSz=sizeof(typename std::underlying_type<typename boost::mpl::deref<typename boost::mpl::begin<BFSM>::type>::type::first::value_type>::type) 89 > 90 class bitfield_map { 91 public: 92 using key_type=uint64_t; 93 using size_type=std::size_t; 94 using mapped_types=BFSM; ///< The range of mapped-types that may be selected by the domain. This range is a collection that is indexed by the unique enum-tag of the range. 95 using bitfields_tags_type=typename boost::mpl::deref<typename boost::mpl::begin<mapped_types>::type>::type::first::value_type; ///< The range of enum-tags used to indicate the requested element in the mapped_types range. 96 using underlying_key_type=typename std::underlying_type<bitfields_tags_type>::type; ///< The underlying type of the domain enum. 97 98 /** 99 Make sure the funky mpl actually finds something that is POD-like so could be an enumeration... 100 */ 101 BOOST_MPL_ASSERT((std::is_pod<bitfields_tags_type>)); 102 /** 103 Make sure the funky mpl actually finds something that is integral so could be an enumeration... 104 */ 105 BOOST_MPL_ASSERT((std::is_integral<typename std::underlying_type<bitfields_tags_type>::type>)); 106 107 enum : std::size_t { 108 range_mapped_types_size=boost::mpl::accumulate< 109 boost::mpl::transform_view<mapped_types, boost::mpl::sizeof_<boost::mpl::second<boost::mpl::_1>>>, 110 boost::mpl::int_<0u>::type, 111 boost::mpl::plus<boost::mpl::_1, boost::mpl::_2> 112 >::type::value, ///< The sum of the sizes of the types in mapped_types. 113 bitfields_size=BFSz, ///< The number of 8-bit bytes that the range of enum-tags should occupy. By default this is the sizeof(underlying_key_type). Note that enums require the underlying type to be an integral type, so this option allows for non-integral "underlying types". Also note that if this option is used it is the responsibility of the user to ensure that the number of bits in the underlying type can accommodate the number of enum-tags in the range. 114 mapped_types_size=boost::mpl::size<mapped_types>::value 115 }; 116 117 using raw_mapped_data_t=std::array<uint8_t, range_mapped_types_size>; 118 119 private: 120 using raw_key_type_t=std::array<uint8_t, bitfields_size>; 121 122 public: 123 enum : bool { 124 all_pod=std::is_same< 125 typename boost::mpl::find_if< 126 mapped_types, 127 boost::mpl::not_<std::is_pod<boost::mpl::second<boost::mpl::_1>>> 128 >::type, 129 typename boost::mpl::end<mapped_types>::type 130 >::type::value ///< True if all of the range mapped_types in the container are PODs otherwise false. 131 }; 132 133 BOOST_MPL_ASSERT_RELATION(sizeof(bitfields_tags_type), <=, sizeof(key_type)); 134 BOOST_MPL_ASSERT_RELATION(sizeof(underlying_key_type), ==, sizeof(bitfields_tags_type)); 135 BOOST_MPL_ASSERT_RELATION(range_mapped_types_size, >, 0); 136 BOOST_MPL_ASSERT_RELATION(boost::mpl::empty<mapped_types>::value, !=, true); 137 138 /// Default-construct an empty container. 139 /** 140 Algorithmic complexity: O(1) 141 Post-condition: empty()==true. 142 */ 143 constexpr bitfield_map() noexcept(true) FORCE_INLINE; 144 /// Bit-wise copy the contents of the argument into the constructed container, out of the argument. 145 /** 146 Algorithmic complexity: O(sizeof(range of mapped_types)) 147 Post-condition: bm.empty()==true. 148 */ 149 constexpr bitfield_map(bitfield_map const &) noexcept(true) FORCE_INLINE; 150 /** 151 Algorithmic complexity: O(sizeof(range of mapped_types)) 152 Post-condition: bm.empty()==true. 153 */ 154 constexpr bitfield_map(bitfield_map &&) noexcept(true) FORCE_INLINE; 155 /** 156 Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type)^2) 157 158 \see clear() 159 */ 160 ~bitfield_map() noexcept(true) FORCE_INLINE; 161 162 /// Bit-wise swap the contents of the container with that of the argument. 163 /** 164 Algorithmic complexity: O(sizeof(range of mapped_types)) 165 166 \see swap() 167 */ 168 constexpr bitfield_map &operator=(bitfield_map &&) noexcept(true) FORCE_INLINE; 169 170 /// Indicate if there are any elements selected in the mapped_types. 171 /** 172 Algorithmic complexity: O(1) 173 Invariant: emply() iff size()==0 174 175 \return true if no types have been selected in the mapped_types, otherwise false. 176 */ 177 constexpr bool empty() const noexcept(true) FORCE_INLINE; 178 179 /// Indicate the total size of any elements selected in the mapped_types, including the size of the domain bitfield. 180 /** 181 Algorithmic complexity: O(sizeof(key_type)) 182 Invariant: size()<=max_size() 183 184 \return The size, in bytes, of the types have been selected in the mapped_types, otherwise 0, always less than or equal to the sum of the sizes of the types in mapped_types, range_mapped_types_size. 185 186 \see max_size() 187 */ 188 constexpr size_type size() const noexcept(true) FORCE_INLINE; 189 190 /// Indicate the maximum size of all elements that can be selected in the mapped_types, including the size of the domain bitfield. 191 /** 192 Algorithmic complexity: O(1) 193 Invariant: size()<=max_size() 194 195 \return At least the size, in bytes, of all of the types have can be selected in the mapped_types. 196 197 \see size() 198 */ 199 static constexpr size_type max_size() noexcept(true) FORCE_INLINE; 200 201 /// Erase each enabled mapped_types selected in the key_type. 202 /** 203 Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type)^2) 204 Post-condition: empty()==true. 205 */ 206 void constexpr clear() noexcept(true) FORCE_INLINE; 207 208 /// Perform a range-checked selection of the requested element. 209 /** 210 Algorithmic complexity: O(sizeof(key_type)) 211 Pre-condition: empty()==false. 212 213 \return Return a reference to the enabled SelectedField in the mapped_types, otherwise throw a std::range_error exception.. 214 */ 215 template< 216 bitfields_tags_type SelectedField, 217 class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type, 218 class Ret=typename boost::mpl::at<mapped_types, AsType>::type 219 > 220 constexpr const Ret &at() const noexcept(false) FORCE_INLINE; 221 /// Perform a range-checked selection of the requested element. 222 /** 223 Algorithmic complexity: O(sizeof(key_type)) 224 Pre-condition: empty()==false. 225 226 \return Return a reference to the enabled SelectedField in the mapped_types, otherwise throw a std::range_error exception.. 227 */ 228 template< 229 bitfields_tags_type SelectedField, 230 class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type, 231 class Ret=typename boost::mpl::at<mapped_types, AsType>::type 232 > 233 constexpr Ret &at() noexcept(false) FORCE_INLINE; 234 235 /// Remove and delete the SelectedField element, if enabled, from the mapped_types range. 236 /** 237 Note: this is not a generic erase, items should only be erased from the end of the collection, otherwise undefined behaviour will result. This should really be considered a pop_back() operation, where the end iterator is manually specified. Not ideal. No compaction of the mapped_types is performed after an erase, so if holes were left, this would cause the at() operations to behave incorrectly. 238 Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type)) 239 Pre-condition: SelectedField is the last enabled bit in the key_type. 240 241 \see push_back() 242 */ 243 template< 244 bitfields_tags_type SelectedField, 245 class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type, 246 class Ret=typename boost::mpl::at<mapped_types, AsType>::type 247 > 248 void erase() noexcept(true) FORCE_INLINE; 249 250 /// Find if the SelectedField is enabled in the key_type, 251 /** 252 Algorithmic complexity: O(1) 253 254 \return True if the SelectedField is enabled in the key_type, otherwise false. 255 */ 256 template<bitfields_tags_type SelectedField> 257 bool find() const noexcept(true) FORCE_INLINE; 258 259 /// Enable the SelectedField in the key_type, and initialise the appropriate element in the mapped_types, if necessary, 260 /** 261 Note: This only works going from smaller to larger tags in the key_type, one must not insert in the middle, otherwise this may result in undefined behaviour. This is because of the internally-computed offsets into the range of mapped_types. Hence one must not insert randomly: it is likely that existing contents will be incorrectly overwritten, also leading to undefined behaviour. 262 Algorithmic complexity: O(sizeof(key_type)) 263 Post-condition: empty()==false. 264 265 \return A reference to the initialised element in the mapped_types. 266 */ 267 template< 268 bitfields_tags_type SelectedField, 269 class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type, 270 class Arg=typename boost::mpl::at<mapped_types, AsType>::type 271 > 272 void push_back(Arg const &) noexcept(false) FORCE_INLINE; 273 274 /// Bit-wise swap the contents of the container with that of the argument. 275 /** 276 Algorithmic complexity: O(sizeof(range of mapped_types)) 277 */ 278 void constexpr swap(bitfield_map &) noexcept(true) FORCE_INLINE; 279 280 private: 281 /** 282 A hack to convert non-integral underlying types to integral types to allow the bit-wise operators to work. 283 */ 284 union converter { 285 key_type conv_bfs; 286 raw_key_type_t bfs; ///< Here's hoping this is LSB-justified in the word, otherwise we are b'gg'r'd... 287 }; 288 key_type constexpr convert_to_biggest_integral_type() const noexcept(true); 289 290 raw_key_type_t bfs; 291 raw_mapped_data_t raw_mapped_data; 292 }__attribute__((packed)); 293 294 } 295 296 #include "bitfield_map_impl.hpp" 297 298 #endif