procedure xencode: write structure to file procedure xdecode: read structure from file
link xcodes
November 19, 1997; Bob Alexander
Contributor: Ralph E. Griswold
See also: codeobj.icn
This file is in the public domain.
Note: This version handles the encoding of records using canonical names: record0, record1, ... . This allows programs to decode files by providing declarations for these names when the original declarations are not available. This version also provides for procedures and files present in the encoded file that are not in the decoding program. This version should be merged with the ordinary version. Description ----------- These procedures provide a way of storing Icon values in files and retrieving them. The procedure xencode(x,f) stores x in file f such that it can be converted back to x by xdecode(f). These procedures handle several kinds of values, including structures of arbitrary complexity and even loops. The following sequence will output x and recreate it as y: f := open("xstore","w") xencode(x,f) close(f) f := open("xstore") y := xdecode(f) close(f) For "scalar" types -- null, integer, real, cset, and string, the above sequence will result in the relationship x === y For structured types -- list, set, table, and record types -- y is, for course, not identical to x, but it has the same "shape" and its elements bear the same relation to the original as if they were encoded and decoded individually. Files, co-expressions, and windows cannot generally be restored in any way that makes much sense. These objects are restored as empty lists so that (1) they will be unique objects and (2) will likely generate run-time errors if they are (probably erroneously) used in computation. However, the special files &input, &output, and &errout are restored. Not much can be done with functions and procedures, except to preserve type and identification. The encoding of strings and csets handles all characters in a way that it is safe to write the encoding to a file and read it back. xdecode() fails if given a file that is not in xcode format or it the encoded file contains a record for which there is no declaration in the program in which the decoding is done. Of course, if a record is declared differently in the encoding and decoding programs, the decoding may be bogus. xencoden() and xdecoden() perform the same operations, except xencoden() and xdecoden() take the name of a file, not a file. xencodet() and xdecodet() are like xencode() and xdecode() except that the trailing argument is a type name. If the encoded decoded value is not of that type, they fail. xencodet() does not take an opt argument. ____________________________________________________________ Complete calling sequences -------------------------- xencode(x, f, p) # returns f where x is the object to encode f is the file to write (default &output) p is a procedure that writes a line on f using the same interface as write() (the first parameter is always a the value passed as "file") (default: write) xencode(f, p) # returns the restored object where f is the file to read (default &input) p is a procedure that reads a line from f using the same interface as read() (the parameter is always a the value passed as "file") (default: read) The "p" parameter is not normally used for storage in text files, but it provides the flexibility to store the data in other ways, such as a string in memory. If "p" is provided, then "f" can be any arbitrary data object -- it need not be a file. For example, to "write" x to an Icon string: record StringFile(s) procedure main() ... encodeString := xencode(x,StringFile(""),WriteString).s ... end procedure WriteString(f,s[]) every f.s ||:= !s f.s ||:= "\n" return end ____________________________________________________________ Notes on the encoding --------------------- Values are encoded as a sequence of one or more lines written to a plain text file. The first or only line of a value begins with a single character that unambiguously indicates its type. The remainder of the line, for some types, contains additional value information. Then, for some types, additional lines follow consisting of additional object encodings that further specify the object. The null value is a special case consisting of an empty line. Each object other than &null is assigned an integer tag as it is encoded. The tag is not, however, written to the output file. On input, tags are assigned in the same order as objects are decoded, so each restored object is associated with the same integer tag as it was when being written. In encoding, any recurrence of an object is represented by the original object's tag. Tag references are represented as integers, and are easily recognized since no object's representation begins with a digit. Where a structure contains elements, the encodings of the elements follow the structure's specification on following lines. Note that the form of the encoding contains the information needed to separate consecutive elements. Here are some examples of values and their encodings: x encode(x) ------------------------------------------------------- 1 N1 2.0 N2.0 &null "\377" "\377" '\376\377' '\376\377' procedure main p "main" co-expression #1 (0) C [] L N0 set() "S" N0 table("a") T N0 "a" ["hi","there"] L N2 "hi" "there" A loop is illustrated by L2 := [] put(L2,L2) for which x encode(x) ------------------------------------------------------- L2 L N1 2 The "2" on the third line is a tag referring to the list L2. The tag ordering specifies that an object is tagged *after* its describing objects, thus the list L2 has the tag 2 (the integer 1 has tag 1). Of course, you don't have to know all this to use xencode and xdecode.