Introduction
SequenceByte container
Platform-specific solution
Another approach
Introduction
A byte array is often declared as follows.
unsigned char byteArray[100]; // 'unsigned char' may or may not be 8 bits wide
This works fine on a byte-addressable system where each memory location holds a byte (an 8-bit value, an octet).
On a word-addressable system, however, there is no concept of a memory byte. Instead, each memory location holds a word, in many cases a 32-bit value. Moreover, 8- and 16-bit integer types (e.g. uint8_t
) do not exist.
A portable byte (octet) array can be declared as follows.
#include <cstdint> std::uint_least8_t byteArray[100]; // 'uint_least8_t' is the smallest integer type at least 8 bits wide
This declaration, although portable, can lead to inefficient use of memory – each memory location (a word on some platforms) is used to hold (only) a single 8-bit value (a byte).
SequenceByte container
PETR provides SequenceByte array-like container class template that can effortlessly store multiple bytes in a word.
#include "petr/Sequence.hpp" Petr::Container::SequenceByte<100, Petr::Container::SEQUENCE_BYTE_PACKED_32_LITTLE_ENDIAN_FAST> byteArray; // bytes packed into words
In the above example, SequenceByte (an array of 100 bytes) will pack four bytes into a word, starting with the least significant byte.
Platform-specific solution
Byte array can be defined flexibly, according to the target platform as shown below.
#include "petr/Platform.h" #include "petr/Sequence.hpp" template <std::size_t CAPACITY> class ByteArray : # if defined(PETR_BYTE_ADDRESSABLE_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_BASIC_FAST> # elif defined(PETR_WORD_ADDRESSABLE_PLATFORM) && defined(PETR_LITTLE_ENDIAN_BYTE_ORDER_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_PACKED_32_LITTLE_ENDIAN_FAST> # elif defined(PETR_WORD_ADDRESSABLE_PLATFORM) && defined(PETR_BIG_ENDIAN_BYTE_ORDER_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_PACKED_32_BIG_ENDIAN_FAST> # else public Petr::Container::Sequence<std::uint_least8_t, CAPACITY, Petr::Container::SEQUENCE_FAST> # endif {}; ByteArray<100> byteArray;
Individual bytes can be written to
ByteArray<100>::Index N = byteArray.getCapacity(); for (ByteArray<100>::Index i = 0; i < N; ++i) { byteArray[i] = i % 256; }
and read from.
int long unsigned sum = 0; for (ByteArray<100>::Index i = 0; i < N; ++i) { sum += byteArray[i]; }
On byte-addressable platforms, discrete bytes are accessed directly. On word-addressable platforms, bytes are transparently packed into words. On other platforms, bytes are stored without packing.
#include <cstddef> #include <cstdint> #include "petr/Platform.h" #include "petr/Sequence.hpp" template <std::size_t CAPACITY> class ByteArray : # if defined(PETR_BYTE_ADDRESSABLE_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_BASIC_FAST> # elif defined(PETR_WORD_ADDRESSABLE_PLATFORM) && defined(PETR_LITTLE_ENDIAN_BYTE_ORDER_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_PACKED_32_LITTLE_ENDIAN_FAST> # elif defined(PETR_WORD_ADDRESSABLE_PLATFORM) && defined(PETR_BIG_ENDIAN_BYTE_ORDER_PLATFORM) public Petr::Container::SequenceByte< CAPACITY, Petr::Container::SEQUENCE_BYTE_PACKED_32_BIG_ENDIAN_FAST> # else public Petr::Container::Sequence<std::uint_least8_t, CAPACITY, Petr::Container::SEQUENCE_FAST> # endif {}; ByteArray<100> byteArray; ByteArray<100>::Index N = byteArray.getCapacity(); for (ByteArray<100>::Index i = 0; i < N; ++i) { byteArray[i] = i % 256; } int long unsigned sum = 0; for (ByteArray<100>::Index i = 0; i < N; ++i) { sum += byteArray[i]; }
Another approach
When developing code for a limited set of known target platforms, one may be able to select a single container type suitable for all of the platforms. For example, when writing code for a byte-addressable little-endian byte order (e.g. ARM Cortex-A in LE mode i.e. x86 compatible) and word-addressable (e.g. ADI SHARC) platforms mentioned above, a sequence container packing four bytes into a single word in little-endian order can serve both types of platforms. Advantage of this approach is simpler implementation. Disadvantage is that, depending on the optimizer, code compiled for the byte-addressable platform may not run as fast as it could.
#include "petr/Sequence.hpp" Petr::Container::SequenceByte<100, Petr::Container::SEQUENCE_BYTE_PACKED_32_LITTLE_ENDIAN_FAST> byteArray;
Note that packing bytes into words in the same order as the byte order of the byte-addressable platform ensures that logical access and memory layout of the sequence container data are conveniently matching.
Casting raw data location and accessing the data using a byte pointer on a byte-addressable platform yields the same order of values as accessing the packed byte sequence container via an index.