diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2011-10-14 04:55:05 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2011-10-14 04:55:05 +0200 |
commit | 6aa0e0017d7d0cddc006da885946934b06949a91 (patch) | |
tree | 66b688ec32e2f91266db760b1762f2a50cc52036 /libslang/examples/saveobj.sl | |
parent | a966db5b71328f6adf9dd767e64b322a3bd7ed9c (diff) | |
download | erlang-slang-fork-6aa0e0017d7d0cddc006da885946934b06949a91.tar.gz |
include libslang-1.4.9 and automatically build it and link erlang-slang against it
few (erlang) people will still have libslang-1.4.9 installed or spend time
to get it to link against the driver
Diffstat (limited to 'libslang/examples/saveobj.sl')
-rw-r--r-- | libslang/examples/saveobj.sl | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/libslang/examples/saveobj.sl b/libslang/examples/saveobj.sl new file mode 100644 index 0000000..c7a39a1 --- /dev/null +++ b/libslang/examples/saveobj.sl @@ -0,0 +1,630 @@ +% This example shows how one can save the values of slang variables to a file +% and then load those values back in another instance of the program. +% +% The following code defines two public functions: +% +% save_object (FILE, obj, ...); +% (obj,...) = load_object (FILE); +% +% For example, +% a = [1:20]; +% b = 2.4; +% c = struct { d, e }; c.d = 2.7; c.e = "foobar"; +% save_object ("foo.save", a, b, c); +% +% saves the values of the variables a, b, c to a file called "foo.save". +% These values may be retrieved later, e.g., by another program instance +% via: +% (a,b,c) = load_object ("foo.save"); +% +% Caveats: +% +% 1. Not all object types are supported. The ones supported include: +% +% All integer types (Int_Type, Char_Type, Long_Type, ...) +% Float_Type, Double_Type +% String_Type, BString_Type +% Null_Type +% +% as well as the container classes of the above objects: +% Struct_Type, Array_Type +% +% 2. The algorithm for saving Struct_Type is recursive. This allows one to +% save a linked-list of Struct_Type objects. However, due to the recursive +% nature of the algorithm and the interpreter's finite stack size, such +% linked-lists cannot be arbitrarily long. +% +% 3. Objects are saved in the native representation. As such, the files are +% not portable across machine architectures. +% +% File Format: +% +% Each slang object is written to the file with the following format +% Data_Type (integer) +% Length of Data Bytes (unsigned integer) +% Data Bytes +% +% Here, Data Bytes may specify other objects if the parent is a container +% object. + +%_debug_info = 1; + +static variable Type_Map = Assoc_Type[Integer_Type, -1]; +static variable Write_Object_Funs = Assoc_Type[Ref_Type]; +static variable Read_Object_Funs = Assoc_Type[Ref_Type]; + +!if (is_defined ("_Save_Object_Cache_Type")) +typedef struct +{ + index +} +_Save_Object_Cache_Type; + +static variable Object_Cache; +static variable Num_Cached; + +static define delete_cache () +{ + Object_Cache = NULL; + Num_Cached = 0; +} + +static define create_cache () +{ + delete_cache (); +} + + +% If the object does not need cached, return the object. +% If the object needs cached but does not exist in the cache, cache it and +% return it. +% Otherwise, the object is in the cache, to return a _Save_Object_Cache_Type +% representing the object. +static define cache_object (obj) +{ + variable t = typeof (obj); + + if ((t != Array_Type) + and (0 == is_struct_type (obj))) + return obj; + + variable n = Num_Cached; + variable c = Object_Cache; + while (n) + { + if (__eqs (c.obj, obj)) + { + obj = @_Save_Object_Cache_Type; + obj.index = n; + return obj; + } + + c = c.next; + n--; + } + + c = struct {obj, next}; + c.obj = obj; + c.next = Object_Cache; + Object_Cache = c; + Num_Cached++; + %vmessage ("%S added to cache", c.obj); + + return obj; +} + +static define get_object_from_cache (index) +{ + variable depth = Num_Cached - index; + variable c = Object_Cache; + while (depth) + { + c = c.next; + depth--; + } + return c.obj; +} + +static define get_type_id (type) +{ + variable id; + id = Type_Map[string (type)]; + if (id == -1) + verror ("Object %S is not supported", type); + return id; +} + + + +static define write_not_implemented (fp, object) +{ + () = fprintf (stderr, "write for object %S not implemented\n", typeof (object)); + return 0; +} + +static define do_fwrite (a, fp) +{ + %vmessage ("Writing %S", a); + variable n = fwrite (a, fp); + if (n == -1) + verror ("fwrite failed: %s", errno_string (errno)); + return n; +} + +static define do_fread (t, n, fp) +{ + variable b; + if (n != fread (&b, t, n, fp)) + verror ("fread failed: %s", errno_string (errno)); + %vmessage ("Read %S", b); + return b; +} + +static define do_ftell (fp) +{ + variable pos = ftell (fp); + if (-1 == pos) + verror ("ftell failed: %s", errno_string (errno)); + return pos; +} + +static define do_fseek (fp, ofs, whence) +{ + if (-1 == fseek (fp, ofs, whence)) + verror ("fseek failed: %s", errno_string (errno)); +} + +static define sizeof (t) +{ + variable size; + + switch (t) + { case Char_Type or case UChar_Type: size = 1; } + { case Int16_Type or case UInt16_Type: size = 2; } + { case Int32_Type or case UInt32_Type: size = 4; } + { case Float_Type: size = 4; } + { case Double_Type: size = 8; } + { + verror ("sizeof (%S) not implemented", t); + } + + return size; +} + + +static define write_numbers (fp, a) +{ + variable size = sizeof (_typeof (a)); + variable num = do_fwrite (a, fp); + return num * size; +} + +static define read_numbers (fp, t, nbytes) +{ + variable size = sizeof (t); + nbytes /= size; + return do_fread (t, nbytes, fp); +} + +static define write_string (fp, a) +{ + return do_fwrite (a, fp); +} + +static define read_string (fp, t, nbytes) +{ + t = do_fread (Char_Type, nbytes, fp); + if (nbytes == 1) + t = char (t); + + return t; +} + +static define start_header (fp, id) +{ + variable len = write_numbers (fp, id); + variable pos = do_ftell (fp); + len += write_numbers (fp, 0); % temporary + + variable h = struct + { + pos, len + }; + h.pos = pos; + h.len = len; + + return h; +} + +static define end_header (fp, h, num) +{ + do_fseek (fp, h.pos, SEEK_SET); + () = do_fwrite (num, fp); + do_fseek (fp, 0, SEEK_END); + return h.len + num; +} + +static define id_to_datatype (id) +{ + variable keys, values; + + keys = assoc_get_keys (Type_Map); + values = assoc_get_values (Type_Map); + variable i = where (values == id); + !if (length (i)) + verror ("Corrupt file? Unknown type-id (%d)", id); + return eval (keys[i][0]); +} + +static define write_scalars (fp, a) +{ + variable id = get_type_id (typeof (a)); + variable h = start_header (fp, id); + variable len = write_numbers (fp, a); + return end_header (fp, h, len); +} + +static define read_null (fp, t, nbytes) +{ + return NULL; +} + +static define write_null (fp, a) +{ + return 0; +} + +static define write_object (); +static define read_object (); + +% Array DataBytes: int num_dims, int dims[num_dims], type, Data... +static define write_array (fp, a) +{ + variable dims, num_dims, data_type; + (dims, num_dims, data_type) = array_info (a); + variable len; + variable id = get_type_id (data_type); + + len = write_numbers (fp, num_dims) + write_numbers (fp, dims) + + write_numbers (fp, id); + + % For now allow numbers or strings + if (_typeof(a) == String_Type) + { + foreach (a) + { + variable elem = (); + len += write_object (fp, elem); + } + + return len; + } + + len += write_numbers (fp, a); + + return len; +} + +static define read_array (fp, type, nbytes) +{ + variable num_dims = do_fread (Int_Type, 1, fp); + variable dims = do_fread (Int_Type, num_dims, fp); + type = do_fread (Int_Type, 1, fp); + variable len; + len = 1; + foreach (dims) + len *= (); + + type = id_to_datatype (type); + + variable v; + + if (type == String_Type) + { + v = String_Type [len]; + _for (0,len-1,1) + { + variable i = (); + v[i] = read_object (fp, NULL); + } + } + else v = do_fread (type, len, fp); + + reshape (v, dims); + return v; +} + +% Data Bytes: int num_fields. String-Object [num_fields], Values[num_fields] +static define write_struct (fp, a) +{ + variable fields = get_struct_field_names (a); + variable len = write_numbers (fp, typecast (length (fields), Int_Type)); + foreach (fields) + { + variable f = (); + len += write_object (fp, f); + } + + foreach (fields) + { + f = (); + len += write_object (fp, get_struct_field (a, f)); + } + + return len; +} + +static define read_struct (fp, type, nbytes) +{ + variable num_fields = do_fread (Int_Type, 1, fp); + variable fields = String_Type[num_fields]; + variable i; + _for (0, num_fields-1, 1) + { + i = (); + fields[i] = read_object (fp, NULL); + } + + variable s = @Struct_Type (fields); + + % make sure it is in the cache in case the fields refer to it. + if (type != _Save_Object_Cache_Type) + () = cache_object (s); + + _for (0, num_fields-1, 1) + { + i = (); + set_struct_field (s, fields[i], read_object (fp, NULL)); + } + + return s; +} + +% Data Bytes: int index +static define write_cached_object (fp, a) +{ + return write_numbers (fp, a.index); +} + + +static define read_cached_object (fp, type, nbytes) +{ + variable index = read_numbers (fp, Int_Type, nbytes); + return get_object_from_cache (index); +} + + +static define add_type (t, w, r, id) +{ + t = string (t); + Type_Map[t] = id; + Write_Object_Funs[t] = w; + Read_Object_Funs [t] = r; +} + +add_type (Char_Type, &write_numbers, &read_numbers, 1); +add_type (UChar_Type, &write_numbers, &read_numbers, 2); +add_type (Short_Type, &write_numbers, &read_numbers, 3); +add_type (UShort_Type, &write_numbers, &read_numbers, 4); +add_type (Integer_Type, &write_numbers, &read_numbers, 5); +add_type (UInteger_Type,&write_numbers, &read_numbers, 6); +add_type (Long_Type, &write_numbers, &read_numbers, 7); +add_type (ULong_Type, &write_numbers, &read_numbers, 8); +add_type (Float_Type, &write_numbers, &read_numbers, 9); +add_type (Double_Type, &write_numbers, &read_numbers, 10); +add_type (String_Type, &write_string, &read_string, 11); +add_type (BString_Type, &write_string, &read_string, 12); +add_type (Struct_Type, &write_struct, &read_struct, 13); +add_type (Array_Type, &write_array, &read_array, 14); +add_type (Null_Type, &write_null, &read_null, 15); + +add_type (_Save_Object_Cache_Type, &write_cached_object, &read_cached_object, 1000); + +static define get_write_function (type) +{ + variable key = string (type); + if (assoc_key_exists (Write_Object_Funs, key)) + return Write_Object_Funs[key]; + verror ("No write method defined for %S", key); +} + +static define get_read_function (type) +{ + variable key = string (type); + if (assoc_key_exists (Read_Object_Funs, key)) + return Read_Object_Funs[key]; + verror ("No read method defined for %S", key); +} + +static define write_object (fp, a) +{ + a = cache_object (a); + variable id = get_type_id (typeof (a)); + + variable h = start_header (fp, id); + variable f = get_write_function (typeof (a)); + variable num = (@f)(fp, a); + %vmessage ("Done Writing %S", a); + return end_header (fp, h, num); +} + +static define read_object (fp, statusp) +{ + variable type, nbytes; + variable status = fread (&type, Integer_Type, 1, fp); + if (status == -1) + { + if (statusp == NULL) + verror ("No more objects in file"); + + @statusp = 0; + return 0; + } + + nbytes = do_fread (Integer_Type, 1, fp); + type = id_to_datatype (type); + + variable f = get_read_function (type); + variable v = (@f)(fp, type, nbytes); + + % Necessary because String_Type may get written as BString_Type + if (type != _Save_Object_Cache_Type) + v = typecast (v, type); + + %vmessage ("Read %S", v); + if (statusp != NULL) + @statusp = 1; + + return v; +} + + +public define save_object () +{ + if (_NARGS < 2) + usage ("save_object (file, obj1, ...)"); + + variable objs = __pop_args (_NARGS - 1); + variable file = (); + + variable fp = fopen (file, "w+"); + if (fp == NULL) + verror ("Unable to open %s: %s", file, errno_string (errno)); + + create_cache (); + + foreach (objs) + { + variable obj = ().value; + () = write_object (fp, obj); + } + + delete_cache (); +} + +public define load_object (file) +{ + variable fp = fopen (file, "r"); + if (fp == NULL) + verror ("Unable to open %s: %s", file, errno_string (errno)); + + create_cache (); + forever + { + variable status; + variable obj = read_object (fp, &status); + if (status == 0) + break; + obj; + } + delete_cache (); +} + +#iffalse +% Regression test +static define failed (s, a, b) +{ + vmessage ("Failed: %s: wrote: '%S', read '%S'\n", s, a, b); +} + +static define test_eqs (); +static define test_eqs (a, b) +{ + if ((typeof (a) != typeof (b)) + or (_typeof (a) != _typeof (b))) + { + failed ("typeof", typeof(a), typeof(b)); + return 0; + } + + if (typeof (a) != Struct_Type) + { + if (length (a) != length (b)) + { + failed ("test_eqs length", a, b); + return 0; + } + + if (length (where (a != b))) + { + failed ("test_eqs", a, b); + return 0; + } + return 1; + } + + variable fa, fb; + fa = get_struct_field_names (a); + fb = get_struct_field_names (b); + + !if (test_eqs (fa, fb)) + { + failed ("test_eqs: fa, fb"); + return 0; + } + + if (length (fa) != length (fb)) + return 0; + + foreach (fa) + { + variable name = (); + variable va, vb; + va = get_struct_field (a, name); + vb = get_struct_field (b, name); + if ((typeof (va) == Struct_Type) + and (typeof (vb) == Struct_Type)) + { + % void loop + continue; + } + !if (test_eqs (va, vb)) + return 0; + } + + return 1; +} + +static define test_save_object () +{ + variable x0 = 1278; + variable x1 = 2.3; + variable x2 = "foo"; + variable x3 = struct + { + a, b, c, d + }; + variable x4 = [1:10]; + variable x5 = ["a","b","c","d"]; + + x3.a = "foo"; + x3.b = PI; + x3.c = [1:20]; + x3.d = x3; + + save_object ("foo.sv", x0,x1,x2,x3,x4,x5); + + variable y0,y1,y2,y3,y4,y5; + + (y0,y1,y2,y3,y4,y5) = load_object ("foo.sv"); + + !if (test_eqs (x0, y0)) + failed ("x0", x0, y0); + !if (test_eqs (x1, y1)) + failed ("x1", x1, y1); + !if (test_eqs (x2, y2)) + failed ("x2", x2, y2); + + !if (test_eqs (x3, y3)) + failed ("x3", x3, y3); + + !if (test_eqs (x4, y4)) + failed ("x4", x4, y4); + !if (test_eqs (x5, y5)) + failed ("x5", x5, y5); + + vmessage ("Regression Test Done"); +} + +test_save_object (); +#endif |