Hi everyone. This my first post here. I'm a bit of novice C programmer and am very insecure about my programming skills. I needed to write a small wrapper for handling binary file I/O for use by a couple of programs that I use on 32-bit and 64-bit Windows and Linux systems. I'm feeling a bit uneasy about my design skills and general coding practices and was wondering if any folks here could give it a once over.
Thank you for your time.
Header file:
Code:
/*
* File: binio.h
* Namespace: binio_ / BINIO_
*
* Binary file I/O library
*
* Note that 'binio' assumes that the user will never read or write values
* outside of the minimum sizes guaranteed by the C Standard. For example, a
* char is expected to be an 8-bit value and a long is expected to be a 32-bit
* value.
*/
#ifndef BINIO_H
#define BINIO_H
#include <stdio.h>
/*
* Binary file handle returned by 'binio_from*' and freed by 'binio_close'.
*/
struct binio_file {
FILE *fp; /* Private */
const char *name; /* Read-only */
int eof; /* Read-only */
int error; /* Read-only */
};
/*
* Opens the file 'file' for use. The string 'mode' is passed to 'fopen' and
* should always include the 'b' option.
*
* Returns: If the file was opened successfully, a new file handle is returned.
* Otherwise, NULL returned and an error message is printed to 'stderr'.
*
* Asserts:
* file != NULL
* mode != NULL
* Returned 'binio_file' is valid
*/
struct binio_file *binio_from_file(const char *file, const char *mode);
/*
* Closes the binary file 'bf'. Note that the file handle is freed by this
* function. If an error occurred while closing the file, an error message is
* printed to 'stderr'.
*
* Asserts:
* bf != NULL
*/
void binio_close(struct binio_file *bf);
/*
* Reads the entire file 'file' into a newly allocated buffer.
*
* Returns: If the buffer was successfully created and read into, it is
* returned and 'size' is set to the size of the buffer. Otherwise, NULL is
* returned and an error message is printed to 'stderr'.
*
* Asserts:
* file != NULL
* size != NULL
*/
unsigned char *binio_read_file(const char *file, size_t *size);
/*
* Reads 'size' bytes from the binary file 'bf' from its current position
* forward into the buffer 'data'. If the end of the file was reached before
* reading is complete, the file handle's 'eof' member will be set to a
* non-zero value. If an error occurred while reading, the file handle's
* 'error' member will be set to a non-zero value and an error message will be
* printed to 'stderr'.
*
* Asserts:
* bf != NULL
* data != NULL
* size > 0
*/
void binio_read(struct binio_file *bf, unsigned char *data, size_t size);
/*
* The following input functions read binary values from the binary file 'bf'.
* Functions with names that end with 'le' read data in little-endian format.
* Functions with names that end with 'be' read data in big-endian format. If
* the end of the input file is reached, the file handle's 'eof' member will be
* set to a non-zero value. If an error occurs while reading, the file handle's
* 'error' member will be set to a non-zero value and an error message will be
* printed to 'stderr'.
*
* Returns: If successful, the value read from file is returned, otherwise
* zero is returned.
*
* Asserts:
* bf != NULL
*/
unsigned char binio_read_u8(struct binio_file *bf);
signed char binio_read_s8(struct binio_file *bf);
unsigned short binio_read_u16le(struct binio_file *bf);
short binio_read_s16le(struct binio_file *bf);
unsigned short binio_read_u16be(struct binio_file *bf);
short binio_read_s16be(struct binio_file *bf);
unsigned long binio_read_u32le(struct binio_file *bf);
long binio_read_s32le(struct binio_file *bf);
unsigned long binio_read_u32be(struct binio_file *bf);
long binio_read_s32be(struct binio_file *bf);
/*
* Writes the binary data in 'data' with the size 'size' to the file 'file'.
*
* Returns: If the binary data was successfully written, zero is returned.
* Otherwise, a non-zero value is returned and an error message is printed to
* 'stderr'.
*
* Asserts:
* file != NULL
* data != NULL
* size > 0
*/
int binio_write_file(const char *file, const unsigned char *data, size_t size);
/*
* Writes 'size' bytes to the binary file 'bf' from its current position
* forward from the buffer 'data'. If an error occurs while writing, the file
* handle's 'error' member will be set to a non-zero value and an error message
* will be printed to 'stderr'.
*
* Asserts:
* bf != NULL
* data != NULL
* size > 0
*/
void binio_write(struct binio_file *bf, const unsigned char *data,
size_t size);
/*
* The following output functions write binary values to the binary file 'bf'.
* Functions with names that end with 'le' write data in little-endian format.
* Functions with names that end with 'be' write data in big-endian format. If
* an error occurs while writing, the file handle's 'error' member will be set
* to a non-zero value and an error message will be printed to 'stderr'.
*
* Asserts:
* bf != NULL
*/
void binio_write_u8(struct binio_file *bf, unsigned char u8);
void binio_write_s8(struct binio_file *bf, signed char s8);
void binio_write_u16le(struct binio_file *bf, unsigned short u16);
void binio_write_s16le(struct binio_file *bf, short s16);
void binio_write_u16be(struct binio_file *bf, unsigned short u16);
void binio_write_s16be(struct binio_file *bf, short s16);
void binio_write_u32le(struct binio_file *bf, unsigned long u32);
void binio_write_s32le(struct binio_file *bf, long s32);
void binio_write_u32be(struct binio_file *bf, unsigned long u32);
void binio_write_s32be(struct binio_file *bf, long s32);
/*
* Resets the current position of the binary file 'bf' to its beginning. If an
* error occurs while rewinding, the file handle's 'error' member will be set
* to a non-zero value and an error message will be printed to 'stderr'.
*
* Asserts:
* bf != NULL
*/
void binio_rewind(struct binio_file *bf);
/*
* Gets the number of bytes left to be read in the binary file 'bf', then
* resets the binary file's position back to the beginning of the file.
*
* Returns: If successful, the number of bytes left in the file is returned.
* Otherwise, if an error occurs or there where no bytes left to read, zero is
* returned, the file handle's 'error' member is set to a non-zero value, and
* an error message will be printed to 'stderr'.
*
* Asserts:
* bf != NULL
*/
size_t binio_get_size(struct binio_file *bf);
#endif /* BINIO_H */
Source:
Code:
/*
* File: binio.c
*
* Binary file I/O library implementation
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "binio.h"
#if UCHAR_MAX > 0xFFU
# error 'binio' requires 8-bit chars
#endif
typedef unsigned char uint8;
typedef signed char int8;
#if USHRT_MAX > 0xFFFFU
# error 'binio' requires 16-bit shorts
#endif
typedef unsigned short uint16;
typedef short int16;
#if ULONG_MAX == 0xFFFFFFFFUL
typedef unsigned long uint32;
typedef long int32;
#elif UINT_MAX == 0xFFFFFFFFUL
typedef unsigned int uint32;
typedef int int32;
#else
# error 'binio' requires either 32-bit longs or 32-bit ints
#endif
struct binio_file *binio_from_file(const char *file, const char *mode)
{
struct binio_file *bf;
assert(file != NULL);
assert(mode != NULL);
if ((bf = malloc(sizeof *bf)) == NULL) {
fprintf(stderr, "Unable to allocate new file handle\n");
return NULL;
}
if ((bf->fp = fopen(file, mode)) == NULL) {
fprintf(stderr, "Error opening \"%s\", %s\n", file,
strerror(errno));
return NULL;
}
bf->name = file;
bf->eof = bf->error = 0;
assert(bf->fp != NULL);
assert(bf->name == file);
assert(bf->eof == 0);
assert(bf->error == 0);
return bf;
}
void binio_close(struct binio_file *bf)
{
assert(bf != NULL);
if (fclose(bf->fp) == EOF) {
fprintf(stderr, "Error closing \"%s\", %s\n", bf->name,
strerror(errno));
}
free(bf);
}
unsigned char *binio_read_file(const char *file, size_t *size)
{
struct binio_file *bf;
unsigned char *data;
int success;
assert(file != NULL);
assert(size != NULL);
if ((bf = binio_from_file(file, "rb")) == NULL) {
return NULL;
}
if ((*size = binio_get_size(bf)) == 0) {
binio_close(bf);
return NULL;
}
if ((data = malloc(sizeof *data * *size)) == NULL) {
fprintf(stderr, "Unable to allocate new binary buffer\n");
binio_close(bf);
return NULL;
}
binio_read(bf, data, *size);
success = !(bf->error || bf->eof);
if (bf->eof) {
fprintf(stderr, "Error reading \"%s\", Unexpected EOF\n",
bf->name);
}
binio_close(bf);
if (!success) {
free(data);
return NULL;
}
return data;
}
void binio_read(struct binio_file *bf, unsigned char *data, size_t size)
{
size_t i;
assert(bf != NULL);
assert(data != NULL);
assert(size > 0);
for (i = 0; i < size; i++) {
data[i] = binio_read_u8(bf);
}
}
unsigned char binio_read_u8(struct binio_file *bf)
{
uint8 u8;
assert(bf != NULL);
if (bf->eof || bf->error) {
return 0;
}
if (fread(&u8, 1, 1, bf->fp) == 1) {
return u8;
}
if (feof(bf->fp)) {
bf->eof = 1;
} else if (ferror(bf->fp)) {
bf->error = 1;
fprintf(stderr, "Error reading \"%s\", %s\n", bf->name,
strerror(errno));
}
return 0;
}
signed char binio_read_s8(struct binio_file *bf)
{
return (int8)binio_read_u8(bf);
}
unsigned short binio_read_u16le(struct binio_file *bf)
{
uint16 u16;
u16 = binio_read_u8(bf);
u16 |= (uint16)binio_read_u8(bf) << 8;
return u16;
}
short binio_read_s16le(struct binio_file *bf)
{
return (int16)binio_read_u16le(bf);
}
unsigned short binio_read_u16be(struct binio_file *bf)
{
uint16 u16;
u16 = (uint16)binio_read_u8(bf) << 8;
u16 |= binio_read_u8(bf);
return u16;
}
short binio_read_s16be(struct binio_file *bf)
{
return (int16)binio_read_u16be(bf);
}
unsigned long binio_read_u32le(struct binio_file *bf)
{
uint32 u32;
u32 = binio_read_u8(bf);
u32 |= (uint32)binio_read_u8(bf) << 8;
u32 |= (uint32)binio_read_u8(bf) << 16;
u32 |= (uint32)binio_read_u8(bf) << 24;
return u32;
}
long binio_read_s32le(struct binio_file *bf)
{
return (int32)binio_read_u32le(bf);
}
unsigned long binio_read_u32be(struct binio_file *bf)
{
uint32 u32;
u32 = (uint32)binio_read_u8(bf) << 24;
u32 |= (uint32)binio_read_u8(bf) << 16;
u32 |= (uint32)binio_read_u8(bf) << 8;
u32 |= binio_read_u8(bf);
return u32;
}
long binio_read_s32be(struct binio_file *bf)
{
return (int32)binio_read_u32be(bf);
}
int binio_write_file(const char *file, const unsigned char *data, size_t size)
{
struct binio_file *bf;
int success;
if ((bf = binio_from_file(file, "wb")) == NULL) {
return 0;
}
binio_write(bf, data, size);
success = !bf->error;
binio_close(bf);
return success;
}
void binio_write(struct binio_file *bf, const unsigned char *data, size_t size)
{
size_t i;
assert(bf != NULL);
assert(data != NULL);
assert(size > 0);
for (i = 0; i < size; i++) {
binio_write_u8(bf, data[i]);
}
}
void binio_write_u8(struct binio_file *bf, unsigned char u8)
{
assert(bf != NULL);
if (bf->error) {
return;
}
if (fwrite(&u8, 1, 1, bf->fp) == 1) {
return;
}
bf->error = 1;
if (ferror(bf->fp)) {
fprintf(stderr, "Error writing \"%s\", %s\n", bf->name,
strerror(errno));
}
}
void binio_write_s8(struct binio_file *bf, signed char s8)
{
binio_write_u8(bf, (uint8)s8);
}
void binio_write_u16le(struct binio_file *bf, unsigned short u16)
{
binio_write_u8(bf, (uint16)(u16 & 0xFF));
binio_write_u8(bf, (uint16)(u16 >> 8 & 0xFF));
}
void binio_write_s16le(struct binio_file *bf, short s16)
{
binio_write_u16le(bf, (uint16)s16);
}
void binio_write_u16be(struct binio_file *bf, unsigned short u16)
{
binio_write_u8(bf, (uint16)(u16 >> 8 & 0xFF));
binio_write_u8(bf, (uint16)(u16 & 0xFF));
}
void binio_write_s16be(struct binio_file *bf, short s16)
{
binio_write_u16be(bf, (uint16)s16);
}
void binio_write_u32le(struct binio_file *bf, unsigned long u32)
{
binio_write_u8(bf, (uint32)(u32 & 0xFF));
binio_write_u8(bf, (uint32)(u32 >> 8 & 0xFF));
binio_write_u8(bf, (uint32)(u32 >> 16 & 0xFF));
binio_write_u8(bf, (uint32)(u32 >> 24 & 0xFF));
}
void binio_write_s32le(struct binio_file *bf, long s32)
{
binio_write_u32le(bf, (uint32)s32);
}
void binio_write_u32be(struct binio_file *bf, unsigned long u32)
{
binio_write_u8(bf, (uint32)(u32 >> 24 & 0xFF));
binio_write_u8(bf, (uint32)(u32 >> 16 & 0xFF));
binio_write_u8(bf, (uint32)(u32 >> 8 & 0xFF));
binio_write_u8(bf, (uint32)(u32 & 0xFF));
}
void binio_write_s32be(struct binio_file *bf, long s32)
{
binio_write_u32be(bf, (uint32)s32);
}
void binio_rewind(struct binio_file *bf)
{
assert(bf != NULL);
if (fseek(bf->fp, 0L, SEEK_SET) == -1) {
bf->error = 1;
fprintf(stderr, "Error rewinding \"%s\", %s\n", bf->name,
strerror(errno));
} else {
bf->eof = 0;
}
}
size_t binio_get_size(struct binio_file *bf)
{
size_t size = 0;
assert(bf != NULL);
binio_read_u8(bf);
while (!bf->eof) {
binio_read_u8(bf);
size++;
}
binio_rewind(bf);
if (bf->error) {
return 0;
}
return size;
}