SwimMaster
SwimMaster

Reputation: 381

Integer stored as incorrect value in when using mmap

Im writing a program that will write an array of structs to a file using mmap. The problem is the third integer value (left) is not being stored properly. When looking at the file through od, the bytes in left seems to be shifted one byte to the left. For example...

|loc            |value  |left          |right          |extra bytes?
001 000 000 000 103 120 000 000 000 003 000 000 000 004 000 000 //expected
001 000 000 000 103 120 000 000 003 000 000 000 004 000 000 000 //result
typedef struct{
    int32_t loc;
    char value[2];
    int32_t left;
    int32_t right;

}Node;

Node newNode(int i);

int main(int argc, char *argv[])
{
    int i;
    int fd;
    int result;
    Node *map;  /* mmapped array of int's */

    int filesize = strtol(argv[2], NULL, 10) * sizeof(Node);
    int numvalues = filesize / sizeof(Node);

    fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
    if (fd == -1) {
        perror("File failed to open");
        exit(1);
    }

    //I dont know why this makes it work but we need to move the file pointer around for some reason.
    result = lseek(fd, filesize-1, SEEK_SET);
    if (result == -1) {
        close(fd);
        perror("Error calling lseek()");
        exit(2);
    }

    // same with this
    result = write(fd, "", 1);

    /* Now the file is ready to be mmapped.
    */
    map = (Node *) mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
        close(fd);
        perror("Error mmapping the file");
        exit(4);
    }


    for (i = 0; i <numvalues; ++i) {
        map[i] = newNode(i);         /* here is where I save the data */
    }

    munmap(map, filesize);
    close(fd);
    return 0;
}

Node newNode(int i) { /*This method is where the structs are made*/
    Node n;
    n.left = i * 2 + 1;
    n.right = i * 2 + 2;
    n.value[0] = (char)(rand() % ('A' - 'Z') )+ 'A';
    n.value[1] = (char)(rand() % ('A' - 'Z') )+ 'A';
    n.loc = i;

    printf("%d, %d, %c, %c, %d\n", n.left, n.right, n.value[0], n.value[1], n.loc);

    return n;
}

also, why are some of the integers saved as little endian and others big endian.

Upvotes: 2

Views: 143

Answers (1)

kaylum
kaylum

Reputation: 14044

You are hitting two issues: endianess and struct padding.

Endianess

It appears your system is little endian. That means the least significant byte is stored first. We can see this in the fact that 1 is stored as 01 00 00 00. In a big endian system it would be 00 00 00 01. That means your "expected" result is not correct. It should be as follows. Note the left and right bytes have been swapped.

|loc            |value  |left          |right          |
001 000 000 000 103 120 003 000 000 000 004 000 000 000    

Struct Padding

So why don't you get the above expected result? Because the compiler adds padding in the struct for word alignment. So there are two pad bytes after value field. Print out sizeof(Node) to see that. Hence everything is actually shifted right by two bytes. So the actual expected result is:

|loc            |value  |pad     |left           |right          |
001 000 000 000 103 120  000 000 003 000 000 000 004 000 000 000    

Which is exactly the actual result you have shown.

Upvotes: 2

Related Questions