Reading and Writing to Memory-Files

Opening and closing a memory-file is just academic. To make the whole shebang work, you must be able to read and write data.

To continue from last week’s Lesson, I introduce the mem_getc() and mem_putc() functions. Both use the mem_file structure and rely upon the mem_file pointer being allocated. They follow the format of the fgetc() and fputc() functions, which are the basic file I/O functions for cooked data. (The fread() and frwite() functions deal with raw data.)

Here is the mem_putc() function:

/* put a character to a memory file
   input- single character, memory file handle
   output- character written or
           EOF on failure
 */
int mem_putc(int c, struct mem_file *m)
{
    int *index;

    if( m->address==NULL )
        return(EOF);

    index = m->address+m->size;
    *index = c;

    m->size++;
    /* check for overflow */
    if( (m->size % DEFAULT_SIZE) == 0 )
    {
        m->address=realloc(m->address,m->size-1+DEFAULT_SIZE);
        if( m->address==NULL )
            return(EOF);
    }

    return(c);
}

The first test is to ensure that the mem_file structure passed is allocated. If not, the EOF constant is returned. This return value is consistent with the fputc() function.

Variable index serves as a reference into the memory-file’s storage. It’s set to reference the end of the used portion of the memory-file’s allocated space: index = m->address+m->size; This function always appends data unless the memory-file size value has been otherwise manipulated.

The character passed, c, is appended to the memory-file: *index = c;

The size offset is incremented: m->size++;

Immediately after incrementing the size value, a test is made to ensure that allocated storage hasn’t been exceeded: if( (m->size % DEFAULT_SIZE) == 0 ) If the result is TRUE, another DEFAULT_SIZE chunk is allocated for the memory-file, along with appropriate error-checking.

The function returns the character added, c.

Here is the mem_getc() function:

/* fetch a character from a memory file
   input- memory file handle
   output- character retrieved or
           EOF on failure
 */
int mem_getc(struct mem_file *m)
{
    int *index;

    if( m->address==NULL )
        return(EOF);

    if( m->offset>m->size )
        return(EOF);

    index = m->address+m->offset;
    m->offset++;

    return( *index );
}

If the address is NULL, the function returns immediately with an EOF.

Next a check is done to ensure that the function isn’t reading beyond the end of the memory-file: if( m->offset>m->size ) If so, the function returns EOF.

When everything is kosher, the value of index is set to the current offset in the file, where data is being read: index = m->address+m->offset;

The value of offset is incremented in anticipation of the next read operation: m->offset++;

Finally, the value refenced at the buffer’s original offset is returned to the caller: return( *index );

You can view the full code on GitHub. In the main() function, 'A' is written to the created and opened memory-file, which is then read back in:

Memory file 'memory file' created
Timestamp = Sat Sep  9 12:29:03 2023

Put character 'A' to file 'memory file'
Fetched character 'A' from file 'memory file'

Memory file 'memory file' closed

One concern I have is with the memory reallocation when the original allocated chunk size (DEFAULT_SIZE) is surpassed. I wrote a test that sent 8K of data to the memory-file, which was read back and output with no problems. Still, I’d want to do more tests to ensure that everything works properly. And if I encounter problems as this project progresses, I’ll mention them in a future post.

Next week’s Lesson covers moving the memory-file offset pointer in preparation for reading and writing chunks of data — random memory-file access.

Leave a Reply