Portable Unformatted Input/Output
Normally, unformatted input/output is not portable between different machine architectures because of differences in the way various machines represent binary data. However, it is possible to produce binary files that are portable by specifying the XDR keyword with the OPEN procedures. XDR (for eXternal Data Representation) is a scheme under which all binary data is written using a standard "canonical" representation. All machines supporting XDR understand this standard representation and have the ability to convert between it and their own internal representation.
XDR represents a compromise between the extremes of unformatted and formatted input/output:
- It is not as efficient as purely unformatted input/output because it does involve the overhead of converting between the external and internal binary representations.
- It is still much more efficient than formatted input/output because conversion to and from ASCII characters is much more involved than converting between binary representations.
- It is much more portable than purely unformatted data, although it is still limited to those machines that support XDR. However, XDR is freely available and can be moved to any system.
XDR Considerations
The primary differences in the way IDL input/output procedures work with XDR files, as opposed to files opened normally are as follows:
- To use XDR, you must specify the XDR keyword when opening the file.
- The only input/output data transfer routines that can be used with a file opened for XDR are READU and WRITEU.
- XDR converts between the internal and standard external binary representations for data instead of simply using the machine's internal representation.
- Since XDR adds extra "bookkeeping" information to data stored in the file and because the binary representation used may not agree with that of the machine being used, it does not make sense to access an XDR file without using XDR.
- OPENW and OPENU normally open files for both input and output. However, XDR files can only be opened in one direction at a time. Thus, using these procedures with the XDR keyword results in a file open for output only. OPENR works in the usual way.
- The length of strings is saved and restored along with the string. This means that you do not have to initialize a string of the correct length before reading a string from the XDR file. (This is necessary with normal unformatted input/output and is described in Using Unformatted Input/Output).
- For efficiency reasons, byte arrays are transferred as a single unit; therefore, byte variables must be initialized to the correct number of elements for the data to be input, or an error will occur. For example, given the statements,
;Open a file for XDR output. OPENW, /XDR, 1, 'data.dat' ;Write a 10-element byte array. WRITEU, 1, BINDGEN(10) ;Close the file and re-open it for input. CLOSE, 1 & OPENR, /XDR, 1, 'data.dat'then the statement,
;Try to read the first byte only. B = 0B & READU, 1, Bresults in the following error:
% READU: Error encountered reading from file unit: 1.Instead, it is necessary to read the entire byte array back in one operation using a statement such as:
;Read the whole array back at once. B=BYTARR(10) & READU, 1, BThis restriction does not exist for other data types.
IDL XDR Conventions for Programmers
IDL uses certain conventions for reading and writing XDR files. If your only use of XDR is through IDL, you do not need to be concerned about these conventions because IDL takes care of it for you. However, programmers who want to create IDL-compatible XDR files from other languages need to know the actual XDR routines used by IDL for various data types. The following table summarizes this information.
The routines used for type COMPLEX, DCOMPLEX, and STRING are not primitive XDR routines. Their definitions are as follows:
bool_t xdr_complex(xdrs, p) XDR *xdrs; struct complex { float r, i} *p; { return(xdr_float(xdrs, (char *) &p->r) && xdr_float(xdrs, (char *) &p->i)); } bool_t xdr_dcomplex(xdrs, p) XDR *xdrs; struct dcomplex { double r, i} *p; { return(xdr_double(xdrs, (char *) &p->r) && xdr_double(xdrs, (char *) &p->i)); } bool_t xdr_counted_string(xdrs, p) XDR *xdrs; char **p; { int input = (xdrs->x_op == XDR_DECODE); short length; /* If writing, obtain the length */ if (!input) length = strlen(*p); /* Transfer the string length */ if (!xdr_short(xdrs, (char *) &length)) return(FALSE); /* If reading, obtain room for the string */ if (input) { *p = malloc((unsigned) (length + 1)); *p[length] = '\0'; /* Null termination */ } /* If the string length is nonzero, transfer it */ return(length ? xdr_string(xdrs, p, length) : TRUE); }
Example: Reading C-Generated XDR Data with IDL
The following C program produces a file containing different types of data using XDR. The usual error checking is omitted for the sake of brevity.
#include <stdio.h> #include <rpc/rpc.h> [ xdr_complex() and xdr_counted_string() included here ] main() { static struct { /* Output data */ unsigned char c; short s; long l; float f; double d; struct complex { float r, i } cmp; char *str; } data = {1, 2, 3, 4, 5.0, { 6.0, 7.0}, "Hello" }; u_int c_len = sizeof(unsigned char); /* Length of a char */ char *c_data = (char *) &data.c; /* Addr of byte field */ FILE *outfile; /* stdio stream ptr */ XDR xdrs; /* XDR handle */ /* Open stdio stream and XDR handle */ outfile = fopen("data.dat", "w"); xdrstdio_create(&xdrs, outfile, XDR_ENCODE); /* Output the data */ (void) xdr_bytes(&xdrs, &c_data, &c_len, c_len); (void) xdr_short(&xdrs, (char *) &data.s); (void) xdr_long(&xdrs, (char *) &data.l); (void) xdr_float(&xdrs, (char *) &data.f); (void) xdr_double(&xdrs, (char *) &data.d); (void) xdr_complex(&xdrs, (char *) &data.cmp); (void) xdr_counted_string(&xdrs, &data.str); /* Close XDR handle and stdio stream */ xdr_destroy(&xdrs); (void) fclose(outfile); }
Running this program creates the file data.dat containing the XDR data. The following IDL statements can be used to read this file and print its contents:
;Create structure containing correct types. DATA={S, C:0B, S:0, L:0L, F:0.0, D:0.0D, CMP:COMPLEX(0), STR:''} ;Open the file for input. OPENR, /XDR, 1, 'data.dat' ;Read the data. READU, 1, DATA ;Close the file. CLOSE, 1 ;Show the results. PRINT, DATA
Executing these IDL statements produces the output:
For further details about XDR, consult the XDR documentation for your machine. Sun users should consult their Network Programming manual.