4

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).

Ray C
  • 547
  • 2
  • 7
  • 24
  • Various approaches at http://stackoverflow.com/questions/7897771/platform-independent-storage-of-signed-integers, such as using ntohl/htonl. – Martin R Oct 22 '16 at 14:41
  • As I understood it, ntohl is not that portable. – Ray C Oct 22 '16 at 14:46
  • Yes, but it *is* part of the POSIX specification: http://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 Oct 22 '16 at 14:50
  • 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...? – Ray C Oct 22 '16 at 15:08
  • How do you transfer the data between the machines? For networking it should handled "automatically", see for example http://stackoverflow.com/questions/25055714/bit-ordering-in-a-byte-does-it-matter. – Martin R Oct 22 '16 at 15:12
  • "Do the bits within the byte not have endianness?" does not apply to reading/writing to files. C has no function to access data in bit units so the bit-order is not relevant. – chux - Reinstate Monica Oct 22 '16 at 19:04
  • Data is transferred between files. Essentially assume it's an executable the user can run and write to a file and read files already packaged with the .exe I want the user to be able to read files like that. So I'm doing files not networking per se – Ray C Oct 22 '16 at 20:01

3 Answers3

2

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);
    }  
}
Community
  • 1
  • 1
Luis de Arquer
  • 377
  • 2
  • 8
  • That seems to be the best solution. Question though, if I have a char * stream with 10 bytes that I fread from a file such as "1234567890" and I fwrite 1 byte at a time, endianness does not matter correct? It will always write it the same way it's read in, 1234567890 – Ray C Oct 22 '16 at 20:06
  • 1
    I don't think you need to worry about how many bytes you write at a time. Assuming your file is a regular disk file (files can be all sort of things on *nix systems, like serial ports with funny line disciplines etc), and your file system does nothing peculiar, all you need to think about is how the data is ordered in your memory buffer, and that's how it will be written to the file (and the other way around for reading). What you cast it to, or how many bytes you write at a time is not important. A char array is not affected by endianness (and you can write the 10 chars at a time) – Luis de Arquer Oct 23 '16 at 08:51
0

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;
}
thewhiteambit
  • 1,365
  • 16
  • 31
0

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);

}