↑ Software ↑


mdx_util - Memory Descriptor (MDX) Utilities

The MDX_UTIL package provides functions for creating and manipulating dynamically sized and dynamically growing memory regions.

This package was inspired by my SDX_UTIL string descriptor package which, in turn, was inspired by VAX/VMS descriptors which consisted of (i) 16 bits of flags including the data type of the object being described, (ii) the 16-bit length of the object, and (iii) the 32-bit address of the object.

While I originally expected MDX_UTIL to be a simple, byte-oriented clone of SDX_UTIL, I unfortunately included the ability to treat a memory region as an array of fixed-size elements — a C++ STL vector, if you wish, and my most common use case. Part way into the writing of the package, I was torn between putting the array functionality into a separate package, say ADX_UTIL, or to keep going and decide later after some experience with an array-capable MDX_UTIL package.

I chose the latter course,
for better or worse,
although I'm bothered all the same,
of course, of course.

(We miss you, Mr. Ed!)

Byte-Oriented Memory Operations

A sequence of bytes is simply an array of one-byte elements in MDX_UTIL's view. The following example creates a zero-length memory descriptor (whose default element size is one byte) and concatenates 50 copies of a 13-byte sequence of bytes, "Hello, World!" in this case. There is no significance aside from convenience to my using a string here; one could just as easily create a 13-byte array of arbitrary, "!isprint()able" bytes.

Note that there are no separators between the sequences of bytes. The example appends the 13-byte sequences end-on-end, 50 times, to the initially empty memory buffer. The result is 650 (13*50) bytes of data. The example adds a NUL terminator as the 651st byte to produce an actual string in the I-could-care-less memory buffer.

    #include  <stdio.h>		-- Standard I/O definitions.
    #include  <string.h>	-- Standard C string functions.
    #include  "mdx_util.h"	-- Memory Descriptor utilities.

    MemoryDx  pbx ;
    size_t  i ;

    mdxCreate (NULL, 0, MdxDynamic, &pbx) ;
    for (i = 0 ;  i < 50 ;  i++) {
        mdxAddS (pbx, false,	-- Add sequence of bytes.
                 "Hello, World!", strlen ("Hello, World!")) ;
    mdxAddB (pbx, 0) ;		-- Add NUL byte.
    printf ("Complete region: \"%s\"\n", (char *) mdxRegionP (pbx, 0)) ;
    mdxDestroy (pbx) ;

Alternatively, don't add the NUL byte, but retrieve the length of 650:

    printf ("Complete region: \"%*s\"\n",
            (int) mdxLength (descriptor),
            (char *) mdxRegionP (pbx, 0)) ;

Multi-Byte-Oriented Array Operations

(NOTE: The example below is very awkward, as you will see. Use the ADX_UTIL package instead — it handles all the bookkeeping and housekeeping for you.)

A memory region can also be treated as an array of fixed-size elements. The following example reads a complete text file into an array of SDX_UTIL string descriptors, one element per line, a convenient framework for a simple text editor.

    #include  "mdx_util.h"		-- Memory Descriptor utilities.
    #include  "sdx_util.h"		-- String Descriptor utilities.

    FILE  *file ;
    size_t  length, numLines = 0 ;
    ssize_t  count ;
    MemoryDx  mdx ;
    StringDx  *element30, sdx ;

    mdxCreate (NULL, 0, MdxDynamic, &mdx) ;
    mdxElementSize (mdx, (ssize_t) sizeof (StringDx)) ;

    file = fopen ("example.txt", "r") ;
    for ( ; ; ) {			-- Break on EOF or error.
        sdxCreate (NULL, -1, SdxDynamic, &sdx) ;
        if (sdxReadLine (file, sdx, &count) || (count < 1))  break ;
        mdxAddS (mdx, false, (const void *) &sdx, sizeof (StringDx)) ;
        numLines++ ;
    fclose (file) ;

	-- Access line 20. --

    printf ("Line 20: \"%s\"\n",
            sdxStringZ (*((StringDx) mdxRegionP (mdx, 20-1)))) ;

	-- Insert a line between line 30 and line 31. --

    length = mdxLength (mdx) ;		-- Make room for new line.
    mdxSetLength (mdx, length + 1) ;
    element30 = (StringDx *) mdxRegionP (mdx, 30-1) ;
    memmove ((void *) &element30[2], (const void *) &element30[1],
             (length - 30) * sizeof (StringDx)) ;
					-- Create and store new line.
    sdxCreate ("I'm the new line 31!!!", -1, SdxVolatile, &sdx) ;
    element30[1] = sdx ;

	-- Delete lines 41-45. --

    element40 = (StringDx *) mdxRegionP (mdx, 40-1) ;
					-- Destroy descriptors being deleted
    for (i = 1 ;  i <= 5 ;  i++) {	-- (or save them in cut/undo buffer).
        sdxDestroy (element40[i]) ;
    length = mdxLength (mdx) ;		-- Move lines 46..n down to line 41.
    memmove ((void *) &element40[1], (const void *) &element40[6],
             (length - 45) * sizeof (StringDx)) ;
					-- Decrease region length by 5 lines.
    mdxSetLength (mdx, length - (45-40)) ;

Hmmm, functional, but not pretty. Use the ADX_UTIL package instead!

Note on the example above: the memory region is an array of pointers (StringDx) to string descriptors (_StringDx), not an array of string descriptors themselves.

Public Procedures

mdxAddB() - adds a byte to a described memory region.
mdxAddS() - adds a sequence of bytes to a described memory region.
mdxAlignment() - sets or gets the byte boundary for alignment.
mdxCopy() - copy the contents of one memory descriptor to another.
mdxCreate() - creates a memory descriptor.
mdxDestroy() - destroys a memory descriptor.
mdxDuplicate() - duplicates a memory descriptor.
mdxErase() - erases the contents of a memory descriptor.
mdxGrow() - increases the size of a descriptor's memory region.
mdxIncrement() - sets or gets the block size used to expand regions.
mdxLength() - returns the length of a described memory region.
mdxOwn() - takes ownership of a described memory region.
mdxRegionP() - returns a pointer to a described memory region.
mdxSetLength() - sets the length of a described memory region.

Source Files


(See libgpl for the complete source, including support routines and build files.)

Alex Measday  /  E-mail