class ref name: ioblockstream
category-group: io
layer: 2

synopsis.
The ioblockstream object ( ioblockstream_o ) is designed to provide blocks of input to other classes repeatedly. This allows for improved efficiency over the alternative of loading a huge chunk into a string_o and then fetching blocks from the string. The object has a reader and a writer, based on the classes iosreader_o and ioswriter_o. These 3 classes form the core of this system. Reading and writing are performed in threads. Using this object is rather complex and precision is required. However, it does a lot.

description.
When ioblockstream_o::run() is invoked, it launches 2 threads: one for the reader (iosreader_o) and another for the writer (ioswriter_o). Prior to this, a fair amount of set-up is required. There are 6 functions that must be created: 3 for the reader and 3 for the writer.

There are 2 ways to set up this object: (a) create sub-classes for the reader and writer, or (b) make the 6 functions and provide them to ioblockstream_o as function pointers. In the example below, the latter approach is taken.

member functions (primary)

ioblockstream_o()
SIGNATURE: ioblockstream_o ()
SYNOPSIS:
creates a a new angle object. The value of the angle value is initialized to 0, the units of measurement default to the global units of measurement (degrees, if the application has not set this via angle_o::set_default_units() . The angle is considered "unset", and hence cannot be [successfully] used [yet] in angular operations.
 

ioblockstream_o(ioblockstream_o)
SIGNATURE: ioblockstream_o (const ioblockstream_o &that)
 

operator = (ioblockstream_o)
SIGNATURE: const ioblockstream_o &operator = (const ioblockstream_o &that)
SYNOPSIS: copies exactly the RHS object ("that"), to the existing object instance.
RETURNS: a reference to the current object
 

destructor
SIGNATURE: ~ioblockstream_o ()
SYNOPSIS: virtual destructor. All object contents are reinitialized to the at-construction state.
 

reset()
SIGNATURE: int reset ()
SYNOPSIS: resets the object to its at-construction, default state.
RETURNS: 0
 

usage.
The following lists describe the steps required for using this class. The first list is method "b" - providing function pointers to ioblockstream_o; the second list describes method "a" - making subclasses of iosreader_o and ioswriter_o.

method b:

  1. make a ioblockstream_o instance with the singleton parameter set to TRUE (let's say the variable name is 'xios').
  2. make 6 subroutines (not class member functions) to handle the reader and writer pre-processing, post-processing, reader data fetch, and writer data handling. pre- and post-process functions all have the same function signiture: "int XXX (size_t &nb, int *)".
  3. pass pointers to the subroutines to 'xios', like so:
    xios.set_read_preprocess (R_prep); xios.set_read_postprocess (R_post); xios.set_read_fetchblock (R_fech); xios.set_write_preprocess (W_prep); xios.set_write_postprocess (W_post); xios.set_write_handleblock (W_exec);
  4. set up a buffer array and a string_o instance that will hold this buffer, and pass the string to the ioblockstream object via the member function set_buffer():
    xios.set_buffer(s, 1024, 64);
    The 2nd and 3rd arguments are the reader and writer block sizes, respectively.
  5. if the input source is to come from a single string object, or if you otherwise know the total size (in bytes) of the input source, you can call 'set_maxbytes()'. Its parameter is the total size of the input stream:
    xios.set_maxbytes (z_strlen(lotsa_text));
    This will allow the reader to monitor the total number of bytes read. Otherwise, processing will stop when fetch_nextblock() returns a non-zero value. . Hopefully, this size does not exceed the capacity of a 'size_t' variable.
  6. call 'run()':
    xios.run();
method a:
(TBC)

note.
This class object is one of the newer classes in the Z Directory.

examples.
This example is longer than most found in the reference manual. It exemplifies the more involved set-up required by the ioblockstream_o class. Here, a block of text is read in 256-byte chunks, and passed to a Rijndael_crypt_o instance (AES encryption object), in 32-byte chunks.

The management of getting and processing the blocks of data, though of different lengths, is handled internally. The writer will loop 8 times for each block the reader fetches a block (8 * 32 = 256). While the writer is processing these blocks, the reader is blocked by a semaphore, and vice-versa.

This code is complete. It should compile and run, successfully encrypting and decrypting a block of text. It is a stripped-down version (for brevity) of part of the AES SQA test driver.

#include "stdafx.h"
#include "z_dirt.h"
#include "z_txtstring.h"
#include "z_crypt.h"
#include "z_iosblock.h"

#define PASSWORD "The human and a fish CAN coexist"
#define IN_BLOCKSIZE 256
#define OT_BLOCKSIZE  32

static char *lotsa_text =
"This sample source code demos how to use the 'ioblockstream_o' class.\n\
The reader (iosreader_o) invokes a function stored in the function pointer\n\
'pre', if one has been loaded. This loading is done in the call to\n\
'set_read_preprocess()'. Likewise for the reader's post-processing and\n\
\"fetch\" functions, and the writer's pre-, post-, and data processing.\n\
The reader's 'pre' function establishes pointers to the data source and the\n\
full length. Reader post-processing and writer pre-processing do nothing.\n\
The fetch subroutine copies 256 bytes from the current block position into\n\
'bigbuf' and advances the pointer to the next block. Finally, the writer\n\
processor (subroutine 'Write_crypt()') sets up local string_o objects,\n\
required for the Rijndael cipher object, and encrypts the current block.\n\
The [encrypted] output is concatenated to the string 'gsot'.\n";

static size_t nb_ios = 0;
static char source[2048];               // a buffer copy of 'lotsa_text'
static byte_t bwork[1024];              // goes with 'pswb' string object
static byte_t bigim[1024];              // goes with 'gsim'  string object
static byte_t bigot[1024];              // goes with 'gsot'  string object
static const char *ios_now   = NULL;
static const char *ios_beg   = NULL;
static const byte_t *bis_now = NULL;
static const byte_t *bis_beg = NULL;
static string_o *pswb = NULL;
static string_o *gsim = NULL;
static string_o *gsot = NULL;
static Rijndael_crypt_o *grex = NULL;

int R_epreproc    (size_t &nb, int *pie);
int R_dpreproc    (size_t &nb, int *pie);
int R_postproc    (size_t &nb, int *pie);
int W_preproc     (size_t &nb, int *pie);
int W_epostproc   (boolean, byte_t *, size_t &, int *);
int W_dpostproc   (boolean, byte_t *, size_t &, int *);
int R_efetch  (byte_t *, size_t &, int *);
int R_dfetch  (byte_t *, size_t &, int *);
int W_encrypt (byte_t *, size_t &, int *);
int W_decrypt (byte_t *, size_t &, int *);
int die (const string_o &, const string_o & = "");


//----------------------------------------------------------------------
int main (const int argc, const char *argv[])
{
    int i, j, ie0, ie1;
    size_t nb;

    z_start();
    string_o sin, sie, sot, s_orig;
    ioblockstream_o x(TRUE);

    z_strcpy (source, lotsa_text);                // copy text to buffer
    for (i=0; i < 2; i++)
    {
        nb = z_strlen(source);                    // find how big is text
        j = nb % 32;                              // how many blocks of 32
        if (nb - (j * 32) > 0)                    // got excess?
        {
            if (!i)                               // 1st time: trim the size
                { nb -= j; source[nb] = '\0'; }
            else                                  // 2nd time: still bad; die
                return die("", "could not set up a block size of 32");
        }
    }
    nb_ios = nb;

    grex = new Rijndael_crypt_o;
    gsim = new string_o("");
    gsot = new string_o("");
    pswb = new string_o("");
    if (grex == NULL || gsim == NULL || gsot == NULL || pswb == NULL)
        return -1;

    pswb->setmode_binary();                     // be in binary mode
    gsim->setmode_binary();                     // for encryption / decryption,
    gsot->setmode_binary();                     // all strings employed should
    pswb->set ((char *) bwork, 1024, 0);        //buffers
    gsim->set ((char *) bigim, 1024, 0);        // set the string object data
    gsot->set ((char *) bigot, 1024, 0);        // buffers to fixed, immovable

    ie0 = grex->set_key(PASS32);
    ie1 = grex->set_blocksize(OT_BLOCKSIZE);
    if (ie0 || ie1)
        return die("set_blocksize", "could not set key &/or block size");

    //..........................................................
    // loop 2 times: 1st: encrypt; 2nd: decrypt
    //..........................................................
    for (i=0; i < 2; i++)
    {
        x.reset();                      // reset the 'ioblockstream_o' object
        grex->beg_ciphering_byblocks();
        if (i == 0)
        {
            x.set_read_preprocess   (R_epreproc);
            x.set_write_postprocess (W_epostproc);
            x.set_write_handleblock (W_encrypt);
            x.set_read_fetchblock   (R_efetch);
        }
        else
        {
            x.set_read_preprocess   (R_dpreproc);
            x.set_write_postprocess (W_dpostproc);
            x.set_write_handleblock (W_decrypt);
            x.set_read_fetchblock   (R_dfetch);
        }
        x.set_read_postprocess (R_postproc);
        x.set_write_preprocess (W_preproc);
        x.set_buffer (*pswb, IN_BLOCKSIZE, OT_BLOCKSIZE, nb_ios);

        ie0 = x.config(&ie1);
        if (ie0) return die(ne, "config", "problem confguring ioblockstream");

        x.run();
        grex->end_ciphering_byblocks();
    }

    s_orig = source;
    if (*gsot == s_orig)
    {
        if (volume() >= zLev_MsgVol_Info)
            std::cout << "AEStest_o::ios_test() - MATCH!\n";
    }
    else
        die(ne, "AEStest_o::ios_test", "", "STRINGS DO NOT MATCH");

    z_finish();
    return 0;
}

//----------------------------------------------------------------------
// R_EPRE -- reader pre-processor subroutine, for encryption
//----------------------------------------------------------------------
int R_epreproc (size_t &nb, int *pie)
{
    *pie = 0;
    nb = 0;
    ios_beg = ios_now = source;
    nb_ios = ::z_strlen(ios_now);
    return 0;
}

//----------------------------------------------------------------------
// R_DPREPROC -- reader pre-processor subroutine, for decryption
//----------------------------------------------------------------------
int R_dpreproc (size_t &nb, int *pie)
{
    *pie = 0;
    nb = 0;

    pswb->force_size(IN_BLOCKSIZE);
    bis_beg = bis_now = (byte_t *) gsim->data();
    nb_ios = gsim->size();
    return 0;
}

//----------------------------------------------------------------------
int R_postproc (size_t &nb, int *pie)
{
    *pie = 0;
    return 0;
}

//----------------------------------------------------------------------
// R_EFETCH -- gets the next 256-byte chunk
//----------------------------------------------------------------------
int R_efetch (byte_t *ptr, size_t &nb, int *pie)
{
    int ie0, ie1;
    *pie = 0;
    nb = IN_BLOCKSIZE;
    if (ios_now >= ios_beg + nb_ios)
        { nb = 0; return 1; }

    z_bcopy (bwork, (byte_t *) ios_now, IN_BLOCKSIZE);
    ios_now += IN_BLOCKSIZE;
    return 0;
}

//----------------------------------------------------------------------
// R_DFETCH -- get the next chunk from gsim (encrypted data string)
//----------------------------------------------------------------------
int R_dfetch (byte_t *ptr, size_t &nb, int *pie)
{
    int ie0, ie1;

    *pie = 0;
    nb = IN_BLOCKSIZE;
    if (bis_now >= bis_beg + nb_ios)
        { nb = 0; return 1; }

    z_bcopy (bwork, bis_now, IN_BLOCKSIZE);
    bis_now += nb;
    return 0;
}

//----------------------------------------------------------------------
int W_preproc (size_t &nb, int *pie)
{
    *pie = 0;
    return 0;
}

//----------------------------------------------------------------------
int W_epostproc (boolean more, byte_t *ptr, size_t &nb, int *pie)
{
    int ie0 = 0, ie1 = 0;
    *pie = 0;
    if (more)
        ie0 = W_encrypt(ptr, nb, &ie1);
    ie1 = grex->close_cipher_byblock();
    return (!ie0 && !ie1) ? 0 : -1;
}

//----------------------------------------------------------------------
int W_dpostproc (boolean more, byte_t *ptr, size_t &nb, int *pie)
{
    int ie0 = 0, ie1 = 0;
    *pie = 0;
    if (more)
        ie0 = W_decrypt(ptr, nb, &ie1);
    ie1 = grex->close_cipher_byblock();
    return (!ie0 && !ie1) ? 0 : -1;
}

//----------------------------------------------------------------------
// W_ENCRYPT -- writer: encrypt a block, starting from position 'ptr'
//----------------------------------------------------------------------
int W_encrypt (byte_t *ptr, size_t &nb, int *pie)
{
    int ie0, ie1;
    string_o sin, sot;
    size_t nb_rem;
    byte_t obbuf[OT_BLOCKSIZE+4];
    byte_t *p_beg = bwork;
    byte_t *p_end = &bwork[1024];
    nb_rem = p_end - ptr;

    *pie = 0;
    nb = OT_BLOCKSIZE;
    z_bnull(obbuf, OT_BLOCKSIZE);
    sin.setmode_binary();
    sot.setmode_binary();
    sin.set((char *) ptr,   nb_rem, nb);
    sot.set((char *) obbuf, nb,     nb);

    ie0 = grex->encrypt_nextblock (sin, sot, nb, &ie1);
    if (!ie0)
        *gsim += sot;
    return ie0;
}

//----------------------------------------------------------------------
// W_DECRYPT -- writer: decrypt a block, starting from position 'ptr'
//----------------------------------------------------------------------
int W_decrypt (byte_t *ptr, size_t &nb, int *pie)
{
    int ie0, ie1;
    byte_t obbuf[OT_BLOCKSIZE+4];
    string_o sin, sot;

    *pie = 0;
    nb = OT_BLOCKSIZE;
    z_bnull(obbuf, OT_BLOCKSIZE);
    sin.setmode_binary();
    sot.setmode_binary();
    sin.set((char *) ptr,   nb, nb);
    sot.set((char *) obbuf, nb, nb);

    ie0 = grex->decrypt_nextblock (sin, sot, nb, &ie1);
    if (!ie0)
        *gsot += sot;
    return ie0;
}

//----------------------------------------------------------------------
// DIE -- show an error message and exit out
//----------------------------------------------------------------------
int die (const string_o &fun, const string_o &s)
{
    std::cerr << "    FAILURE: ";
    if (fun.size() > 0)
        std::cerr << "'" << fun << "()';";
    if (s.size() > 0)
        std::cerr << " " << s;
    std::cerr << "\n";

    exit(1);
    return -1;
}