Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make retrieval of file size more portable #234

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sxbp/sxbp.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,9 @@ sxbp_result_t sxbp_copy_buffer(
* @returns `SXBP_RESULT_FAIL_MEMORY` or `SXBP_RESULT_FAIL_IO` on failure to copy the file contents
* @returns `SXBP_RESULT_FAIL_PRECONDITION` if `file_handle` or `buffer` is
* `NULL`
* @returns `SXBP_RESULT_FAIL_IO` if the file's size could not be determined
* @returns `SXBP_RESULT_FAIL_UNIMPLEMENTED` if the file size appears to be
* greater than 2GiB on Microsoft Windows only
* @since v0.54.0
*/
sxbp_result_t sxbp_buffer_from_file(
Expand Down
8 changes: 8 additions & 0 deletions sxbp/sxbp_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ typedef struct sxbp_bounds_t {
*/
extern const sxbp_vector_t SXBP_VECTOR_DIRECTIONS[4];

/*
* private, portably retrieves the size in bytes of the file at the given handle
* and writes this out to file_size
* returns SXBP_RESULT_FAIL_IO if an error occurred
* returns SXBP_RESULT_OK if successful
*/
sxbp_result_t sxbp_get_file_size(FILE* file_handle, size_t* file_size);

/*
* private, updates the current figure bounds given the location of the end of
* the most recently-plotted line
Expand Down
140 changes: 140 additions & 0 deletions sxbp/sxbp_internal_get_file_size.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* This source file forms part of sxbp, a library which generates experimental
* 2D spiral-like shapes based on input binary data.
*/

/**
* @internal
* @file
*
* @brief This source file provides the implementation for a function which
* tries to portably retrieve the size of a file, using OS-specific methods for
* POSIX and Windows, falling back to the standard library (which ironically is
* not portable due to the standard not requiring the host to meaningfully
* support SEEK_END in the ftell() function) in all other cases.
*
* @author Joshua Saxby <joshua.a.saxby@gmail.com>
* @date 2018
*
* @copyright Copyright (C) Joshua Saxby 2016-2017, 2018
*
* @copyright
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>

#include "sxbp.h"
#include "sxbp_internal.h"


#ifdef __cplusplus
#error "This file is ISO C99. It should not be compiled with a C++ Compiler."
#endif

// TODO: Check these macro constants for correctness on each OS

// POSIX version
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
// we're gonna need fstat() for this!
#include <sys/stat.h>


sxbp_result_t sxbp_get_file_size(FILE* file_handle, size_t* file_size) {
// preconditional assertions
assert(file_handle != NULL);
assert(file_size != NULL);
// try and get a file descriptor for the file handle
int file_descriptor = fileno(file_handle);
if (file_descriptor == -1) {
// getting a file descriptor failed
return SXBP_RESULT_FAIL_IO;
}
// results from the fstat call are stored here
struct stat file_info = { 0 };
if (fstat(file_descriptor, &file_info) == 0) {
// file size is file_info.st_size
*file_size = (size_t)file_info.st_size;
// return success
return SXBP_RESULT_OK;
} else {
// return error code
return SXBP_RESULT_FAIL_IO;
}
}

// Windows version
#elif defined(_WIN32)
// We're gonna need GetFileSizeEx() for this!
#include <Windows.h>


sxbp_result_t sxbp_get_file_size(FILE* file_handle, size_t* file_size) {
// preconditional assertions
assert(file_handle != NULL);
assert(file_size != NULL);
// try and convert the file handle to a Windows file descriptor
int file_descriptor = _fileno(file_handle);
if (file_descriptor < 0) {
// Windows API returns a negative code if it couldn't do the operation
return SXBP_RESULT_FAIL_IO;
}
// try and convert the Windows file descriptor to a Windows HANDLE
intptr_t windows_file_handle = _get_osfhandle(file_descriptor);
if (windows_file_handle == INVALID_HANDLE_VALUE) {
// Windows couldn't do it, return an error
return SXBP_RESULT_FAIL_IO;
}
// the file's size from the Windows API call will be stored in this struct
LARGE_INTEGER file_size_info;
// call the Windows API to check the file size
if (!GetFileSizeEx((HANDLE)windows_file_handle, &file_size_info)) {
// if it failed, return an error code
return SXBP_RESULT_FAIL_IO;
}
/*
* LARGE_INTEGER is actually a struct of two DWORDS (32-bits) with an extra
* member which is 64 bits on 64-bit systems.
* As it happens, we don't care about files that are larger than 1GiB so if
* the higher DWORD is set, we can return an error code instead as we don't
* need it (can't use it for input to the algorithm)
*/
if (file_size_info.HighPart != 0) {
return SXBP_RESULT_FAIL_UNIMPLEMENTED;
} else {
*file_size = (size_t)file_size_info.LowPart;
return SXBP_RESULT_OK;
}
}

// Generic version
#else
sxbp_result_t sxbp_get_file_size(FILE* file_handle, size_t* file_size) {
// preconditional assertions
assert(file_handle != NULL);
assert(file_size != NULL);
/*
* seek to end
* NOTE: This isn't portable due to lack of meaningful support of `SEEK_END`
*/
if (fseek(file_handle, 0, SEEK_END) != 0) {
// handle fseek() error
return SXBP_RESULT_FAIL_IO;
}
// get size and check it, before storing it or throwing an error
int check_file_size = ftell(file_handle);
if (check_file_size < 0) {
// handle ftell() error
return SXBP_RESULT_FAIL_IO;
}
*file_size = (size_t)check_file_size;
// seek to start again
fseek(file_handle, 0, SEEK_SET);
// return the calculated file size
return SXBP_RESULT_OK;
}
#endif
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this generic version. It does things in a fundamentally different way to the others. I think it should be removed instead.

26 changes: 9 additions & 17 deletions sxbp/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,6 @@ sxbp_result_t sxbp_copy_buffer(
}
}

/*
* private, works out and returns the size of the file referred to by the given
* file handle
*/
static size_t sxbp_get_file_size(FILE* file_handle) {
// seek to end
// NOTE: This isn't portable due to lack of meaningful support of `SEEK_END`
fseek(file_handle, 0, SEEK_END);
// get size
size_t file_size = (size_t)ftell(file_handle);
// seek to start again
fseek(file_handle, 0, SEEK_SET);
return file_size;
}

sxbp_result_t sxbp_buffer_from_file(
FILE* file_handle,
sxbp_buffer_t* const buffer
Expand All @@ -140,8 +125,15 @@ sxbp_result_t sxbp_buffer_from_file(
SXBP_RETURN_FAIL_IF_NULL(buffer);
// erase buffer
sxbp_free_buffer(buffer);
// get the file's size
buffer->size = sxbp_get_file_size(file_handle);
/*
* get the file's size
* we'll store any errors encountered by this operation here
*/
sxbp_result_t status = SXBP_RESULT_UNKNOWN;
if (!sxbp_check(sxbp_get_file_size(file_handle, &buffer->size), &status)) {
// handle error
return status;
}
// allocate the buffer to this size and handle error if this failed
if (!sxbp_success(sxbp_init_buffer(buffer))) {
// allocation failed - this can only be a memory error
Expand Down