HiveBrain v1.2.0
Get Started
← Back to all entries
patterncModerate

Implementation of C Standard Library Function ntohl()

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ntohlfunctionstandardlibraryimplementation

Problem

This is an implementation of ntohl() that I wrote as an exercise. ntohl() takes a uint32_t value and returns it unchanged if the host architecture is network-byte-order (big-endian), otherwise the value is converted to host-byte-order.

My version converts to little-endian; is it always the case that host-byte-order is taken to mean little-endian? This appears to be the case, from what I have read, but what if the host architecture is middle-endian? Do real implementations of ntohl() detect other byte-orders, or strictly big- and little-endian?

I am also interested in any comments about the use of a union to detect endianness on the host machine, suggestions and comparison with other methods, and similarly, comments and suggestions relating to the use of bitwise operators to perform the conversion from big-endian to little-endian.

#ifndef _STDINT_H
#include 
#endif

uint32_t my_ntohl(uint32_t netlong)
{
    union {
        uint16_t num;
        uint8_t bytes[2];
    } endian_test = { .bytes = { 0x01, 0x00 }};

    if (endian_test.num == 0x0001) {
        netlong = (netlong > 8) | (netlong >> 24);
    }

    return netlong;
}

Solution

Unless you wish to optimize the code, with specialized swappers for various hosts orders, you are doing it wrong.

I invite you to check The Byte Order Fallacy by Rob Pike. The punch line: the byte order of the computer you are executing the code on doesn't matter, because the language abstracts it for you.

Thus, only the byte order of the network matters, and the network is big-endian:

#include 
#include 

uint32_t ntohl(uint32_t const net) {
    uint8_t data[4] = {};
    memcpy(&data, &net, sizeof(data));

    return ((uint32_t) data[3] << 0)
         | ((uint32_t) data[2] << 8)
         | ((uint32_t) data[1] << 16)
         | ((uint32_t) data[0] << 24);
}


This function will work no matter the endianness of the host, even on the crazy middle-endians ones.

Oh, and it optimizes well in general, in case you were wondering:

ntohl(unsigned int):
        mov     eax, edi
        bswap   eax
        ret


bswap being the native CPU instruction to swap bytes on x86.

Code Snippets

#include <stdint.h>
#include <string.h>

uint32_t ntohl(uint32_t const net) {
    uint8_t data[4] = {};
    memcpy(&data, &net, sizeof(data));

    return ((uint32_t) data[3] << 0)
         | ((uint32_t) data[2] << 8)
         | ((uint32_t) data[1] << 16)
         | ((uint32_t) data[0] << 24);
}
ntohl(unsigned int):
        mov     eax, edi
        bswap   eax
        ret

Context

StackExchange Code Review Q#149717, answer score: 17

Revisions (0)

No revisions yet.