4
\$\begingroup\$

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));
}
\$\endgroup\$
  • \$\begingroup\$ There are standard libs for this: htonl => host to network long linux.die.net/man/3/htonl \$\endgroup\$ – Martin York Feb 19 at 20:23
  • \$\begingroup\$ @MartinYork I have to convert 2,3,4,5 or 8 bytes. \$\endgroup\$ – Vishal Sharma Feb 20 at 8:44
6
\$\begingroup\$

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
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.