Chapter 27
Stream Internals
The concepts embodied by a fz_stream object, and details of how to use them were
given in chapter 12 The Stream interface. The above, relatively rich, set of functions
are implemented on a fairly simple basic structure.
To implement your own fz_stream, simply define a creation function, of the
form:
fz_stream *fz_new_stream_foo(fz_context *ctx, <more parameters here>) { fz_stream *stm; foo_state *state; state = <create structure to hold foo specific stream state> stm = fz_new_stream(ctx, state, foo_next, foo_close); <set stm->seek if required> <set stm->meta if required> return stm; }
Note that some fz_try/fz_catch-ery may be required as part of the setup for
state.
The hard work for this function is done using fz_new_stream, and two ‘foo’ specific
functions, foo_next and foo_close. First let’s look at fz_new_stream:
/* fz_new_stream: Create a new stream object with the given internal state and function pointers. state: Internal state (opaque to everything but implementation). next: Should provide the next set of bytes (up to max) of stream data. Return the number of bytes read, or EOF when there is no more data. close: Should clean up and free the internal state. May not throw exceptions. */ fz_stream *fz_new_stream(fz_context *ctx, void *state, fz_stream_next_fn *next, fz_stream_close_fn *close);
This creates the main fz_stream structure, populates it with the given pointers
(state, foo_next and foo_close) and sets the internal buffer pointers up to indicate
an empty buffer.
As soon as anyone tries to read from the buffer (or to find out how many bytes are
available), the MuPDF stream functions will cause foo_next to be called. This is a
function of the following type:
/* fz_stream_next_fn: A function type for use when implementing fz_streams. The supplied function of this type is called whenever data is required, and the current buffer is empty. stm: The stream to operate on. max: a hint as to the maximum number of bytes that the caller needs to be ready immediately. Can safely be ignored. Returns -1 if there is no more data in the stream. Otherwise, the function should find its internal state using stm->state, refill its buffer, update stm->rp and stm->wp to point to the start and end of the new data respectively, and then "return *stm->rp++". */ typedef int (fz_stream_next_fn)(fz_context *ctx, fz_stream *stm, size_t max);
When the stream is closed, the foo_close function will be called. This should be a
function of type fz_stream_close_fn:
/* fz_stream_close_fn: A function type for use when implementing fz_streams. The supplied function of this type is called when the stream is closed, to release the stream specific state information. state: The stream state to release. */
In our example, if the state was created by a simple fz_malloc_struct(ctx,
foo_state) then foo_close might be as simple as a fz_free(ctx, state). If the
internal state of the stream is more complex then the destructor will be similarly
more complex.
These three functions (creation, next and close) are all that is required to define a
stream.
Optionally, you can also define a seek and/or a meta function, using functions of the
following types:
/* fz_stream_seek_fn: A function type for use when implementing fz_streams. The supplied function of this type is called when fz_seek is requested, and the arguments are as defined for fz_seek. The stream can find its private state in stm->state. */ typedef void (fz_stream_seek_fn)(fz_context *ctx, fz_stream *stm, fz_off_t offset, int whence); /* fz_stream_meta_fn: A function type for use when implementing fz_streams. The supplied function of this type is called when fz_meta is requested, and the arguments are as defined for fz_meta. The stream can find its private state in stm->state. */ typedef int (fz_stream_meta_fn)(fz_context *ctx, fz_stream *stm, int key, int size, void *ptr);