Skip to content

User_Manual:_Code:_io.c

Derek Lamb edited this page Jun 7, 2017 · 5 revisions

User Manual: Code: io.c

(Return to Code or the User Manual)

io.c contains some simple I/O utilities -- save and restoral of a FLUX arena from an ASCII file, and some miscellany for dumping trees and/or rendering simple drawings to debug the geometry code. The basic I/O interface is also used for generation of new simulation arenas: the simplest way to create a new FLUX interface is to generate a string in the FLUX ASCII data format, and then parse it with the file I/O routines. On the Perl interface side, that is accomplished by the Perl routine str2world.

The ASCII data format is a custom keyword-based format that is intended to be simple to emit and parse. It succeeds in the former, but not necessarily in the latter.

World File I/O

FLUX's ASCII format is supported through two routines in io.c, read_world and fprint_world. You can take individual action on a World by feeding lines into the single line parser, footpoint_action.

fprint_world

int fprint_world(FILE *file, WORLD *world, char *header)

Writes the given WORLD object to the file handle file, which must be open for writing. If you supply a non-NULL string in header then it is written to the file before the world is dumped there.

There is no particularly meaningful return value.

print_world

int print_world(WORLD *a, char *header)

Convenience interface to fprint_world, sends the file to the POSIX standard output.

read_world

WORLD *read_world(FILE *file, WORLD *a)

Reads in a complete world from the specified FILE (this is a standard i/o library file handle, which should already be open for reading). If a is NULL, then a new WORLD object is created. If you feed in an existing WORLD object, then it is updated using the information in the file.

This is the main way to create new WORLDs, and in fact the Perl str2world constructor simply writes its string to a temporary file and calls read_world on it.

Recent versions of read_world call world_check on the newly created WORLD object before returning it.

next_line

char *next_line(FILE *file)

Line snarfer and preparser used by read_world: strips comments, skips all-comment, all-whitespace, or blank lines.

footpoint_action

int footpoint_action(WORLD *world, char *s)

Parses a line from a FLUX dump file and performs the appropriate action on the supplied WORLD. Returns 0 on success, or a cryptic error code on failure. The parser is hand-coded and primitive: it simply checks the first few characters of each word on the line and uses sscanf to pick out appropriate parameters based on the line.

The main complication comes from the fact that the numeric type (NUM) can be set in the headers to be either double or float (to save memory); this means that the format codes for sscanf must themselves be generated on-the-fly.

Most errors are thrown by setting "badstr" to contain an error message, which causes the error message to be printed to stderr and the routine to return 2.

Miscellany

Here are some miscellaneous printing routines that come in handy for debugging, but aren't normally used.

fprint_tree

void fprint_tree(FILE *file, void *tree, int label_offset, int link_offset, int indent, void ((*printer)()))

This is a tree-walking function that prints all nodes of a tree -- rather like the tree_walker routine in data.c. The printer should accept 5 arguments, and be declared like so:

printer(FILE *f, void *object, int indent, int label_offset, int link_offset)

The printer should interpret the object, ASCIIfy it, and print it to the open file handle f. The indent is a count of space characters to be printed at the beginning of the line.

print_tree

void print_tree(void *tree, int label_offset, int link_offset, int indent, void ((*printer)()))

Convenience interface to fprint_tree, for printing to stdout.

fprint_node

This is a generic printer function for fprint_tree - it just prints label information for each node in a generic tree.

fdump_fluxon

void fdump_fluxon(FILE *f, FLUXON *foo, int indent)

This is a printer function for fprint_tree, that prints a whole fluxon and all its vertices. You can also call it with an individual fluxon. (Note that this function omits the later parameters of the printer, and therefore only works on architectures that comply with POSIX, placing the function arguments in order on the stack.)

fdump_all_fluxon_tree

void fdump_all_fluxon_tree(FILE *f, FLUXON *foo)

Prints out the whole fluxon tree from a world -- you pass in the top fluxon in the tree.

Binary dump/restore

FLUX can emit and restore binary data in machine format, for rapid serialization / restoration of simulation arenas. This code can support a general purpose tag-based binary data format, but currently contains just enough code to support first-stage parallelization.

The binary dump code is intended for fast dump/restore, without (necessarily) being portable across architectures or (in teh case of fluxon_pipe) even across unrelated processes within a particular machine.

Binary dump format

Binary dumps are in a containerized format that includes a series of tagged fields. The content and length of each field are arbitrary. Each field begins with three long integers: a fence (value 0xAB5C155A), a type code, and a length of the data segment (in bytes). The data segment should be followed immediately by the next field fence. A special field, BD_END, is used to end the file.

The type codes are defined in io.h, as BD_mnemonic. The mnemonics should be consecutive integers starting with 1. The macro MD_MAX_TYPENO should contain the highest allowed type code.

Writing binary dump files

Just open a file descriptor using the open system call, and start dumping fields to it using these routines. When you are done, call binary_dump_end and close the descriptor. That is all!

binary_dump_end

binary_dump_end(int fd)

Writes an end-of-file field. It is your responsibility to close the file descriptor.

binary_dump_fluxon_pipe

binary_dump_fluxon_pipe(int fd, FLUXON *f)

Writes out all the VERTEX information in a particular fluxon, in a suitable format for first-stage parallelization: the information contains actual pointer values, so it is only useful for a related pid (e.g. parent from a fork call), and even then only if the source process hasn't allocated any new data structures since the fork (and the parent process hasn't deleted any since then).

Reading binary dump files

You can read binary state from a binary dump file by calling binary_read_dumpfile. It will parse the file and dispatch each field to the appropriate read routine. Individual read routines are not listed here as they match the dump routines above.

binary_read_dumpfile

binary_read_dumpfile(int fd, WORLD *w)

Like ASCII .flux files, binary dump files modify the state of an existing WORLD structure, so you have to pass in not just an open file descriptor (as returned by the open system call) but also an allocated WORLD structure.

Writing new binary routines

New binary I/O routines should include a dump routine, a read routine, a new type code (in io.h), and a new case: in binary_read_dumpfile.

It would be useful to write enough component dumpers to store a complete WORLD object, but that has not (yet) been done. Such a format would require some thought about backwards compatibility in case of code updates.