libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
bitfield_map.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_BITFIELD_MAP_HPP
2 #define LIBJMMCG_CORE_BITFIELD_MAP_HPP
3 /******************************************************************************
4 ** Copyright © 2014 by J.M.McGuiness, coder@hussar.me.uk
5 **
6 ** This library is free software; you can redistribute it and/or
7 ** modify it under the terms of the GNU Lesser General Public
8 ** License as published by the Free Software Foundation; either
9 ** version 2.1 of the License, or (at your option) any later version.
10 **
11 ** This library is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ** Lesser General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public
17 ** License along with this library; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 
21 #include "bit_fiddling.hpp"
22 #include "count_setbits.hpp"
23 #include "memops.hpp"
24 #include "sizeof_void.hpp"
25 
26 #include <boost/mpl/accumulate.hpp>
27 #include <boost/mpl/assert.hpp>
28 #include <boost/mpl/at.hpp>
29 #include <boost/mpl/begin.hpp>
30 #include <boost/mpl/deref.hpp>
31 #include <boost/mpl/empty.hpp>
32 #include <boost/mpl/find_if.hpp>
33 #include <boost/mpl/int.hpp>
34 #include <boost/mpl/next.hpp>
35 #include <boost/mpl/not.hpp>
36 #include <boost/mpl/placeholders.hpp>
37 #include <boost/mpl/plus.hpp>
38 #include <boost/mpl/size.hpp>
39 #include <boost/mpl/sizeof.hpp>
40 #include <boost/mpl/transform_view.hpp>
41 
42 #include <cassert>
43 #include <limits>
44 #include <cstdint>
45 #include <sstream>
46 #include <stdexcept>
47 #include <type_traits>
48 
49 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
50 
51 /// This container is an associative collection of types. The domain is a bit-map of the mapped_types that are selected in the range.
52 /**
53  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.
54  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.
55 */
56 template<
57  class BFSM,
58  std::size_t BFSz=sizeof(typename std::underlying_type<typename boost::mpl::deref<typename boost::mpl::begin<BFSM>::type>::type::first::value_type>::type)
59 >
60 class bitfield_map {
61 public:
62  using key_type=uint64_t;
63  using size_type=std::size_t;
64  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.
65  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.
66  using underlying_key_type=typename std::underlying_type<bitfields_tags_type>::type; ///< The underlying type of the domain enum.
67 
68  /**
69  Make sure the funky mpl actually finds something that is POD-like so could be an enumeration...
70  */
71  BOOST_MPL_ASSERT((std::is_pod<bitfields_tags_type>));
72  /**
73  Make sure the funky mpl actually finds something that is integral so could be an enumeration...
74  */
75  BOOST_MPL_ASSERT((std::is_integral<typename std::underlying_type<bitfields_tags_type>::type>));
76 
77  enum : std::size_t {
78  range_mapped_types_size=boost::mpl::accumulate<
79  boost::mpl::transform_view<mapped_types, boost::mpl::sizeof_<boost::mpl::second<boost::mpl::_1>>>,
80  boost::mpl::int_<0u>::type,
81  boost::mpl::plus<boost::mpl::_1, boost::mpl::_2>
82  >::type::value, ///< The sum of the sizes of the types in mapped_types.
83  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.
84  mapped_types_size=boost::mpl::size<mapped_types>::value
85  };
86 
87  using raw_mapped_data_t=std::array<uint8_t, range_mapped_types_size>;
88 
89 private:
90  using raw_key_type_t=std::array<uint8_t, bitfields_size>;
91 
92 public:
93  enum : bool {
94  all_pod=std::is_same<
95  typename boost::mpl::find_if<
96  mapped_types,
97  boost::mpl::not_<std::is_pod<boost::mpl::second<boost::mpl::_1>>>
98  >::type,
99  typename boost::mpl::end<mapped_types>::type
100  >::type::value ///< True if all of the range mapped_types in the container are PODs otherwise false.
101  };
102 
103  BOOST_MPL_ASSERT_RELATION(sizeof(bitfields_tags_type), <=, sizeof(key_type));
104  BOOST_MPL_ASSERT_RELATION(sizeof(underlying_key_type), ==, sizeof(bitfields_tags_type));
105  BOOST_MPL_ASSERT_RELATION(range_mapped_types_size, >, 0);
106  BOOST_MPL_ASSERT_RELATION(boost::mpl::empty<mapped_types>::value, !=, true);
107 
108  /// Default-construct an empty container.
109  /**
110  Algorithmic complexity: O(1)
111  Post-condition: empty()==true.
112  */
113  constexpr bitfield_map() noexcept(true) FORCE_INLINE;
114  /// Bit-wise copy the contents of the argument into the constructed container, out of the argument.
115  /**
116  Algorithmic complexity: O(sizeof(range of mapped_types))
117  Post-condition: bm.empty()==true.
118  */
119  constexpr bitfield_map(bitfield_map const &) noexcept(true) FORCE_INLINE;
120  /**
121  Algorithmic complexity: O(sizeof(range of mapped_types))
122  Post-condition: bm.empty()==true.
123  */
124  constexpr bitfield_map(bitfield_map &&) noexcept(true) FORCE_INLINE;
125  /**
126  Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type)^2)
127 
128  \see clear()
129  */
130  ~bitfield_map() noexcept(true) FORCE_INLINE;
131 
132  /// Bit-wise swap the contents of the container with that of the argument.
133  /**
134  Algorithmic complexity: O(sizeof(range of mapped_types))
135 
136  \see swap()
137  */
138  constexpr bitfield_map &operator=(bitfield_map &&) noexcept(true) FORCE_INLINE;
139 
140  /// Indicate if there are any elements selected in the mapped_types.
141  /**
142  Algorithmic complexity: O(1)
143  Invariant: emply() iff size()==0
144 
145  \return true if no types have been selected in the mapped_types, otherwise false.
146  */
147  constexpr bool empty() const noexcept(true) FORCE_INLINE;
148 
149  /// Indicate the total size of any elements selected in the mapped_types, including the size of the domain bitfield.
150  /**
151  Algorithmic complexity: O(sizeof(key_type))
152  Invariant: size()<=max_size()
153 
154  \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.
155 
156  \see max_size()
157  */
158  constexpr size_type size() const noexcept(true) FORCE_INLINE;
159 
160  /// Indicate the maximum size of all elements that can be selected in the mapped_types, including the size of the domain bitfield.
161  /**
162  Algorithmic complexity: O(1)
163  Invariant: size()<=max_size()
164 
165  \return At least the size, in bytes, of all of the types have can be selected in the mapped_types.
166 
167  \see size()
168  */
169  static constexpr size_type max_size() noexcept(true) FORCE_INLINE;
170 
171  /// Erase each enabled mapped_types selected in the key_type.
172  /**
173  Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type)^2)
174  Post-condition: empty()==true.
175  */
176  void constexpr clear() noexcept(true) FORCE_INLINE;
177 
178  /// Perform a range-checked selection of the requested element.
179  /**
180  Algorithmic complexity: O(sizeof(key_type))
181  Pre-condition: empty()==false.
182 
183  \return Return a reference to the enabled SelectedField in the mapped_types, otherwise throw a std::range_error exception..
184  */
185  template<
186  bitfields_tags_type SelectedField,
188  class Ret=typename boost::mpl::at<mapped_types, AsType>::type
189  >
190  constexpr const Ret &at() const noexcept(false) FORCE_INLINE;
191  /// Perform a range-checked selection of the requested element.
192  /**
193  Algorithmic complexity: O(sizeof(key_type))
194  Pre-condition: empty()==false.
195 
196  \return Return a reference to the enabled SelectedField in the mapped_types, otherwise throw a std::range_error exception..
197  */
198  template<
199  bitfields_tags_type SelectedField,
200  class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type,
201  class Ret=typename boost::mpl::at<mapped_types, AsType>::type
202  >
203  constexpr Ret &at() noexcept(false) FORCE_INLINE;
204 
205  /// Remove and delete the SelectedField element, if enabled, from the mapped_types range.
206  /**
207  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.
208  Algorithmic complexity: POD: O(1) otherwise O(sizeof(key_type))
209  Pre-condition: SelectedField is the last enabled bit in the key_type.
210 
211  \see push_back()
212  */
213  template<
214  bitfields_tags_type SelectedField,
215  class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type,
216  class Ret=typename boost::mpl::at<mapped_types, AsType>::type
217  >
218  void erase() noexcept(true) FORCE_INLINE;
219 
220  /// Find if the SelectedField is enabled in the key_type,
221  /**
222  Algorithmic complexity: O(1)
223 
224  \return True if the SelectedField is enabled in the key_type, otherwise false.
225  */
226  template<bitfields_tags_type SelectedField>
227  bool find() const noexcept(true) FORCE_INLINE;
228 
229  /// Enable the SelectedField in the key_type, and initialise the appropriate element in the mapped_types, if necessary,
230  /**
231  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.
232  Algorithmic complexity: O(sizeof(key_type))
233  Post-condition: empty()==false.
234 
235  \return A reference to the initialised element in the mapped_types.
236  */
237  template<
238  bitfields_tags_type SelectedField,
239  class AsType=typename std::integral_constant<bitfields_tags_type, SelectedField>::type,
240  class Arg=typename boost::mpl::at<mapped_types, AsType>::type
241  >
242  void push_back(Arg const &) noexcept(false) FORCE_INLINE;
243 
244  /// Bit-wise swap the contents of the container with that of the argument.
245  /**
246  Algorithmic complexity: O(sizeof(range of mapped_types))
247  */
248  void constexpr swap(bitfield_map &) noexcept(true) FORCE_INLINE;
249 
250 private:
251  /**
252  A hack to convert non-integral underlying types to integral types to allow the bit-wise operators to work.
253  */
254  union converter {
255  key_type conv_bfs;
256  raw_key_type_t bfs; ///< Here's hoping this is LSB-justified in the word, otherwise we are b'gg'r'd...
257  };
258  key_type constexpr convert_to_biggest_integral_type() const noexcept(true);
259 
260  raw_key_type_t bfs;
261  raw_mapped_data_t raw_mapped_data;
262 }__attribute__((packed));
263 
264 } }
265 
267 
268 #endif