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.