From 034944cac1f163843c5c0e03c8025e53f266ebbb Mon Sep 17 00:00:00 2001 From: David Thomas Date: Thu, 6 Oct 2022 00:16:07 +0100 Subject: [PATCH] Stream: Add get/fill functions --- docs/stream.md | 42 ++++ include/io/stream.h | 23 +- libraries/io/stream/stream-mem.c | 6 + libraries/io/stream/stream-packbitscomp.c | 52 +++-- libraries/io/stream/stream-packbitsdecomp.c | 48 +++-- libraries/io/stream/stream-stdio.c | 7 +- libraries/io/stream/stream.c | 20 +- libraries/io/stream/test/stream-test.c | 219 ++++++++++++++++++-- 8 files changed, 333 insertions(+), 84 deletions(-) create mode 100644 docs/stream.md diff --git a/docs/stream.md b/docs/stream.md new file mode 100644 index 0000000..d636b71 --- /dev/null +++ b/docs/stream.md @@ -0,0 +1,42 @@ +DPTLib > io > stream +==================== +"stream" is a sub-library of DPTLib for creating data sources and transforms. + +The core type `stream_t` is an interface which can be used to wrap, or create, sources of bytes. The interface is primarily byte oriented but block operations are supported too. Byte access is efficiently implemented as a macro. + +If a stream implementor also accepts a stream as input you can chain together the streams to build pipelines, similar to Unix pipes. + +Implementations of TIFF-style PackBits RLE and "Move to Front" compression and decompression are provided but they're really only intended as examples. + +This is a reimplementation of a technique that I was introduced to by Robin Watts and Paul Gardiner. + +Creating a stream +----------------- +See `stream_mem_create()` and its associated functions for a concrete example of how to construct a stream. + +Using a stream +-------------- +Fetch a **byte** by using `stream_getc(stream)`. If the returned value is EOF then the stream has ended. + +Fetch a **block** by first using `stream_remaining_and_fill(stream)`. This will attempt to fill the buffer `stream->buf` up. Note that we don't specify by how much the buffer will be filled to allow for flexibility. + +Chaining streams +---------------- +You can link streams together to create data pipelines that transform data in sequence. The second stream needs to be written to accept a stream as input, e.g. the example `stream_mtfcomp_create()` works like this. + +Provided example streams +------------------------ +* `stream-stdio` + - Creates a stream from a stdio FILE (read only). +* `stream-mem` + - Creates a stream from a single block of memory (read only). +* `stream-packbits` + - Performs PackBits RLE (de)compression. +* `stream-mtfcomp` + - Provides "move to front" adaptive (de)compression. + +Taking it further +----------------- +Streams can be written to "fork" data into two separate pipes, to "cat" two pipes together, to "zip" pipes, etc. + +Remember to never cross the streams. diff --git a/include/io/stream.h b/include/io/stream.h index 7fc0182..bac4bf1 100644 --- a/include/io/stream.h +++ b/include/io/stream.h @@ -58,16 +58,17 @@ typedef result_t stream_op_t(stream_t *s, void *opaque); typedef result_t stream_seek_t(stream_t *s, stream_size_t pos); typedef int stream_get_t(stream_t *s); -typedef stream_size_t stream_fill_t(stream_t *s, stream_size_t need); +typedef stream_size_t stream_fill_t(stream_t *s); typedef stream_size_t stream_length_t(stream_t *s); typedef void stream_destroy_t(stream_t *doomed); +/* This is exposed for efficiency - don't use these directly! */ struct stream { const unsigned char *buf; /**< Current buffer pointer. */ const unsigned char *end; /**< End of buffer pointer (exclusive - points to the char after buffer end). */ - result_t last; /**< Last error. Set when we return EOF? */ + result_t last; /**< Last error. Set whenever we return `stream_EOF`? */ stream_op_t *op; stream_seek_t *seek; @@ -78,31 +79,29 @@ struct stream }; /* - * Main entry points + * User entry points */ stream_op_t stream_op; stream_seek_t stream_seek; +stream_get_t stream_get; /**< `stream_getc()` is the inline alternative */ +stream_fill_t stream_fill; stream_length_t stream_length; stream_destroy_t stream_destroy; -/* Get a byte from a stream. Returns EOF (not stream_EOF) at EOF. */ +/** Get a byte from a stream. Returns `EOF` (not `stream_EOF`) at EOF. */ #define stream_getc(s) (((s)->buf != (s)->end) ? *(s)->buf++ : (s)->get(s)) -/* Put back the last byte gotten. */ +/** Put back the last byte gotten. */ #define stream_ungetc(s) --(s)->buf -/* Returns the number of bytes remaining in the current buffer. */ +/** Returns the number of bytes remaining in the current buffer. */ #define stream_remaining(s) ((stream_size_t) ((s)->end - (s)->buf)) -/* Returns the number of bytes remaining in the current buffer. +/** Returns the number of bytes remaining in the current buffer. * Will attempt to fill the buffer if it's found to be empty. */ #define stream_remaining_and_fill(s) \ - (stream_remaining(s) != 0 ? stream_remaining(s) : (s)->fill(s, 1)) - -/* As above but attempts to make 'need' bytes available. */ -#define stream_remaining_need_and_fill(s, need) \ - (stream_remaining(s) >= (need) ? stream_remaining(s) : (s)->fill(s, need)) + (stream_remaining(s) != 0 ? stream_remaining(s) : (s)->fill(s)) #ifdef __cplusplus } diff --git a/libraries/io/stream/stream-mem.c b/libraries/io/stream/stream-mem.c index 2a93fe2..c490504 100644 --- a/libraries/io/stream/stream-mem.c +++ b/libraries/io/stream/stream-mem.c @@ -45,6 +45,11 @@ static int stream_mem_get(stream_t *s) return EOF; } +static stream_size_t stream_mem_fill(stream_t *s) +{ + return stream_remaining(s); /* no refill */ +} + static stream_size_t stream_mem_length(stream_t *s) { stream_mem_t *sm = (stream_mem_t *) s; @@ -72,6 +77,7 @@ result_t stream_mem_create(const unsigned char *block, sm->base.op = NULL; sm->base.seek = stream_mem_seek; sm->base.get = stream_mem_get; + sm->base.fill = stream_mem_fill; sm->base.length = stream_mem_length; sm->base.destroy = NULL; diff --git a/libraries/io/stream/stream-packbitscomp.c b/libraries/io/stream/stream-packbitscomp.c index abc3753..1d69833 100644 --- a/libraries/io/stream/stream-packbitscomp.c +++ b/libraries/io/stream/stream-packbitscomp.c @@ -60,19 +60,22 @@ static void stream_packbitscomp_reset(stream_packbitscomp_t *c) c->resume = 0; } -static int stream_packbitscomp_get(stream_t *s) +static stream_size_t stream_packbitscomp_fill(stream_t *s) { stream_packbitscomp_t *sm = (stream_packbitscomp_t *) s; + unsigned char *orig_p; unsigned char *p; unsigned char *end; int n; int first; - /* are we only called when buffer empty? */ - assert(sm->base.buf == sm->base.end); + orig_p = p = (unsigned char *) sm->base.buf; // cur buf ptr + end = sm->buffer + sm->bufsz; // abs buf end + + if (p == end) + return stream_remaining(s); // already full - p = sm->buffer; - end = sm->buffer + sm->bufsz; + // assert(stream_remaining(s) < need); if (sm->resume) { @@ -107,15 +110,16 @@ static int stream_packbitscomp_get(stream_t *s) if (second != EOF) stream_ungetc(sm->input); - DBUG(("stream_packbitscomp_get: n=%d", n)); + DBUG(("stream_packbitscomp_fill: n=%d", n)); again: /* more of the current run left to pack */ /* we assume here that we need two spare bytes to continue (which is not * always true) */ - if (p + 2 > end) + if (end - p < 2) { /* save state */ + DBUG(("stream_packbitscomp_fill: %d bytes spare, saving state", end - p)); sm->resume = 1; sm->n = n; sm->first = (unsigned char) first; @@ -127,7 +131,7 @@ static int stream_packbitscomp_get(stream_t *s) case Initial: /* Initial state: Set state to 'Run' or 'Literal'. */ if (n > 1) { - DBUG(("stream_packbitscomp_get: Initial -> Run of %d", MIN(n, 128))); + DBUG(("stream_packbitscomp_fill: Initial -> Run of %d", MIN(n, 128))); sm->state = Run; /* Clamp run lengths to a maximum of 128. Technically they could go @@ -143,7 +147,7 @@ static int stream_packbitscomp_get(stream_t *s) } else { - DBUG(("stream_packbitscomp_get: Initial -> Literal")); + DBUG(("stream_packbitscomp_fill: Initial -> Literal")); sm->state = Literal; sm->lastliteral = p; @@ -155,7 +159,7 @@ static int stream_packbitscomp_get(stream_t *s) case Literal: /* Last object was a literal. */ if (n > 1) { - DBUG(("stream_packbitscomp_get: Literal -> Run of %d", MIN(n, 128))); + DBUG(("stream_packbitscomp_fill: Literal -> Run of %d", MIN(n, 128))); sm->state = LiteralRun; *p++ = (unsigned char)(-MIN(n, 128) + 1); @@ -167,7 +171,7 @@ static int stream_packbitscomp_get(stream_t *s) } else { - DBUG(("stream_packbitscomp_get: Literal -> Literal")); + DBUG(("stream_packbitscomp_fill: Literal -> Literal")); assert(sm->lastliteral); @@ -186,7 +190,7 @@ static int stream_packbitscomp_get(stream_t *s) case Run: /* Last object was a run. */ if (n > 1) { - DBUG(("stream_packbitscomp_get: Run -> Run")); + DBUG(("stream_packbitscomp_fill: Run -> Run")); *p++ = (unsigned char)(-MIN(n, 128) + 1); *p++ = (unsigned char) first; @@ -197,7 +201,7 @@ static int stream_packbitscomp_get(stream_t *s) } else { - DBUG(("stream_packbitscomp_get: Run -> Literal")); + DBUG(("stream_packbitscomp_fill: Run -> Literal")); sm->state = Literal; sm->lastliteral = p; @@ -218,7 +222,7 @@ static int stream_packbitscomp_get(stream_t *s) * which case we convert literal-run-literal to a single literal. */ if (n == 1 && p[-2] == (unsigned char) -1 && ll <= 125) { - DBUG(("stream_packbitscomp_get: LiteralRun merge literal-run-literal")); + DBUG(("stream_packbitscomp_fill: LiteralRun merge literal-run-literal")); ll += 2; /* ..127 */ sm->state = (ll == 127) ? Initial : Literal; *sm->lastliteral = ll; @@ -226,7 +230,7 @@ static int stream_packbitscomp_get(stream_t *s) } else { - DBUG(("stream_packbitscomp_get: LiteralRun -> Run")); + DBUG(("stream_packbitscomp_fill: LiteralRun -> Run")); sm->state = Run; } goto again; @@ -234,16 +238,17 @@ static int stream_packbitscomp_get(stream_t *s) } } - if (p == sm->buffer) + if (p == orig_p) { - DBUG(("stream_packbitscomp_get: no bytes generated in decomp")); - return EOF; /* EOF at start */ + DBUG(("stream_packbitscomp_fill: no bytes generated in decomp")); + // EOF was returned here in older code... + } + else + { + sm->base.end = p; } - sm->base.buf = sm->buffer; - sm->base.end = p; - - return *sm->base.buf++; + return stream_remaining(s); } result_t stream_packbitscomp_create(stream_t *input, int bufsz, stream_t **s) @@ -270,7 +275,8 @@ result_t stream_packbitscomp_create(stream_t *input, int bufsz, stream_t **s) sp->base.op = stream_packbitscomp_op; sp->base.seek = NULL; /* can't seek */ - sp->base.get = stream_packbitscomp_get; + sp->base.get = stream_get; + sp->base.fill = stream_packbitscomp_fill; sp->base.length = NULL; /* unknown length */ sp->base.destroy = NULL; diff --git a/libraries/io/stream/stream-packbitsdecomp.c b/libraries/io/stream/stream-packbitsdecomp.c index 5fe8b66..016e2e5 100644 --- a/libraries/io/stream/stream-packbitsdecomp.c +++ b/libraries/io/stream/stream-packbitsdecomp.c @@ -24,7 +24,7 @@ * more of the time, as we'd be able to suspend and resume within the * repetition and literal generation steps. */ -typedef struct stream_packbits_decomp +typedef struct stream_packbitsdecomp { stream_t base; stream_t *input; @@ -35,19 +35,20 @@ typedef struct stream_packbits_decomp * PackBits algorithm can generate, i.e. at * least 128 bytes. */ } -stream_packbits_decomp_t; +stream_packbitsdecomp_t; -static int stream_packbits_decomp_get(stream_t *s) +static stream_size_t stream_packbitsdecomp_fill(stream_t *s) { - stream_packbits_decomp_t *sm = (stream_packbits_decomp_t *) s; - unsigned char *p; - unsigned char *end; + stream_packbitsdecomp_t *sm = (stream_packbitsdecomp_t *) s; + unsigned char *orig_p; + unsigned char *p; + unsigned char *end; /* are we only called when buffer empty? */ assert(sm->base.buf == sm->base.end); - p = sm->buffer; - end = sm->buffer + sm->bufsz; + orig_p = p = (unsigned char *) sm->base.buf; // cur buf ptr + end = sm->buffer + sm->bufsz; // abs buf end for (;;) { @@ -65,7 +66,7 @@ static int stream_packbits_decomp_get(stream_t *s) * N = -(N - 256) + 1; when N is unsigned, since N > 128 * N = -N + 257; */ - DBUG(("stream_packbits_decomp_get: decomp run: %d -> %d\n", N, -N + 257)); + DBUG(("stream_packbitsdecomp_fill: decomp run: %d -> %d\n", N, -N + 257)); N = -N + 257; @@ -88,7 +89,7 @@ static int stream_packbits_decomp_get(stream_t *s) { N++; /* 0-127 on input means 1-128 bytes out */ - DBUG(("stream_packbits_decomp_get: decomp literal: %d\n", N)); + DBUG(("stream_packbitsdecomp_fill: decomp literal: %d\n", N)); /* ensure we have enough buffer space */ @@ -99,7 +100,6 @@ static int stream_packbits_decomp_get(stream_t *s) } /* copy literal bytes across to output */ - #if 0 while (N--) { @@ -129,8 +129,8 @@ static int stream_packbits_decomp_get(stream_t *s) DBUG(("*** truncated ***\n")); goto exit; /* likely truncated data */ } - avail = MIN(avail, (stream_size_t) N); + avail = MIN(avail, (stream_size_t) N); if (avail == 1) { *p++ = *sm->input->buf++; @@ -150,28 +150,29 @@ static int stream_packbits_decomp_get(stream_t *s) } else { - DBUG(("stream_packbits_decomp_get: *** 128 encountered! ***")); + DBUG(("stream_packbitsdecomp_fill: *** 128 encountered! ***")); /* ignore the 128 case */ } } exit: - if (p == sm->buffer) + if (p == orig_p) { - DBUG(("stream_packbits_decomp_get: no bytes generated in decomp\n")); - return EOF; /* EOF at start */ + DBUG(("stream_packbitsdecomp_fill: no bytes generated in decomp")); + // EOF was returned here in older code... + } + else + { + sm->base.end = p; } - sm->base.buf = sm->buffer; - sm->base.end = p; - - return *sm->base.buf++; + return stream_remaining(s); } result_t stream_packbitsdecomp_create(stream_t *input, int bufsz, stream_t **s) { - stream_packbits_decomp_t *sp; + stream_packbitsdecomp_t *sp; if (bufsz <= 0) bufsz = 128; @@ -181,7 +182,7 @@ result_t stream_packbitsdecomp_create(stream_t *input, int bufsz, stream_t **s) assert(input); - sp = malloc(offsetof(stream_packbits_decomp_t, buffer) + bufsz); + sp = malloc(offsetof(stream_packbitsdecomp_t, buffer) + bufsz); if (!sp) return result_OOM; @@ -192,7 +193,8 @@ result_t stream_packbitsdecomp_create(stream_t *input, int bufsz, stream_t **s) sp->base.op = NULL; sp->base.seek = NULL; /* can't seek */ - sp->base.get = stream_packbits_decomp_get; + sp->base.get = stream_get; + sp->base.fill = stream_packbitsdecomp_fill; sp->base.length = NULL; /* unknown length */ sp->base.destroy = NULL; diff --git a/libraries/io/stream/stream-stdio.c b/libraries/io/stream/stream-stdio.c index 7af554c..04a52d5 100644 --- a/libraries/io/stream/stream-stdio.c +++ b/libraries/io/stream/stream-stdio.c @@ -55,15 +55,14 @@ static int stream_stdio_get(stream_t *s) return *sf->base.buf++; } -/* ensure we have at least 'need' bytes in the buffer. return 0, or EOF. */ -static stream_size_t stream_stdio_fill(stream_t *s, stream_size_t need) +static stream_size_t stream_stdio_fill(stream_t *s) { stream_file_t *sf = (stream_file_t *) s; size_t remaining = stream_remaining(s); size_t read; - if (remaining >= need) - return remaining; /* have enough bytes already */ + if (remaining >= sf->bufsz) + return remaining; /* buffer already full */ /* shift any remaining bytes to the start of the buffer */ if (remaining) diff --git a/libraries/io/stream/stream.c b/libraries/io/stream/stream.c index 7d2e56e..6101315 100644 --- a/libraries/io/stream/stream.c +++ b/libraries/io/stream/stream.c @@ -1,5 +1,6 @@ /* stream.c -- stream system support functions */ +#include #include #include @@ -27,10 +28,27 @@ result_t stream_seek(stream_t *s, stream_size_t pos) return s->seek(s, pos); } +int stream_get(stream_t *s) +{ + if (stream_remaining(s) < 1) + if (s->fill(s) == 0) + return EOF; + + return *s->buf++; +} + +stream_size_t stream_fill(stream_t *s) +{ + if (!s->fill) + return 0; + + return s->fill(s); +} + stream_size_t stream_length(stream_t *s) { if (!s->length) - return -1; // FIXME: Returned as size_t + return 0; return s->length(s); } diff --git a/libraries/io/stream/test/stream-test.c b/libraries/io/stream/test/stream-test.c index ab07315..9bc9d01 100644 --- a/libraries/io/stream/test/stream-test.c +++ b/libraries/io/stream/test/stream-test.c @@ -19,9 +19,75 @@ #include "test/all-tests.h" -#define BUFSZ 0 /* use the default buffer size in each case */ +#define BUFSZ 0 /* use the default buffer size in each case */ +#define USEFILE 0 /* some of tests will use files rather than in-core */ -#define USEFILE 0 +static result_t test_getc_empty(void) +{ + static const unsigned char block[1] = { 'X' }; + + result_t rc = result_TEST_PASSED; + stream_t *s; + int c; + + printf("Test - Empty\n"); + + rc = stream_mem_create(block, 0, &s); + if (rc) + goto Failure; + + if (stream_remaining(s) != 0) + return result_TEST_FAILED; + + c = stream_getc(s); + if (c != stream_EOF) + return result_TEST_FAILED; + + stream_destroy(s); + + return rc; + + +Failure: + return rc; +} + +static result_t test_getc_1byte(void) +{ + static const unsigned char block[1] = { 'X' }; + + result_t rc = result_TEST_PASSED; + stream_t *s; + int c; + + printf("Test - Single Byte\n"); + + rc = stream_mem_create(block, sizeof(block), &s); + if (rc) + goto Failure; + + if (stream_remaining(s) > 1) + goto Failure; + + c = stream_getc(s); + if (c != 'X') + goto Failure; + + if (stream_remaining(s) != 0) + goto Failure; + + c = stream_getc(s); + if (c != stream_EOF) + goto Failure; + + stream_destroy(s); + + return rc; + + +Failure: + return result_TEST_FAILED; +} typedef enum Index { @@ -36,16 +102,16 @@ Index_t; #if !USEFILE static const unsigned char input[] = - "abbcccddddeeeeeffffff\n" - "a bb ccc dddd eeeee ffffff\n" - "We all love aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaardvarks.\n" - "and iiiiii\n" - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"; +"abbcccddddeeeeeffffff\n" +"a bb ccc dddd eeeee ffffff\n" +"We all love aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaardvarks.\n" +"and iiiiii\n" +"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"; #endif -result_t stream_test(const char *resources) +static result_t test_compressors(void) { - result_t err = result_OK; + result_t rc = result_TEST_PASSED; #if USEFILE FILE *in; FILE *out; @@ -57,6 +123,8 @@ result_t stream_test(const char *resources) const unsigned char *p; #endif + printf("Test - Compressors\n"); + #if USEFILE in = fopen("stream-test-input", "rb"); if (in == NULL) @@ -70,27 +138,27 @@ result_t stream_test(const char *resources) #if USEFILE err = stream_stdio_create(in, BUFSZ, &stream[Source]); #else - err = stream_mem_create(input, NELEMS(input), &stream[Source]); + rc = stream_mem_create(input, NELEMS(input), &stream[Source]); #endif - if (err) + if (rc) goto Failure; /* source -> mtfcomp -> mtfdecomp -> packbitscomp -> packbitsdecomp */ - err = stream_mtfcomp_create(stream[Source], BUFSZ, &stream[MTFComp]); - if (err) + rc = stream_mtfcomp_create(stream[Source], BUFSZ, &stream[MTFComp]); + if (rc) goto Failure; - err = stream_mtfdecomp_create(stream[MTFComp], BUFSZ, &stream[MTFDecomp]); - if (err) + rc = stream_mtfdecomp_create(stream[MTFComp], BUFSZ, &stream[MTFDecomp]); + if (rc) goto Failure; - err = stream_packbitscomp_create(stream[MTFDecomp], BUFSZ, &stream[PackBitsComp]); - if (err) + rc = stream_packbitscomp_create(stream[MTFDecomp], BUFSZ, &stream[PackBitsComp]); + if (rc) goto Failure; - err = stream_packbitsdecomp_create(stream[PackBitsComp], BUFSZ, &stream[PackBitsDecomp]); - if (err) + rc = stream_packbitsdecomp_create(stream[PackBitsComp], BUFSZ, &stream[PackBitsDecomp]); + if (rc) goto Failure; #if USEFILE @@ -124,12 +192,121 @@ result_t stream_test(const char *resources) /* fclose(in) is handled by the FILE stream's destruction */ #endif - return result_TEST_PASSED; + return rc; + + +Failure: + return result_TEST_FAILED; +} + +static result_t test_block(void) +{ + result_t rc = result_TEST_PASSED; +#if USEFILE + FILE *in; + FILE *out; +#endif + stream_t *s; + int c; + int i; +#if !USEFILE + const unsigned char *p; +#endif + + printf("Test - Block\n"); + +#if USEFILE + in = fopen("stream-test-input", "rb"); + if (in == NULL) + goto Failure; + + out = fopen("stream-test-output", "wb"); + if (out == NULL) + goto Failure; +#endif + +#if USEFILE + err = stream_stdio_create(in, BUFSZ, &s); +#else + rc = stream_mem_create(input, NELEMS(input), &s); +#endif + if (rc) + goto Failure; + +#if USEFILE + for (;;) + { + //c = stream_getc(s); + //if (c == EOF) + // break; + + fputc(c, out); + } +#else + for (p = input; ; p++) + { + const stream_size_t need = 100; + stream_size_t remaining; + + remaining = stream_remaining_and_fill(s); + if (remaining < need) + { + printf("needed %zu bytes, but only %zu bytes in buffer\n", need, remaining); + break; + } + + c = stream_getc(s); + if (c == EOF) + break; + + if (c != *p) + { + printf("difference at %ld\n", p - input); + rc = result_TEST_FAILED; + } + } + + printf("%ld bytes processed.\n", p - input); +#endif + + stream_destroy(s); + +#if USEFILE + fclose(out); + /* fclose(in) is handled by the FILE stream's destruction */ +#endif + + return rc; Failure: + return result_TEST_FAILED; +} - printf("\n\n*** result_t %x\n", err); +result_t stream_test(const char *resources) +{ + result_t rc; + + rc = test_getc_empty(); + if (rc) + goto Failure; + + rc = test_getc_1byte(); + if (rc) + goto Failure; + + rc = test_compressors(); + if (rc) + goto Failure; + + rc = test_block(); + if (rc) + goto Failure; + return result_TEST_PASSED; + + +Failure: + printf("\n\n*** result=%x\n", rc); return result_TEST_FAILED; }