Skip to content
Merged
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
36 changes: 26 additions & 10 deletions src/include/inflatelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ extern "C"
{
#endif

#define INFLATELIB_VERSION_STRING "0.2.0"
#define INFLATELIB_VERSION_STRING "0.3.0"
#define INFLATELIB_VERSION_MAJOR 0
#define INFLATELIB_VERSION_MINOR 2
#define INFLATELIB_VERSION_MINOR 3
#define INFLATELIB_VERSION_PATCH 0

typedef void* (*inflatelib_alloc)(void* userData, size_t bytes, size_t alignment);
Expand Down Expand Up @@ -140,17 +140,33 @@ extern "C"
* will remain unchanged. A positive return value indicates an "interesting" change in state that is not considered a
* failure, while a return value of zero indicates generic success.
*/
#define INFLATELIB_OK 0 /* No error occurred */
#define INFLATELIB_EOF 1 /* No error occurred; reached the end of the stream */
#define INFLATELIB_ERROR_ARG -1 /* Invalid argument */
#define INFLATELIB_ERROR_DATA -2 /* Error in the input data */
#define INFLATELIB_ERROR_OOM -3 /* Failed to allocate data */
#define INFLATELIB_OK 0 /* No error occurred */
#define INFLATELIB_EOF 1 /* No error occurred; reached the end of the stream */
#define INFLATELIB_ERROR_ARG -1 /* Invalid argument */
#define INFLATELIB_ERROR_DATA -2 /* Error in the input data */
#define INFLATELIB_ERROR_OOM -3 /* Failed to allocate data */
#define INFLATELIB_ERROR_VERSION -4 /* The major version of this header and the compiled library differ */

/*
* Initializes the stream. The 'user_data', 'alloc', and 'free' members MUST be set prior to the init call and MUST
* NOT be changed after the init call completes. This function returns one of the status values specified above.
* Returns the version of the compiled library as a string in the form 'X.Y.Z'. The implementation just returns
* 'INFLATELIB_VERSION_STRING', however this may differ when linking, statically or dynamically, against a pre-built
* library. The major version of 'INFLATELIB_VERSION_STRING' must match the major version returned by this function,
* otherwise the two are incompatible (struct size, arrangement, etc. may differ). The 'inflatelib_init' macro does
* this for you by passing 'INFLATELIB_VERSION_STRING' to 'inflatelib_init_'.
*/
INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_init(inflatelib_stream* stream);
INFLATELIB_EXPORT const char* INFLATELIB_CALLCONV inflatelib_version(void);

/*
* Initializes the stream. The 'user_data', 'alloc', and 'free' members MUST be set prior to the init call and MUST NOT
* be changed after the init call completes. This function returns one of the status values specified above.
*/
#define inflatelib_init(stream) inflatelib_init_(stream, INFLATELIB_VERSION_STRING)

/*
* Backing implementation of 'inflatelib_init' that verififes the major version of this header matches the major
* version the library was compiled with.
*/
INFLATELIB_EXPORT int INFLATELIB_CALLCONV inflatelib_init_(inflatelib_stream* stream, const char* version);

/*
* Resets the stream's state back to its initialized state. This allows the stream to be reused for multiple inflate
Expand Down
2 changes: 1 addition & 1 deletion src/lib/huffman_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include <stdint.h>

// NOTE: We can't include 'internal.h' since it includes us, so forward declare what we need
/* NOTE: We can't include 'internal.h' since it includes us, so forward declare what we need */
struct inflatelib_stream;

/* Code length codes are encoded using 3 bits (length of 0-7 bits), and the code length alphabet, which defines how the
Expand Down
36 changes: 29 additions & 7 deletions src/lib/inflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

#include "internal.h"

const char* INFLATELIB_CALLCONV inflatelib_version(void)
{
return INFLATELIB_VERSION_STRING;
}

static void* inflatelib_default_alloc(void* unusedUserData, size_t bytes, size_t alignment)
{
void* result;
Expand All @@ -35,10 +40,27 @@ static void inflatelib_default_free(void* unusedUserData, void* ptr, size_t byte
free(ptr);
}

int inflatelib_init(inflatelib_stream* stream)
int INFLATELIB_CALLCONV inflatelib_init_(inflatelib_stream* stream, const char* version)
{
int result;
inflatelib_state* state;
const char* localVer = INFLATELIB_VERSION_STRING;

/* If the major versions don't match, make no assumptions about the layout of 'inflatelib_stream' as it may not be
safe to write to. This failure is communicated ONLY through the return value. */
for (size_t i = 0;; ++i)
{
if (version[i] != localVer[i])
Comment thread
dunhor marked this conversation as resolved.
{
errno = EINVAL;
return INFLATELIB_ERROR_VERSION;
}

if (version[i] == '.')
{
break;
}
}

/* Start with no error message, in case it was set before (or contains uninitialized memory) */
stream->error_msg = NULL;
Expand Down Expand Up @@ -93,7 +115,7 @@ int inflatelib_init(inflatelib_stream* stream)
return INFLATELIB_OK;
}

int inflatelib_reset(inflatelib_stream* stream)
int INFLATELIB_CALLCONV inflatelib_reset(inflatelib_stream* stream)
{
inflatelib_state* state = stream->internal;

Expand All @@ -107,15 +129,15 @@ int inflatelib_reset(inflatelib_stream* stream)
bitstream_reset(&state->bitstream);
window_reset(&state->window);

// NOTE: The Huffman trees do not need to be reset as they are reset on demand as needed. If we've made it this far,
// all of their internal state has been allocated, and that's the best that we can ask for
/* NOTE: The Huffman trees do not need to be reset as they are reset on demand as needed. If we've made it this far,
all of their internal state has been allocated, and that's the best that we can ask for */

state->ifstate = ifstate_init;

return INFLATELIB_OK;
}

int inflatelib_destroy(inflatelib_stream* stream)
int INFLATELIB_CALLCONV inflatelib_destroy(inflatelib_stream* stream)
{
inflatelib_state* state = stream->internal;

Expand Down Expand Up @@ -238,7 +260,7 @@ static int do_inflate(inflatelib_stream* stream)
return result;
}

int inflatelib_inflate(inflatelib_stream* stream)
int INFLATELIB_CALLCONV inflatelib_inflate(inflatelib_stream* stream)
{
inflatelib_state* state = stream->internal;

Expand Down Expand Up @@ -273,7 +295,7 @@ int inflatelib_inflate(inflatelib_stream* stream)
return do_inflate(stream);
}

int inflatelib_inflate64(inflatelib_stream* stream)
int INFLATELIB_CALLCONV inflatelib_inflate64(inflatelib_stream* stream)
{
inflatelib_state* state = stream->internal;

Expand Down
2 changes: 1 addition & 1 deletion src/lib/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ extern "C"
}
#endif

#endif // INFLATELIB_WINDOW_H
#endif
32 changes: 32 additions & 0 deletions test/cpp/InflateTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,32 @@ static void inflate64_error_test(const char* inputFileName, const char* errFragm
do_inflate_test<&inflatelib::stream::try_inflate64>(input, {}, errFragment);
}

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define MAJOR_VERSION_STRING STRINGIFY(INFLATELIB_VERSION_MAJOR)
#define MAKE_VERSION_STRING(x, y, z) STRINGIFY(x) "." STRINGIFY(y) "." STRINGIFY(z)

TEST_CASE("InflateErrors", "[inflate]")
{
inflate_error_test("error.invalid-block-type.in.bin", "Unexpected block type '3'");

// Error if we call 'inflatelib_inflate' before calling 'inflate64_init'
inflatelib_stream stream = {};
REQUIRE(inflatelib_inflate(&stream) == INFLATELIB_ERROR_ARG);

// Error if we have a mis-matched major version
auto checkMajorVersionMismatch = [&](const char* version) {
REQUIRE(inflatelib_init_(&stream, version) == INFLATELIB_ERROR_VERSION);
};

checkMajorVersionMismatch("." INFLATELIB_VERSION_STRING); // Leading dot
checkMajorVersionMismatch(" " INFLATELIB_VERSION_STRING); // Leading space
checkMajorVersionMismatch("0" INFLATELIB_VERSION_STRING); // Leading zero
checkMajorVersionMismatch(MAJOR_VERSION_STRING "0.0.0"); // Zero after the correct major version
checkMajorVersionMismatch(MAJOR_VERSION_STRING); // Only major version; not a supported form
// Different major version - library is older
checkMajorVersionMismatch(MAKE_VERSION_STRING("9", INFLATELIB_VERSION_MINOR, INFLATELIB_VERSION_PATCH));
// TODO: Once we reach a 1.0 release, we can test when the library is newer
}

TEST_CASE("Inflate64Errors", "[inflate64]")
Expand All @@ -290,6 +309,19 @@ TEST_CASE("Inflate64Errors", "[inflate64]")
REQUIRE(inflatelib_inflate64(&stream) == INFLATELIB_ERROR_ARG);
}

TEST_CASE("InflateInitDifferentMinorVersion", "[inflate]")
{
auto checkInit = [](const char* version) {
inflatelib_stream stream = {};
REQUIRE(inflatelib_init_(&stream, version) == INFLATELIB_OK);
inflatelib_destroy(&stream);
};

checkInit(INFLATELIB_VERSION_STRING); // Same version, obvious success
checkInit(MAKE_VERSION_STRING(INFLATELIB_VERSION_MAJOR, 0, 0)); // Library is (likely) newer
checkInit(MAKE_VERSION_STRING(INFLATELIB_VERSION_MAJOR, 999, 999)); // Library is (likely) older
}

TEST_CASE("InflateUncompressed", "[inflate]")
{
inflate_test("uncompressed.empty.in.bin", "uncompressed.empty.out.bin");
Expand Down
Loading