Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bsls_alignmentutil
[Package bsls]

Provide constants, types, and operations related to alignment. More...

Namespaces

namespace  bsls

Detailed Description

Outline
Purpose:
Provide constants, types, and operations related to alignment.
Classes:
bsls::AlignmentUtil namespace for alignment functions, types, and constants
See also:
bslma_bufferimputil
Description:
This component defines a struct, bsls::AlignmentUtil, which serves as a namespace for compile-time constants, types, and operations associated with alignment on the current platform.
The BSLS_MAX_ALIGNMENT enumerator provides the minimal value that satisfies the alignment requirements for all types on the current platform. Additionally, the MaxAlignedType typedef specifies a primitive type that requires an alignment of BSLS_MAX_ALIGNMENT.
The calculateAlignmentFromSize function calculates a usable (but not necessarily minimal) alignment for any object with a memory footprint of the specified size (in bytes). The calculateAlignmentOffset function calculates the offset from a specified address where an object having a specified alignment requirement can be constructed safely. The is2ByteAligned, is4ByteAligned, and is8ByteAligned functions each return true if a specified address is, respectively, 2-, 4-, or 8-byte aligned. Finally, roundUpToMaximalAlignment returns the smallest whole multiple of BSLS_MAX_ALIGNMENT greater than or equal to its argument.
Assumptions:
The functionality in this component makes several assumptions:
  • The alignment for any given type is an integral power of 2.
  • The required alignment for a type T evenly divides sizeof(T), which implies that the required alignment for T is never larger than sizeof(T).
  • The required alignment for a struct, class, or union is the same as the required alignment of its most restrictive non-static data member (including the implicit virtual table pointer in polymorphic classes and internal pointers in virtually-derived classes).
Usage:
A sequential memory allocator is used to return a sequence of memory blocks of varying requested sizes from a larger chunk of contiguous memory. Each block returned must also have an alignment that is sufficient for any conceivable object of that size. To achieve a fully factored implementation, we might choose to provide a low-level helper function naturallyAlign that, given the address of the next available byte in the larger chunk along with the requested block size (in bytes), returns the first appropriately (or naturally) aligned address for the requested block at or after address:
  void *naturallyAlign(void **currentAddress, int size);
      // Return the closest memory address at or after the specified
      // '*currentAddress' that is sufficiently aligned to accommodate any
      // object of the specified 'size', and update '*currentAddress' to
      // refer to the first available byte after the allocated object.  The
      // behavior is undefined unless '1 <= size'.
We can implement the naturallyAlign helper function easily using the methods defined in this class:
  void *naturallyAlign(void **currentAddress, std::size_t size)
  {
      int   alignment = bsls::AlignmentUtil::calculateAlignmentFromSize(
                                                              size);
      int   offset    = bsls::AlignmentUtil::calculateAlignmentOffset(
                                                             *currentAddress,
                                                              alignment);
      char *result    = static_cast<char *>(*currentAddress) + offset;
      *currentAddress = result + size;
      return result;
  }
We will then be able to use this naturallyAlign helper function to allocate, from a buffer of contiguous memory, efficiently (but not necessarily optimally) aligned memory for objects of varying sizes based solely on the size of each object (i.e., determined by its natural, not actual, alignment).
To illustrate the functionality provided in this component, we begin by assuming that we have some user-defined type, MyType, comprising several data members:
  struct MyType {         // size 24; actual alignment 8; natural alignment 8
      int     d_int;
      double  d_double;   // Assume 8-byte alignment.
      char   *d_charPtr;  // Assume size <= 8 bytes.
  };
We then define a function, f, which starts off by creating a maximally aligned buffer on the program stack:
  void f()
  {
      // The remainder of the usage example is in the USAGE test case.
  }

      union {
          bsls::AlignmentUtil::MaxAlignedType d_dummy;  // force max. align.
          char                                d_buffer[BUFFER_SIZE];
      } buffer;
Next we use the bsls::AlignmentUtil functions directly to confirm that buffer is sufficiently aligned to accommodate a MaxAlignedType object:
      int alignment = bsls::AlignmentFromType<
                                 bsls::AlignmentUtil::MaxAlignedType>::VALUE;
      int offset =
                bsls::AlignmentUtil::calculateAlignmentOffset(
                                                             buffer.d_buffer,
                                                             alignment);
      assert(0 == offset);  // sufficient alignment
Below we perform various memory allocations using our naturallyAlign helper function:
      void *p         = static_cast<void *>(buffer.d_buffer);

      (void)            naturallyAlign(&p, sizeof(char));

      void *shortPtr5 = naturallyAlign(&p, 5 * sizeof(short));
Note that the address held in shortPtr is numerically divisible by the alignment of a short on the current platform:
      assert(0 == ((static_cast<char *>(shortPtr5) - buffer.d_buffer) %
                                     bsls::AlignmentFromType<short>::VALUE));

      assert(bsls::AlignmentUtil::is2ByteAligned(shortPtr5));
Next we use naturallyAlign to allocate a block of appropriate size and sufficient alignment to store a MyType object:
      void *objPtr = naturallyAlign(&p, sizeof(MyType));
Note that the alignment of the address held in objPtr is numerically divisible by the actual alignment requirement: Assuming buffer has sufficient capacity, and the alignments for char, short, and MyType are, respectively, 1, 2, and 8, we would expect this layout within buffer.d_buffer:
  charPtr shortPtr5                            objPtr
  |       |                                                       |
  V       V                                                       V
  .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.-
  |ccc|   |sssssss:sssssss:sssssss:sssssss:sssssss|   :   :   :   |oooooo...
  ^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^-
  0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18
Note that on an atypical 32-bit platform where a double is 4-byte aligned, the actual alignment of MyType would be 4, but its natural alignment would still be 8 because its size would be 16; it is highly unlikely that MyType would have an actual (and therefore natural) alignment of 4 on a 64-bit platform when using default compiler settings.