Read binary file chunk by chunk in C

Im new to C, and used to python. But because of current situation in the world my primary computer was destroyed and all I left with is 12 years old laptop. Python would not work really good there.

My goal is to create encryption algorithm. Currently its fairly simple, but I will upgrade it later

How can I read file in chunks and overwrite its data?
here’s my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char byte;

void printa(byte a[]) {
    printf("[");
    for (int i = 0; i < strlen(a); i++) {
        printf("%d", a[i]);
        if (i != strlen(a) - 1) {
            printf(", ");
        };
    };
    printf("]\n");
};

int encrypt(byte data[], byte key[], int keyi) {
    for (int i = 0; i < strlen(data); i++) {
        data[i] += key[keyi];
        keyi = (keyi + 1) * (keyi < strlen(key) - 1); //branchless if (keyi<strlen(key)) {keyi++;} else {keyi=0;};
    };
    return keyi;
};

void encrypt_file(byte filename[], byte key[], int buffer_size) {
    FILE *file;
    byte *buff = calloc(sizeof(byte), buffer_size);
    file = fopen(filename, "rb");
    if (file == NULL) {
        printf("UNABLE TO OPEN FILE\n");
    };
};

int main() {
    encrypt_file("test.txt", "key", 1024);
    return 0;
};

My files will be around 2GB in size, but my laptop ram is 512MB, so I have to use chunking.

>Solution :

To encrypt the file on a block basis, you should use fread and fwrite.

To reset the key index keyi at the end of key, comparing it to strlen(key) - 1 is potentially very inefficient because strlen() iterates on the whole string to locate the null terminator. Just test if key[keyi] is a null byte.

Also don’t worry about branchless code:

  • any good compiler will generate branchless code for a simple test if (key[keyi] == '\0') keyi = 0;
  • branches are not a real issue on modern processors, as long as they can be consistently predicted.

Here is a modified version:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char byte;

int encrypt(byte data[], size_t size, const byte key[], int keyi) {
    for (size_t i = 0; i < size; i++) {
        data[i] ^= key[keyi++];  // involutory encryption (apply twice to decrypt)
        if (key[keyi] == '\0')
            keyi = 0;
    }
    return keyi;
}

int encrypt_file(const char *filename, const char *filename2,
                 const byte key[], size_t buffer_size)
{
    byte *buff = calloc(sizeof(*buff), buffer_size);
    if (buff == NULL) {
        fprintf(stderr, "Cannot allocate memory\n");
        return 1;
    }
    FILE *f1 = fopen(filename, "rb");
    if (f1 == NULL) {
        fprintf(stderr, "Cannot open file %s: %s\n", filename, strerror(errno));
        free(buff);
        return 1;
    }
    FILE *f2 = fopen(filename2, "wb");
    if (f2 == NULL) {
        fprintf(stderr, "Cannot open file %s: %s\n", filename2, strerror(errno));
        fclose(f1);
        free(buff);
        return 1;
    }
    size_t nread;
    int keyi = 0;
    while ((nread = fread(buff, sizeof(*buff), size, f1)) != 0) {
        keyi = encrypt(buff, nread, key, keyi);
        if (fwrite(buff, sizeof(*buff), nread, f2) != nread) {
            fprintf(stderr, "Error writing to file %s: %s\n", filename2, strerror(errno));
            break;
        }
    }
    fclose(f1);
    fclose(f2);
    free(buff);
    return 0;
}

int main() {
    return encrypt_file("test.txt", "test.out", (const byte *)"key", 1024);
}

Leave a Reply