4
votes

fwrite an integer depends on endianness, but is there a way to write an integer 0x00000004 to a file such that it can be fread always as 0x00000004 regardless of machine it's run on.

  • One thought is to write the first bit, then second, then third always in a specific order,such as grabbing each decimal value using modulus or bit shifting.

  • Not sure if there's a way to force fwrite to write as little endian without having some convoluted check to see if the current system is big endian, and then reversing the bits before fwriting.

  • Another thought is to store it in the file as ascii, which isn't a huge deal it just turns 4 bytes into maybe 8 (hex). But I figure this is just a lazy solution.

I want to fwrite and fread from possibly different machines, so both operations need to be able to fwrite and fread the same endianness, and I'm not sure (after searching this site) on a portable way to do this (without using some obscure library that may or may not be on certain machines).

3
Various approaches at stackoverflow.com/questions/7897771/…, such as using ntohl/htonl.Martin R
As I understood it, ntohl is not that portable.jake
Yes, but it is part of the POSIX specification: pubs.opengroup.org/onlinepubs/9699919799/functions/htonl.html. If you don't want to depend on that then you can write the bytes, not bits, in network independent order.Martin R
Do the bits within the byte not have endianness? Or just the bytes. Like if a byte is 8 bits, 00000001, will writing that byte to a file on any machine make it always stay the same or...?jake
How do you transfer the data between the machines? For networking it should handled "automatically", see for example stackoverflow.com/questions/25055714/….Martin R

3 Answers

2
votes

I don't think you need to worry about bit-level endianness, I think they are always the same how is data stored at bit level according to "Endianness"?

I would probably use a fixed endiannes to store the data, and make some wrapper functions for fread and fwrite.

So say you decide to store everything in little endian, the wrappers would check the machine endianness and:

  • If the machine is little endian, use fread and fwrite directly.
  • If the machine is big endian, swap bytes accordingly. Obviously, you would be assuming int-only aligned reads/writes (if you mix different length types, your wrapper will have no way to know which bytes to swap).

EDIT E.g., your fwrite could look like this (not tested though, just to illustrate the idea). Endianness check from C program to check little vs. big endian:

size_t lendian_fwrite(const void *ptr, size_t size, size_t nmemb,
                     FILE *stream)
{
    if (size != 4)
    {
        /* Warn and exit */
    }

    int x = 1;

    if ( *((char*)&x) == 1)
    {
        /* Little endian machine, use fwrite directly */
        return fwrite(ptr, size, nmemb, stream);
    }
    else
    {
        /* Big endian machine, pre-process first */

        unsigned char *buffer = (unsigned char*) ptr;

        for (int i=0; i<nmemb; i++)
        {           
            unsigned char a = buffer[4*i];
            unsigned char b = buffer[4*i + 1];

            buffer[4*i] = buffer[4*i + 3];
            buffer[4*i + 1] = buffer[4*i + 2];
            buffer[4*i + 2] = b;
            buffer[4*i + 3] = a;
        }

        return fwrite(ptr, size, nmemb, stream);
    }  
}
0
votes

I just made this simple endian swap fwrite. Works with any endianness, use it whenever you want the byte order swapped. Works for every element size and does not change the original data:

size_t endian_swap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
    unsigned char *buffer_src = (unsigned char*)ptr;
    unsigned char *buffer_dst = new unsigned char[size*nmemb];
    for (size_t i = 0; i < nmemb; i++)
    {
        for (size_t ix = 0; ix < size; ix++) {
            buffer_dst[size * i + (size - 1 - ix)] = buffer_src[size * i + ix];
        }
    }
    size_t result = fwrite(buffer_dst, size, nmemb, stream);
    delete buffer_dst;
    return result;
}
0
votes

Little improvised

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
static size_t lendian_fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream)
{
    int x = 1;

    if ( *((char*)&x) == 1)
    {
        /* Little endian machine, use fwrite directly */
        return fwrite(ptr, size, nmemb, stream);
    }
    else
    {
        if(size == sizeof(uint8_t))     //1 Byte
        {
            return fwrite(ptr, size, nmemb, stream);
        }
        else if(size == sizeof(uint16_t))   //2 Byte
        {
            /* Big endian machine, pre-process first */
            unsigned char *buffer = malloc(size*nmemb);
            unsigned char *input = (unsigned char*) ptr;

            for (uint32_t i=0; i<nmemb; i++)
            {           
                buffer[2*i] = input[2*i + 1];
                buffer[2*i + 1] = input[2*i];
            }
            int ret =fwrite((void*)buffer, size, nmemb, stream);      
            free(buffer);
            return ret;
        }
        else if(size == sizeof(uint32_t)) //4 Byte
        { 
            /* Big endian machine, pre-process first */
            unsigned char *buffer = malloc(size*nmemb);
            unsigned char *input = (unsigned char*) ptr;

            for (uint32_t i=0; i<nmemb; i++)
            {           
                buffer[4*i    ] = input[4*i + 3];
                buffer[4*i + 1] = input[4*i + 2];
                buffer[4*i + 2] = input[4*i + 1];
                buffer[4*i + 3] = input[4*i    ];
            }

            int ret =fwrite((void*)buffer, size, nmemb, stream);      
            free(buffer);
            return ret;
        }
        else if(size == sizeof(uint64_t)) //8 Byte
        { 
            /* Big endian machine, pre-process first */
            unsigned char *buffer = malloc(size*nmemb);
            unsigned char *input = (unsigned char*) ptr;

            for (uint32_t i=0; i<nmemb; i++)
            {           
                buffer[8*i    ] = input[4*i + 7];
                buffer[8*i + 1] = input[4*i + 6];
                buffer[8*i + 2] = input[4*i + 5];
                buffer[8*i + 3] = input[4*i + 4];
                buffer[8*i + 4] = input[4*i + 3];
                buffer[8*i + 5] = input[4*i + 2];
                buffer[8*i + 6] = input[4*i + 1];
                buffer[8*i + 7] = input[4*i    ];
            }

            int ret =fwrite((void*)buffer, size, nmemb, stream);      
            free(buffer);
            return ret;
        }
        else
        {
            printf("%s Function received invalid element size:%ld\n",__FUNCTION__,size);
            return -1;
        }
 
    }  
}


int main()
{
    
    uint8_t  buf1[8] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88 };
    uint16_t buf2[4] = { 0x1122,0x3344,0x5566,0x7788 };
    uint32_t buf3[2] = { 0x11223344, 0x55667788 };
    uint64_t buf4 = { 0x1122334455667788 };

    FILE *ofp = NULL;
    
    if((ofp=fopen("file.bin","wb"))==NULL)
    {
        printf("Cannot open output file!");
        return -1;
    }

    lendian_fwrite(&buf1, sizeof(uint8_t),sizeof(buf1),ofp);
    lendian_fwrite(&buf2, sizeof(uint16_t),sizeof(buf2)/sizeof(uint16_t),ofp);
    lendian_fwrite(&buf3, sizeof(uint32_t),sizeof(buf3)/sizeof(uint32_t),ofp);
    lendian_fwrite(&buf4, sizeof(uint64_t),sizeof(buf4)/sizeof(uint64_t),ofp);

    fclose(ofp);

}