The basic Input/Output class in Karma is the ``channel''. This is provided by the ch package. A channel is a (possibly) full duplex buffered stream of data.
A channel may be associated with a file (disc file, named FIFO, tty, etc.), a connection to another process, a memory object, and others. A single interface for reading and writing is presented by the ch package.
Channels are similar to the standard C library FILE * streams, but fix a number of serious deficiencies in FILE * streams, such as:
Besides these changes, a number of powerful enhancements are available with Channels:
The interface to channel objects is designed to be similar to the standard C library FILE * streams interface, however there a few subtle differences:
Below are a few simple examples which show how to use Channels.
This example shows a simple ASCII file reader for a 2-dimensional data format. The first line contains the horizontal and vertical lengths. The remaining lines contain the array data.
#include <stdio.h> #include <errno.h> #include <karma.h> #include <karma_ch.h> #include <karma_m.h> int main (int argc, char **argv) { Channel channel; int xlen, ylen, x, y; float *array; extern char *sys_errlist[]; static char usage_string[] = "readfile infile"; if (argc != 2) { fprintf (stderr, "Usage:\t%s\n", usage_string); exit (RV_BAD_PARAM); } /* Open the input file (this is a simple 2D format) */ if ( ( channel = ch_open_file (argv[1], "r") ) == NULL ) { fprintf (stderr, "Error opening file: \"%s\"\t%s\n", argv[1], sys_errlist[errno]); exit (RV_CANNOT_OPEN); } /* Read the array size */ ch_scanf (channel, "%d %d", &xlen, &ylen); /* Create 2D float array */ if ( ( array = (float *) m_alloc (sizeof *array * xlen * ylen) ) == NULL ) { fprintf (stderr, "Error allocating image\n"); exit (RV_MEM_ERROR); } /* Now read data values in from the input file and write to the array */ for (y = 0; y < naxis2; ++y) for (x = 0; x < naxis1; ++x) { ch_scanf (channel, "%e", array + x + y * xlen); } ch_close (channel); /* Now do something with the array. Since the point of this example is to show how to use channels, let's not bother with being really useful */ return (RV_OK); } /* End Function main */
If you wanted to add support for automatic uncompression of the input file you would only need to change the call to ch_open_file to:
channel = ch_open_file (argv[1], "rz");
If the file has the extension .gz then the input file is automatically uncompressed. Note that you must have the <gzip> (GNU zip) programme in your PATH.
This example shows how to ``load'' a simple 2-dimensional data file which is stored in binary format. The first two words contain the size of the array and the rest of the file contains the binary single precision floating point data. The first and last values in the array are multiplied by 2.0 and ``written'' back. Note that this file format is machine-dependant: if you were to read the same file on a big-endian and little-endian machines, the results would be quite different. So one would not use such a file format in real life. The point of this example is to show how to use memory-mapping to avoid slow reading.
#include <stdio.h> #include <errno.h> #include <karma.h> #include <karma_ch.h> int main (int argc, char **argv) { Channel channel; char *ptr; int xlen, ylen; float *array; extern char *sys_errlist[]; static char usage_string[] = "modfile infile"; if (argc != 2) { fprintf (stderr, "Usage:\t%s\n", usage_string); exit (RV_BAD_PARAM); } /* Map the input file (this is a simple 2D format) */ if ( ( channel = ch_map_disc (argv[1], K_CH_MAP_ALWAYS, TRUE, TRUE) ) == NULL ) { fprintf (stderr, "Error opening file: \"%s\"\t%s\n", argv[1], sys_errlist[errno]); exit (RV_CANNOT_OPEN); } /* Get the mapped file */ ptr = ch_get_mmap_addr (channel); /* Get the array size */ xlen = *(int *) ptr; ptr += sizeof xlen; ylen = *(int *) ptr; ptr += sizeof ylen; array = (float *) ptr; /* Now multiply the first and last elements in the array by 2.0 */ array[0] *= 2.0; array[xlen - 1 + (ylen - 1) * xlen] *= 2.0; /* Close the channel: this also causes the modified data to be written */ ch_close (channel); return (RV_OK); } /* End Function main */
Notice how the call to ch_map_disc specifies that the file should be mapped so the memory region is writable and changes made to the region are written back to disc. An application that did not want to modify the file would instead ask for a read-only mapping (an example application would be one that displays the first and last values in the array).