bitunion.hh revision 12895
1/* 2 * Copyright (c) 2007-2008 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Gabe Black 29 */ 30 31#ifndef __BASE_BITUNION_HH__ 32#define __BASE_BITUNION_HH__ 33 34#include <functional> 35#include <iostream> 36#include <type_traits> 37#include <typeinfo> 38 39#include "base/bitfield.hh" 40 41// The following implements the BitUnion system of defining bitfields 42//on top of an underlying class. This is done through the pervasive use of 43//both named and unnamed unions which all contain the same actual storage. 44//Since they're unioned with each other, all of these storage locations 45//overlap. This allows all of the bitfields to manipulate the same data 46//without having to have access to each other. More details are provided with 47//the individual components. 48 49//This class wraps around another which defines getter/setter functions which 50//manipulate the underlying data. The type of the underlying data and the type 51//of the bitfield itself are inferred from the argument types of the setter 52//function. 53template<class Base> 54class BitfieldTypeImpl : public Base 55{ 56 static_assert(std::is_empty<Base>::value, 57 "Bitfield base class must be empty."); 58 59 private: 60 61 struct TypeDeducer 62 { 63 template<typename> 64 struct T; 65 66 template<typename C, typename Type1, typename Type2> 67 struct T<void (C::*)(Type1 &, Type2)> 68 { 69 typedef Type1 Storage; 70 typedef Type2 Type; 71 }; 72 73 struct Wrapper : public Base 74 { 75 using Base::setter; 76 }; 77 78 typedef typename T<decltype(&Wrapper::setter)>::Storage Storage; 79 typedef typename T<decltype(&Wrapper::setter)>::Type Type; 80 }; 81 82 protected: 83 typedef typename TypeDeducer::Storage Storage; 84 typedef typename TypeDeducer::Type Type; 85 86 Type getter(const Storage &storage) const = delete; 87 void setter(Storage &storage, Type val) = delete; 88 89 Storage __storage; 90 91 operator Type () const 92 { 93 return Base::getter(__storage); 94 } 95 96 Type 97 operator=(const Type val) 98 { 99 Base::setter(__storage, val); 100 return val; 101 } 102 103 Type 104 operator=(BitfieldTypeImpl<Base> const & other) 105 { 106 return *this = (Type)other; 107 } 108}; 109 110//A wrapper for the above class which allows setting and getting. 111template<class Base> 112class BitfieldType : public BitfieldTypeImpl<Base> 113{ 114 protected: 115 using Impl = BitfieldTypeImpl<Base>; 116 using typename Impl::Type; 117 118 public: 119 operator Type () const { return Impl::operator Type(); } 120 Type operator=(const Type val) { return Impl::operator=(val); } 121 Type 122 operator=(BitfieldType<Base> const & other) 123 { 124 return Impl::operator=(other); 125 } 126}; 127 128//A wrapper which only supports getting. 129template<class Base> 130class BitfieldROType : public BitfieldTypeImpl<Base> 131{ 132 public: 133 using Impl = BitfieldTypeImpl<Base>; 134 using typename Impl::Type; 135 136 Type operator=(BitfieldROType<Base> const &other) = delete; 137 operator Type () const { return Impl::operator Type(); } 138}; 139 140//A wrapper which only supports setting. 141template <class Base> 142class BitfieldWOType : public BitfieldTypeImpl<Base> 143{ 144 protected: 145 using Impl = BitfieldTypeImpl<Base>; 146 using typename Impl::Type; 147 148 public: 149 Type operator=(const Type val) { return Impl::operator=(val); } 150 Type 151 operator=(BitfieldWOType<Base> const & other) 152 { 153 return Impl::operator=(other); 154 } 155}; 156 157//This namespace is for classes which implement the backend of the BitUnion 158//stuff. Don't use any of these directly. 159namespace BitfieldBackend 160{ 161 template<class Storage, int first, int last> 162 class Unsigned 163 { 164 static_assert(first >= last, 165 "Bitfield ranges must be specified as <msb, lsb>"); 166 167 protected: 168 uint64_t 169 getter(const Storage &storage) const 170 { 171 return bits(storage, first, last); 172 } 173 174 void 175 setter(Storage &storage, uint64_t val) 176 { 177 replaceBits(storage, first, last, val); 178 } 179 }; 180 181 template<class Storage, int first, int last> 182 class Signed 183 { 184 static_assert(first >= last, 185 "Bitfield ranges must be specified as <msb, lsb>"); 186 187 protected: 188 int64_t 189 getter(const Storage &storage) const 190 { 191 return sext<first - last + 1>(bits(storage, first, last)); 192 } 193 194 void 195 setter(Storage &storage, int64_t val) 196 { 197 replaceBits(storage, first, last, val); 198 } 199 }; 200 201 //This class contains the basic bitfield types which are automatically 202 //available within a BitUnion. They inherit their Storage type from the 203 //containing BitUnion. 204 template<class Storage> 205 class BitfieldTypes 206 { 207 protected: 208 209 template<int first, int last=first> 210 using Bitfield = BitfieldType<Unsigned<Storage, first, last> >; 211 template<int first, int last=first> 212 using BitfieldRO = 213 BitfieldROType<Unsigned<Storage, first, last> >; 214 template<int first, int last=first> 215 using BitfieldWO = 216 BitfieldWOType<Unsigned<Storage, first, last> >; 217 218 template<int first, int last=first> 219 using SignedBitfield = 220 BitfieldType<Signed<Storage, first, last> >; 221 template<int first, int last=first> 222 using SignedBitfieldRO = 223 BitfieldROType<Signed<Storage, first, last> >; 224 template<int first, int last=first> 225 using SignedBitfieldWO = 226 BitfieldWOType<Signed<Storage, first, last> >; 227 }; 228 229 //When a BitUnion is set up, an underlying class is created which holds 230 //the actual union. This class then inherits from it, and provids the 231 //implementations for various operators. Setting things up this way 232 //prevents having to redefine these functions in every different BitUnion 233 //type. More operators could be implemented in the future, as the need 234 //arises. 235 template <class Base> 236 class BitUnionOperators : public Base 237 { 238 static_assert(sizeof(Base) == sizeof(typename Base::__StorageType), 239 "BitUnion larger than its storage type."); 240 241 public: 242 BitUnionOperators(typename Base::__StorageType const &val) 243 { 244 Base::__storage = val; 245 } 246 247 BitUnionOperators() {} 248 249 operator const typename Base::__StorageType () const 250 { 251 return Base::__storage; 252 } 253 254 typename Base::__StorageType 255 operator=(typename Base::__StorageType const &val) 256 { 257 Base::__storage = val; 258 return val; 259 } 260 261 typename Base::__StorageType 262 operator=(BitUnionOperators const &other) 263 { 264 Base::__storage = other; 265 return Base::__storage; 266 } 267 268 bool 269 operator<(Base const &base) const 270 { 271 return Base::__storage < base.__storage; 272 } 273 274 bool 275 operator==(Base const &base) const 276 { 277 return Base::__storage == base.__storage; 278 } 279 }; 280} 281 282//This macro is a backend for other macros that specialize it slightly. 283//First, it creates/extends a namespace "BitfieldUnderlyingClasses" and 284//sticks the class which has the actual union in it, which 285//BitfieldOperators above inherits from. Putting these classes in a special 286//namespace ensures that there will be no collisions with other names as long 287//as the BitUnion names themselves are all distinct and nothing else uses 288//the BitfieldUnderlyingClasses namespace, which is unlikely. The class itself 289//creates a typedef of the "type" parameter called __StorageType. This allows 290//the type to propagate outside of the macro itself in a controlled way. 291//Finally, the base storage is defined which BitfieldOperators will refer to 292//in the operators it defines. This macro is intended to be followed by 293//bitfield definitions which will end up inside it's union. As explained 294//above, these is overlayed the __storage member in its entirety by each of the 295//bitfields which are defined in the union, creating shared storage with no 296//overhead. 297#define __BitUnion(type, name) \ 298 class BitfieldUnderlyingClasses##name : \ 299 public BitfieldBackend::BitfieldTypes<type> \ 300 { \ 301 protected: \ 302 typedef type __StorageType; \ 303 friend BitfieldBackend::BitUnionBaseType< \ 304 BitfieldBackend::BitUnionOperators< \ 305 BitfieldUnderlyingClasses##name> >; \ 306 friend BitfieldBackend::BitUnionBaseType< \ 307 BitfieldUnderlyingClasses##name>; \ 308 public: \ 309 union { \ 310 type __storage; 311 312//This closes off the class and union started by the above macro. It is 313//followed by a typedef which makes "name" refer to a BitfieldOperator 314//class inheriting from the class and union just defined, which completes 315//building up the type for the user. 316#define EndBitUnion(name) \ 317 }; \ 318 }; \ 319 typedef BitfieldBackend::BitUnionOperators< \ 320 BitfieldUnderlyingClasses##name> name; 321 322//This sets up a bitfield which has other bitfields nested inside of it. The 323//__storage member functions like the "underlying storage" of the top level 324//BitUnion. Like everything else, it overlays with the top level storage, so 325//making it a regular bitfield type makes the entire thing function as a 326//regular bitfield when referred to by itself. 327#define __SubBitUnion(name, fieldType, ...) \ 328 class \ 329 { \ 330 public: \ 331 union { \ 332 fieldType<__VA_ARGS__> __storage; 333 334//This closes off the union created above and gives it a name. Unlike the top 335//level BitUnion, we're interested in creating an object instead of a type. 336//The operators are defined in the macro itself instead of a class for 337//technical reasons. If someone determines a way to move them to one, please 338//do so. 339#define EndSubBitUnion(name) \ 340 }; \ 341 inline operator __StorageType () const \ 342 { return __storage; } \ 343 \ 344 inline __StorageType operator = (const __StorageType & _storage) \ 345 { return __storage = _storage;} \ 346 } name; 347 348//Regular bitfields 349//These define macros for read/write regular bitfield based subbitfields. 350#define SubBitUnion(name, first, last) \ 351 __SubBitUnion(name, Bitfield, first, last) 352 353//Regular bitfields 354//These define macros for read/write regular bitfield based subbitfields. 355#define SignedSubBitUnion(name, first, last) \ 356 __SubBitUnion(name, SignedBitfield, first, last) 357 358//Use this to define an arbitrary type overlayed with bitfields. 359#define BitUnion(type, name) __BitUnion(type, name) 360 361//Use this to define conveniently sized values overlayed with bitfields. 362#define BitUnion64(name) __BitUnion(uint64_t, name) 363#define BitUnion32(name) __BitUnion(uint32_t, name) 364#define BitUnion16(name) __BitUnion(uint16_t, name) 365#define BitUnion8(name) __BitUnion(uint8_t, name) 366 367 368//These templates make it possible to define other templates related to 369//BitUnions without having to refer to internal typedefs or the BitfieldBackend 370//namespace. 371 372//To build a template specialization which works for all BitUnions, accept a 373//template argument T, and then use BitUnionType<T> as an argument in the 374//template. To refer to the basic type the BitUnion wraps, use 375//BitUnionBaseType<T>. 376 377//For example: 378//template <typename T> 379//void func(BitUnionType<T> u) { BitUnionBaseType<T> b = u; } 380 381//Also, BitUnionBaseType can be used on a BitUnion type directly. 382 383template <typename T> 384using BitUnionType = BitfieldBackend::BitUnionOperators<T>; 385 386namespace BitfieldBackend 387{ 388 template<typename T> 389 struct BitUnionBaseType 390 { 391 typedef typename BitUnionType<T>::__StorageType Type; 392 }; 393 394 template<typename T> 395 struct BitUnionBaseType<BitUnionType<T> > 396 { 397 typedef typename BitUnionType<T>::__StorageType Type; 398 }; 399} 400 401template <typename T> 402using BitUnionBaseType = typename BitfieldBackend::BitUnionBaseType<T>::Type; 403 404 405//An STL style hash structure for hashing BitUnions based on their base type. 406namespace std 407{ 408 template <typename T> 409 struct hash<BitUnionType<T> > : public hash<BitUnionBaseType<T> > 410 { 411 size_t 412 operator() (const BitUnionType<T> &val) const 413 { 414 return hash<BitUnionBaseType<T> >::operator()(val); 415 } 416 }; 417} 418 419 420namespace BitfieldBackend 421{ 422 423 template<typename T> 424 static inline std::ostream & 425 bitfieldBackendPrinter(std::ostream &os, const T &t) 426 { 427 os << t; 428 return os; 429 } 430 431 //Since BitUnions are generally numerical values and not character codes, 432 //these specializations attempt to ensure that they get cast to integers 433 //of the appropriate type before printing. 434 template <> 435 inline std::ostream & 436 bitfieldBackendPrinter(std::ostream &os, const char &t) 437 { 438 os << (int)t; 439 return os; 440 } 441 442 template <> 443 inline std::ostream & 444 bitfieldBackendPrinter(std::ostream &os, const unsigned char &t) 445 { 446 os << (unsigned int)t; 447 return os; 448 } 449} 450 451//A default << operator which casts a bitunion to its underlying type and 452//passes it to BitfieldBackend::bitfieldBackendPrinter. 453template <typename T> 454std::ostream & 455operator << (std::ostream &os, const BitUnionType<T> &bu) 456{ 457 return BitfieldBackend::bitfieldBackendPrinter( 458 os, (BitUnionBaseType<T>)bu); 459} 460 461#endif // __BASE_BITUNION_HH__ 462