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);