|
|
|
mdx_util - Memory Descriptor (MDX) UtilitiesThe 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!)
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)) ;
(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.
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.mdx_util.c
mdx_util.h
(See libgpl
for the complete source, including support routines and build files.)