I'm working on a little endian machine and in my program I need to convert 2,3,4,5 or 8 bytes into network order before transmitting them over the network. I've written the following function which basically just reverses the bytes. I just wanted to make sure if it's logic is correct.
void convertLittleToBig(const uint8_t* in, uint8_t* out, const uint64_t& sizeInBytes) {
for(int i=0;i<sizeInBytes;++i)
out[i] = in[sizeInBytes-i-1];
}
The function assumes that 'out'
and 'in'
will be pointing at 'sizeInBytes'
memory.
If I'm not wrong, I can use the same function to convert from network order into little endian. Basically big endian and little endian, are mirror images of each other in terms of their byte order as per my current understanding.
Given below is a sample program:
#include <iostream>
#include <cstdint>
#include <iostream>
using std::cin;
//This function prints the raw bytes in memory.
void printBytes(const uint8_t* p, const uint64_t size) {
for(int i=0;i<size;++i)
printf("\nbyte[%d](%x)",i+1,p[i]);
printf("\n");
}
void convertLittleToBig(const uint8_t* in, uint8_t* out, const uint64_t& sizeInBytes) {
printf("\nCurrent representation\n");
printBytes(in,sizeInBytes);
for(int i=0;i<sizeInBytes;++i)
out[i] = in[sizeInBytes-i-1];
printf("\nRepresentation after conversion\n");
printBytes(out,sizeInBytes);
}
int main() {
uint64_t little;
printf("\nEnter a number that you wish to convert into big endian: ");
int64_t inp;
cin>>inp;
little=inp;
uint64_t big;
convertLittleToBig((uint8_t*)&little,(uint8_t*)&big,sizeof(big));
}
1 Answer 1
Includes
We have included <iostream>
twice, but missed <cstdio>
.
Misspelt standard library identifiers
std::printf
, std::uint8_t
and std::uint64_t
are consistently misspelt. You might sometimes get away with this, as your standard library is allowed to add global-namespace versions of those identifiers; since it's not required to do so, you have a portability bug.
Use appropriate size type
std::size_t
is the appropriate type to use for the size of an object, rather than std::uint64_t
(which could be unnecessarily big, or - theoretically, at least for now - too small).
Conversion function shouldn't have side-effects
The printf()
calls within convertLittleToBig
make it unusable in any serious program.
Consider a standard algorithm
The <algorithm>
header provides a very useful std::copy()
function we can use if we have a reverse iterator to copy from or to. We can get suitable iterators from std::span
views onto the inputs (from C++20 onwards).
Avoid raw pointers
We could do better, using a template to accept values and return by value, inferring the size from the argument type:
#include <algorithm>
#include <span>
template<typename T>
T convertLittleToBig(const T& val)
{
auto in = std::as_bytes(std::span(&val, 1));
T result;
auto out = std::as_writable_bytes(std::span(&result, 1));
std::copy(in.rbegin(), in.rend(), out.begin());
return result;
}
That's much simpler - no counting needed, and the caller doesn't need to use sizeof
(to work with odd-sized values, they need to be passed as arrays).
This is how we'd use it in a simple test program:
#include <cstddef>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
std::string to_hex(auto const& val)
{
std::ostringstream oss;
oss << std::hex << std::setfill('0');
auto in = std::as_bytes(std::span(&val, 1));
for (auto c: in) {
oss << std::setw(2) << std::to_integer<unsigned int>(c);
}
return oss.str();
}
// hex-print the value and its reversal
void demo_endian_swap(auto const& val)
{
std::cout << to_hex(val) << " -> "
<< to_hex(convertLittleToBig(val)) << '\n';
}
#include <array>
int main() {
// Demonstrate a selection of types
demo_endian_swap(std::uint16_t{0x1234});
demo_endian_swap(std::uint64_t{0x123456789abcde});
demo_endian_swap(std::array<unsigned char,5>{1,2,3,4,5});
//demo_endian_swap("abc"); // Invalid - can't return an array
//demo_endian_swap(std::string{"abc"}); // oops - reverses whole structure
}
Example output:
3412 -> 1234
debc9a7856341200 -> 00123456789abcde
0102030405 -> 0504030201
-
\$\begingroup\$ C++20 is a tall requirement, particularly for environments often operating at the byte level — e.g., microcontrollers, though OP doesn't mention anything about standards; and your answer/solution immediately made me regret how much time I spent "optimizing" my own. \$\endgroup\$ardnew– ardnew2024年05月15日 03:18:33 +00:00Commented May 15, 2024 at 3:18
-
\$\begingroup\$ @ardnew, what do you mean by a "tall requirement"?
std::span
is ideal for microcontroller operations. It's certainly worth being careful about code size and dynamic allocation, but there's a good subset of C++20 that's perfect for these applications. \$\endgroup\$Toby Speight– Toby Speight2024年05月21日 08:53:17 +00:00Commented May 21, 2024 at 8:53 -
\$\begingroup\$ Agreed, with all of those points. My point is that many microcontroller vendor-default toolchains still do not include support for C++20, or even C++17 (Arduino AVR, STM32[GFL], etc.), regardless if the language is well-suited for the target or not. \$\endgroup\$ardnew– ardnew2024年05月22日 17:35:43 +00:00Commented May 22, 2024 at 17:35
-
\$\begingroup\$ Ah yes, if you're stuck with proprietary toolchains life will be more difficult. That's unfortunate. \$\endgroup\$Toby Speight– Toby Speight2024年05月22日 17:41:30 +00:00Commented May 22, 2024 at 17:41
htonl => host to network long
linux.die.net/man/3/htonl \$\endgroup\$