diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..7d020b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2010, Troy Hanson http://tpl.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..780f7ff --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src +EXTRA_DIST = LICENSE tests lang doc diff --git a/README b/README new file mode 100644 index 0000000..aefeea2 --- /dev/null +++ b/README @@ -0,0 +1,56 @@ +tpl: fast, easy serialization in C +============================================================================== + +Documentation for tpl is available in the doc/ directory or at: + + http://tpl.sourceforge.net + +You can build tpl as a library, like so: + + ./configure + make + make install + +This installs libtpl.so and libtpl.a into a standard system library directory. +You can customize the install directory using configure's "--prefix" option: + + ./configure --prefix=/some/directory + +For other options accepted by configure, run "./configure --help". + +NON-LIBRARY OPTION +------------------ +Alternatively, if you don't want to muck around with libraries, you can simply +copy these two files into your own C project and build them with your program: + + src/tpl.h + src/tpl.c + +WINDOWS +------- +You can build tpl as a DLL under Visual Studio 2008. Or you can use MinGW or +Cygwin. + +SELF-TEST SUITE +--------------- +The automated self-test can be run by doing: + + cd tests + make + +LICENSE +------- +The BSD license applies to this software. The text is in the LICENSE file. + +CREDITS +------- +Many people have contributed to tpl, both bits of code and ideas. Rather than +listing them all here, at risk of omitting anyone- I just wish to say thank +you. Some particular features are noted with contributors' names in the +ChangeLog. + +Feel free to send me questions, comments or bug reports. + +Troy D. Hanson, February 5, 2010 +thanson@users.sourceforge.net + diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..d717c2b --- /dev/null +++ b/bootstrap @@ -0,0 +1,11 @@ +#!/bin/sh + +# THIS SCRIPT IS FOR PROJECT MAINTAINER ONLY +# It is executed only to generate "configure" + +set -x +aclocal -I config +autoheader +libtoolize --copy --force +automake --foreign --add-missing --copy +autoconf diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..3d2fe0e --- /dev/null +++ b/configure.ac @@ -0,0 +1,28 @@ +AC_PREREQ(2.59) + +AC_INIT([libtpl], [1.4], [thanson@users.sourceforge.net]) +AC_CONFIG_SRCDIR(src/tpl.c) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_HEADERS(config/config.h) +AM_INIT_AUTOMAKE +AC_PROG_CC +dnl next 4 lines are a hack to avoid libtool's +dnl needless checks for C++ and Fortran compilers +m4_undefine([AC_PROG_CXX]) +m4_defun([AC_PROG_CXX],[]) +m4_undefine([AC_PROG_F77]) +m4_defun([AC_PROG_F77],[]) +AC_PROG_LIBTOOL + +dnl detect Cygwin or MinGW and use mmap family replacements +AC_CONFIG_LIBOBJ_DIR(src/win) +case $host in + *-*-mingw32* | *-*-cygwin* | *-*-windows*) + AC_LIBOBJ(mmap) + AC_MSG_NOTICE([using custom mmap for Cygwin/MinGW]) + ;; +esac + +AC_CONFIG_FILES(src/win/Makefile src/Makefile Makefile) +AC_OUTPUT + diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..b440c38 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,26 @@ +all: css userguide pdf changelog perl + +userguide: txt/userguide.txt + asciidoc --unsafe --out-file=html/userguide.html -a linkcss=1 -a theme=tdh txt/userguide.txt + +changelog: txt/ChangeLog.txt + asciidoc --out-file=html/ChangeLog.html txt/ChangeLog.txt + +.PHONY: pdf + +pdf: txt/userguide.txt + a2x -f pdf $< + mv txt/userguide.pdf pdf/ + cd html && ln -sf ../pdf/userguide.pdf userguide.pdf + rm -f txt/userguide.xml + +perl: txt/perl.txt + asciidoc --unsafe --out-file=html/perl.html -a linkcss=1 -a theme=tdh txt/perl.txt + +css: html/toc.css + cat /etc/asciidoc/stylesheets/xhtml11.css html/toc.css > html/tdh.css + cp /etc/asciidoc/stylesheets/xhtml11-quirks.css html/tdh-quirks.css + +docbook: txt/userguide.txt + asciidoc -b docbook --out-file=/tmp/userguide.xml txt/userguide.txt + xmlto -o html html-nochunks /tmp/userguide.xml diff --git a/doc/NOTES b/doc/NOTES new file mode 100644 index 0000000..9133089 --- /dev/null +++ b/doc/NOTES @@ -0,0 +1,11 @@ +# maintainer notes + +# IE6 png gamma bug: +# PNG images in IE6 display with wrong background colors, +# solution: save PNG in Gimp *Without save gamma checked* + +#update sourceforge web site: +#cd html +#scp *.{html,css} thanson@shell.sourceforge.net:/home/groups/t/tp/tpl/htdocs +#cd img +#scp *.png *.jpg thanson@shell.sourceforge.net:/home/groups/t/tp/tpl/htdocs/img diff --git a/doc/html/ChangeLog.html b/doc/html/ChangeLog.html new file mode 100644 index 0000000..587e668 --- /dev/null +++ b/doc/html/ChangeLog.html @@ -0,0 +1,796 @@ + + + + + +tpl ChangeLog + + + + + +
+

Version 1.6 (2010-??-??)

+
+
    +
  • +

    +Fixed a bug in the Windows version of tpl that prevented an application + from serializing more than once to the same file- the file stayed locked until + the application was closed. (thanks, Werner Krattenthaler!) +

    +
  • +
  • +

    +Fixed a documentation error to indicate that tpl_dump when used in the + TPL_GETSIZE mode stores its result in a size_t rather than a uint32_t + (thanks, M. Nunberge!) +

    +
  • +
  • +

    +Fixed a typo in the User Guide example of packing a linked link (thanks, Bryan Mishkin!) +

    +
  • +
+
+

Version 1.5 (2010-02-05)

+
+
    +
  • +

    +tpl now builds as a DLL under Microsoft Visual Studio! (thanks, degski and Zhang Yafei!) +

    +
  • +
  • +

    +there are now two download options: the tarball and the Visual Studio solution +

    +
  • +
  • +

    +a crash in tpl_free on certain format strings has been fixed (thanks, Eric Rose!) +

    +
  • +
  • +

    +fixed a bug in tpl_dump on 64-bit, big-endian platforms +

    +
  • +
  • +

    +changed some pointer casts from long to uintptr_t since 64-bit Windows has 32-bit longs +

    +
  • +
  • +

    +tpl has been downloaded 4,195 times. +

    +
  • +
+
+

Version 1.4 (2009-04-21)

+
+
    +
  • +

    +fixed-length arrays can now be multi-dimensional like i## +

    +
  • +
  • +

    +fixed-length string arrays like s# are now supported +

    +
  • +
  • +

    +nested structures can now be expressed, using the dollar symbol, e.g. S(ci$(cc)) +

    +
  • +
  • +

    +tpl_dump can use a caller-allocated output buffer (TPL_MEM|TPL_PREALLOCD) +

    +
  • +
  • +

    +tpl_load can tolerate excess space in input buffer (TPL_MEM|TPL_EXCESS_OK) +

    +
  • +
  • +

    +implement TPL_FXLENS flag for tpl_peek to get lengths of fixed-length arrays +

    +
  • +
  • +

    +implement TPL_GETSIZE flag for tpl_dump to get dump size without dumping +

    +
  • +
  • +

    +fix success return code from tpl_dump(TPL_FD,...) (thanks, Max Lapan!) +

    +
  • +
  • +

    +deprecated the wildcard unpacking S(*) feature +

    +
  • +
+
+

Version 1.3 (2009-02-10)

+
+
    +
  • +

    +added TPL_DATAPEEK mode for tpl_peek +

    +
  • +
  • +

    +added support for NULL strings +

    +
  • +
  • +

    +added support for 16-bit integer types (j,v) +

    +
  • +
  • +

    +added tpl_jot +

    +
  • +
  • +

    +added support for fixed-length arrays of structures S(...)# +

    +
  • +
  • +

    +added support for pre-C99 compilers (thanks, Wei Wei!) +

    +
  • +
  • +

    +improved structure alignment calculation (thanks, Wu Yongwei!) +

    +
  • +
  • +

    +added RPM spec file (thanks, Alessandro Ren!) +

    +
  • +
  • +

    +compiles cleanly with -Wall and -pedantic and with -O3 +

    +
  • +
  • +

    +made BSD license terms even more permissive +

    +
  • +
  • +

    +test suite: exit with status zero when all tests pass +

    +
  • +
  • +

    +added PDF user guide +

    +
  • +
  • +

    +added update news +(RSS) + +

    +
  • +
  • +

    +added tpl wiki +

    +
  • +
+
+

Version 1.2 (2007-04-27)

+
+
    +
  • +

    +Perl API and XML converter support 64-bit types +

    +
  • +
+
+

Version 1.1 (2007-04-25)

+
+
    +
  • +

    +support for serializing C structures +

    +
  • +
  • +

    +support for serializing fixed-length arrays +

    +
  • +
  • +

    +MinGW support (thanks, Horea Haitonic!) +

    +
  • +
  • +

    +revised User Guide +

    +
  • +
+
+

Version 1.0 (2006-09-28)

+
+
    +
  • +

    +Initial version +

    +
  • +
+
+
+

+ + + diff --git a/doc/html/img/banner.png b/doc/html/img/banner.png new file mode 100644 index 0000000..ba9ffe8 Binary files /dev/null and b/doc/html/img/banner.png differ diff --git a/doc/html/img/banner.svg b/doc/html/img/banner.svg new file mode 100644 index 0000000..f9f1f85 --- /dev/null +++ b/doc/html/img/banner.svg @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + tpl + easily store and retrieve binary data in C + + diff --git a/doc/html/img/grad_azure.png b/doc/html/img/grad_azure.png new file mode 100644 index 0000000..bf5c0cf Binary files /dev/null and b/doc/html/img/grad_azure.png differ diff --git a/doc/html/img/grad_azure.svg b/doc/html/img/grad_azure.svg new file mode 100644 index 0000000..ab5fc49 --- /dev/null +++ b/doc/html/img/grad_azure.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/doc/html/img/rss.png b/doc/html/img/rss.png new file mode 100755 index 0000000..b3c949d Binary files /dev/null and b/doc/html/img/rss.png differ diff --git a/doc/html/img/tpl-mini.png b/doc/html/img/tpl-mini.png new file mode 100644 index 0000000..7c193e9 Binary files /dev/null and b/doc/html/img/tpl-mini.png differ diff --git a/doc/html/img/tpl-mini.svg b/doc/html/img/tpl-mini.svg new file mode 100644 index 0000000..07df268 --- /dev/null +++ b/doc/html/img/tpl-mini.svg @@ -0,0 +1,242 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + tpl + + diff --git a/doc/html/img/tpl.dia b/doc/html/img/tpl.dia new file mode 100644 index 0000000..d63ed77 Binary files /dev/null and b/doc/html/img/tpl.dia differ diff --git a/doc/html/img/tpl.png b/doc/html/img/tpl.png new file mode 100644 index 0000000..59f775c Binary files /dev/null and b/doc/html/img/tpl.png differ diff --git a/doc/html/img/tpl_aai.dia b/doc/html/img/tpl_aai.dia new file mode 100644 index 0000000..464ccbc Binary files /dev/null and b/doc/html/img/tpl_aai.dia differ diff --git a/doc/html/index.html b/doc/html/index.html new file mode 100644 index 0000000..ac74a00 --- /dev/null +++ b/doc/html/index.html @@ -0,0 +1,162 @@ + + + + + tpl home page + + + + + +
+ sf.net summary page > + tpl home +
+ +
+
+ + + +
+ +
+
Efficient serialization in C
+You can use tpl to store and reload your C data quickly and easily. +Tpl works with files, memory buffers and file descriptors so it's +suitable for use as a file format, IPC message format or any scenario +where you need to store and retrieve your data. +
+ +
+
Express your data
+Just express the type of data you are working with as a tpl format string. For +example, if you have a list of numeric ids and corresponding usernames, your +format string is A(is). Map your C variables to the format string and +then pack or unpack data. The format string lets you focus on your data, +rather than the storage format. +
+ +
+ + + + + + + + + +
+Storing ids and usernames + +Reloading ids and usernames +
+
+
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+    tpl_node *tn;
+    int id=0;
+    char *name, *names[] = { "joe", "bob", "cary" };
+
+    tn = tpl_map("A(is)", &id, &name);
+
+    for(name=names[0]; id < 3; name=names[++id]) {
+        tpl_pack(tn,1);
+    }
+
+    tpl_dump(tn, TPL_FILE, "users.tpl");
+    tpl_free(tn);
+}
+
+
+
+
+
+#include "tpl.h"
+
+int main(int argc, char *argv[]) {
+    tpl_node *tn;
+    int id;
+    char *name;
+
+    tn = tpl_map("A(is)", &id, &name);
+    tpl_load(tn, TPL_FILE, "users.tpl");
+
+    while ( tpl_unpack(tn,1) > 0 ) {
+        printf("id %d, user %s\n", id, name);
+        free(name);
+    }
+    tpl_free(tn);
+}
+
+
+
+
+ +
+
No library dependencies
+Tpl does not make your software dependent on any libraries. You can compile its +source code (one file) right into your program. +
+ +
For more information
+For a more thorough explanation and more examples, please read the +User Guide. + +
+
+ +
+ + + + + diff --git a/doc/html/license.html b/doc/html/license.html new file mode 100644 index 0000000..43aa2e9 --- /dev/null +++ b/doc/html/license.html @@ -0,0 +1,61 @@ + + + + + tpl home page + + + + + +
+ sf.net summary page > + tpl home > + BSD license +
+ +
+
+ + +
+ +
+Copyright (c) 2005-2010, Troy D. Hanson    http://tpl.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+ +
+ + + + + diff --git a/doc/html/perl.html b/doc/html/perl.html new file mode 100644 index 0000000..ccfb48c --- /dev/null +++ b/doc/html/perl.html @@ -0,0 +1,412 @@ + + + + + +tpl Perl API + + + + + + + +
+
+
+SourceForge.net +
+ sf.net summary page > + tpl home > + tpl Perl API + [View PDF] +
+
+
+

Perl API

+
+
+ +

The Perl API for reading and writing tpl is nearly identical to the C API. This +document will briefly explain the Perl API and provide examples. The chief +motivation for having a Perl API is to communicate with C programs that use tpl.

+
+ + + +
+
Tip
+
+
Start with the C API
This document assumes familiarity with the C API. The concepts of using tpl +are not explained here. For an introduction to tpl and its C API, see the +User Guide.
+
+

Tpl.pm

+

The Tpl.pm file (in the lang/perl) directory contains the Perl module. You +can copy it to another directory if you wish. Your Perl program may need to +include a use lib statement to find the module.

+
+
+
#!/usr/bin/perl
+use lib "/some/directory";
+use Tpl;
+
+

tpl_map

+

This function resembles the C version, except that it’s invoked via the Tpl +module, and it takes references to Perl variables after the format string.

+
+
+
my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+
+

The return value is a tpl object; all other API calls are object methods. +Incidentally, there is no tpl_free() method corresponding to the C API.

+

Fixed-length arrays

+

Format strings such as i# denote a fixed-length array. In the Perl API, +fixed-length arrays require two arguments: a list reference, and the fixed +length. For example:

+
+
+
my @x;
+my $tpl = Tpl->tpl_map("i#", \@x, 10);
+
+

When fixed-length arrays are packed or unpacked, the specified number of +elements will be copied from (or placed into) the designated list.

+

Structures

+

Format strings containing S(...) are handled in the Perl API as if only the +interior, parenthesized part was present. (It does not work like the C API). So +simply ignore the S(...) and consider only its interior format characters when +constructing the argument list:

+
+
+
my ($str, $int);
+my $tpl = Tpl->tpl_map("S(si)", \$str, \$int);
+
+

It really only makes sense to use S(...) in a format string in the Perl API if +you are communicating with a C program that uses structures.

+

tpl_pack

+

This is nearly identical to the C version. The only argument is the index +number to pack.

+
+
+
$tpl->tpl_pack(1);
+
+

tpl_dump

+

This method is a little different than the C version. Given no arguments, it +returns the tpl image; given one argument it writes a file with that name.

+
+
+
$tpl->tpl_dump("demo.tpl");   # writes demo.tpl
+
+

Or,

+
+
+
my $img = $tpl->tpl_dump();
+
+

The tpl image is a binary buffer. You can do whatever you want with it, such as +write it to a socket or pipe (probably to C program listening on the other end), +or save it somewhere and later re-load it using tpl_load().

+

tpl_load

+

This method loads a tpl image from a file or from a Perl variable. It takes +one argument. If it’s not a reference, it’s assumed to be a filename to load.

+
+
+
$tpl->tpl_load("demo.tpl");
+
+

Otherwise, if the argument is a Perl reference, it’s construed as a variable +containing the tpl image:

+
+
+
$tpl->tpl_load(\$img);
+
+

The method will die if the image is invalid or the file doesn’t exist. You +can wrap it with eval to catch such errors:

+
+
+
eval { $tpl->tpl_load(\$img); };
+print "failed to load\n" if $@;
+
+

tpl_unpack

+

This is nearly identical to the C version. The only argument is the index +number to unpack.

+
+
+
$tpl->tpl_unpack(1);
+
+
+

Examples

+
+

Integer array

+
+
Packing A(i) to file
+
+
#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Tpl;
+
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+for($i=0; $i<10; $i++) {
+    $tpl->tpl_pack(1);
+}
+$tpl->tpl_dump("demo.tpl");
+
+
+
Unpacking A(i) from file
+
+
#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Tpl;
+
+my $j;
+my $tpl2 = Tpl->tpl_map("A(i)",\$j);
+$tpl2->tpl_load("demo.tpl");
+while($tpl2->tpl_unpack(1) > 0) {
+    print "$j\n";
+}
+
+

Message-passing

+

While the bulk of this example is socket handling, it demonstrates how you can +use tpl as a message-passing format. In the real-world, you might have a C +server and a Perl client, for example. In this example, we’ll code both a client +and a server in Perl.

+
+
+

Server

+

The server waits for a connection from a client. When it gets one, it accepts +the connection and immediately forks a child process to handle it. Then it goes +back to waiting for another new connection.

+

The server child process handles the client by loading and unpacking the tpl +image sent by the client (containing an array of integers). It calculates their +sum and constructs a new tpl image containing the sum, which it sends back to +the client.

+
+
Server
+
+
#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use Tpl;
+
+$SIG{CHLD} = "IGNORE"; # don't create zombies
+
+our $port = 2000;
+
+sub handle_client {
+    my $client = shift;
+
+    undef $/;
+    my $request = <$client>; # get request (slurp)
+
+    # read input array, and calculate total
+    my ($i,$total);
+    my $tpl = Tpl->tpl_map("A(i)", \$i);
+    eval { $tpl->tpl_load(\$request); };
+    die "received invalid tpl" if $@;
+    $total += $i while $tpl->tpl_unpack(1) > 0;
+
+    # formulate response and send
+    my $tpl2 = Tpl->tpl_map("i", \$total);
+    $tpl2->tpl_pack(0);
+    my $response = $tpl2->tpl_dump();
+    print $client $response;
+    close $client;
+}
+
+my $server = IO::Socket::INET->new(LocalPort => $port,
+                   Type => SOCK_STREAM,
+                   Reuse => 1,
+                   Listen => 10 )
+        or die "Can't listen on port $port: $!\n";
+
+while (1) {
+    my $client = $server->accept();
+    next unless $client;
+    # new connection
+    my $pid = fork;
+    die "can't fork: $!\n" unless defined $pid;
+    if ($pid > 0) {
+        # parent
+        close $client;
+    } elsif ($pid == 0) {
+        # child
+        handle_client($client);
+        exit(0);
+    }
+}
+close ($server);
+
+

Client

+

The client is a simpler program. It constructs the tpl image containing the +integer array (taken from its command-line arguments), connects to the server +and sends the tpl image to it, and then awaits the response tpl. The response +containing the sum is loaded, unpacked and printed.

+
+
Client
+
+
#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use IO::Socket::INET;
+use Tpl;
+
+our $port = 2000;
+
+# construct tpl
+my $i;
+my $tpl = Tpl->tpl_map("A(i)",\$i);
+$tpl->tpl_pack(1) while ($i=shift @ARGV);
+my $request = $tpl->tpl_dump();
+
+# send to server, get response
+my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect";
+print $socket $request;
+shutdown($socket,1);        # done writing (half-close)
+undef $/;
+my $response = <$socket>;       # get reply (slurp)
+
+# decode response (or print error)
+my $total;
+my $tpl2 = Tpl->tpl_map("i", \$total);
+eval { $tpl2->tpl_load(\$response); };
+die "invalid response\n" if $@;
+$tpl2->tpl_unpack(0);
+print "total is $total\n";
+
+

Running thise example

+

If the client and server programs are in client.pl and server.pl, then +you can run the example by starting the server in one window:

+
+
+
./server.pl
+
+

Then run the client in another window. E.g.,

+
+
+
./client.pl 1 2 3 4 5
+
+

The client runs and then exits, printing:

+
+
+
total is 15
+
+

You can re-run the client with different arguments. When done, type Ctrl-C in +the server window to terminate it.

+
+
+

+ + + diff --git a/doc/html/styles.css b/doc/html/styles.css new file mode 100644 index 0000000..c23156e --- /dev/null +++ b/doc/html/styles.css @@ -0,0 +1,160 @@ +#banner { + /* font-size: x-large; */ + /* background: #ff00ff; */ + /* height: 100px; */ +} + +#topnav { + /* background-image: url(img/grad_topnav.png); */ + /* background-repeat: repeat-y; */ + /* background-color: #af00af; */ + /* height: 25px; */ + margin: 10px 0px 10px 20px; + padding: 3px; + font-size: 9pt; + font-family: sans-serif; + /* border-style: solid; */ + /* border-width: 1px; */ +} + + +#topnav {font-weight: bold} +#topnav a {font-weight: normal} + +h1,p { margin: 0; } /* non-0 margin on firefox */ + +#mid { + background-image: url(img/grad_azure.png); + background-repeat: repeat-y; + /* background-color: #ffddaa; */ + padding-top: 20px; + margin-bottom: 10px; +} + +#mid img { + padding-left: 10px; + vertical-align: middle; +} + +a img { + border: 0 +} + +#nav { + background-color: #fff8f1; + margin-left: 10px; + margin-top: 20px; + float: left; + padding: 10px; + border-style: solid; + border-width: 2px; + font-family: sans-serif; +} + + +#nav h2 { + font-weight: bold; + font-size: 10pt; +} + +#nav h3 { + /* font-weight: bold; */ + padding-left: 5px; + /* font-style: oblique; */ + font-family: sans-serif; + font-size: 7pt; +} + +#nav div { + font-size: 9pt; + padding-left: 15px; +} + +#main { + background: #ffffff; + margin-top: 20px; + margin-left: 170px; + padding-left: 20px; + height: 100%; + /* font-family: sans-serif; */ +} + +#main h1 { + font-family: sans-serif; +} + + +.listing { + margin: 20px; + font-family: sans-serif; + font-weight: bold; +} + +.code { + padding: 10px; + background: #f3f3f3; + font-size: 8pt; + font-weight: normal; + width: 80%; + border-style: solid; + border-width: 1px; +} + +.code pre { + padding-left: 20px; + padding-right: 80px; +} + +#formatstrings { + margin: 20px; +} + +#footer { + /* background: #00ffff; */ + margin-top: 5px; + font-size: small; + font-family: sans-serif; +} + +em { + font-weight: bold; +} + +hr { + height: 0.04em; + background: black; + margin: 0 10% 0 0; +} + +#footer img { + margin-right: 5px; + float: right; +} + +.lead { + font-family: sans-serif; + font-size: larger; + font-weight: bold; + /* font-style: oblique; */ + margin: 30px 30px 30px 0px; + color: #1122dd; +} + +ol { + font-family: monospace; + background: #dddddd; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + border-width: 1px; + border-style: solid; + /* font-size: smaller; */ +} + +#main #portrait { + float: right; + font-size: smaller; + font-family: sans-serif; + text-align: center; + margin: 10px; +} diff --git a/doc/html/tdh-quirks.css b/doc/html/tdh-quirks.css new file mode 100644 index 0000000..a39a6d6 --- /dev/null +++ b/doc/html/tdh-quirks.css @@ -0,0 +1,41 @@ +/* Workarounds for IE6's broken and incomplete CSS2. */ + +div.sidebar-content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} +div.sidebar-title, div.image-title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + margin-top: 0.0em; + margin-bottom: 0.5em; +} + +div.listingblock div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock-attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock-content { + white-space: pre; +} +div.verseblock-attribution { + padding-top: 0.75em; + text-align: left; +} + +div.exampleblock-content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +/* IE6 sets dynamically generated links as visited. */ +div#toc a:visited { color: blue; } diff --git a/doc/html/tdh.css b/doc/html/tdh.css new file mode 100644 index 0000000..4f927f3 --- /dev/null +++ b/doc/html/tdh.css @@ -0,0 +1,402 @@ +/* Debug borders */ +p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 { +/* + border: 1px solid red; +*/ +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +tt { + color: navy; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + font-family: sans-serif; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} + +div.sectionbody { + font-family: serif; + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +span#author { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + font-size: 1.1em; +} +span#email { +} +span#revnumber, span#revdate, span#revremark { + font-family: sans-serif; +} + +div#footer { + font-family: sans-serif; + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +div#footer-text { + float: left; + padding-bottom: 0.5em; +} +div#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +div#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + color: #606060; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #dddddd; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > div.content { + white-space: pre; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #527bbd; +} +thead, p.table.header { + font-family: sans-serif; + font-weight: bold; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + + +@media print { + div#footer-badges { display: none; } +} + +div#toc { + margin-bottom: 2.5em; +} + +div#toctitle { + color: #527bbd; + font-family: sans-serif; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} +#toc { + float: right; + font-family: sans-serif; + border: 1px solid #000; + margin: 0px 0px 20px 20px; + padding: 0px; + background: #f0f0f0; + font-size: 80%; +} + +#toc #hdr { + color:#ffffff; + background:#98b1c4; + text-align:center; + margin-bottom:5px; +} + +a img { + border: 0 +} + +#toc a:visited, #toc a:link { color:#000; text-decoration: none } +#toc a:hover { color:#00f; text-decoration: underline; } + +#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; } +#toc .level3 { margin-left: 2em; font-size: 0.8em } + +.toplink { + float: right; + font-size: 50%; + cursor: pointer; +} + +#topnav {font-weight: bold} +#topnav a {font-weight: normal} diff --git a/doc/html/toc.css b/doc/html/toc.css new file mode 100644 index 0000000..ae3363e --- /dev/null +++ b/doc/html/toc.css @@ -0,0 +1,35 @@ +#toc { + float: right; + font-family: sans-serif; + border: 1px solid #000; + margin: 0px 0px 20px 20px; + padding: 0px; + background: #f0f0f0; + font-size: 80%; +} + +#toc #hdr { + color:#ffffff; + background:#98b1c4; + text-align:center; + margin-bottom:5px; +} + +a img { + border: 0 +} + +#toc a:visited, #toc a:link { color:#000; text-decoration: none } +#toc a:hover { color:#00f; text-decoration: underline; } + +#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; } +#toc .level3 { margin-left: 2em; font-size: 0.8em } + +.toplink { + float: right; + font-size: 50%; + cursor: pointer; +} + +#topnav {font-weight: bold} +#topnav a {font-weight: normal} diff --git a/doc/html/userguide.html b/doc/html/userguide.html new file mode 100644 index 0000000..6cd2c0b --- /dev/null +++ b/doc/html/userguide.html @@ -0,0 +1,1548 @@ + + + + + +tpl User Guide + + + + + + + +
+
+
+SourceForge.net +
+ sf.net summary page > + tpl home > + tpl User Guide + [View PDF] +
+
+
+

Overview

+
+
+ +

Serialization in C

+

Tpl is a library for serializing C data. The data is stored in its natural +binary form. The API is small and tries to stay "out of the way". +Tpl can serialize many C data types, including structures.

+

Uses for tpl

+

Tpl makes a convenient file format. For example, suppose a program needs to +store a list of user names and ids. This can be expressed using the format +string A(si). If the program needs two such lists (say, one for regular +users and one for administrators) this could be expressed as A(si)A(si). It +is easy to read and write this kind of structured data using tpl.

+

Tpl can also be used as an IPC message format. It handles byte order issues +and deframing individual messages off of a stream automatically.

+

Expressing type

+

The "data type" of a tpl is explicitly stated as a format string. There is +never any ambiguity about the type of data stored in a tpl. Some examples:

+
    +
  • +

    +A(is) is a variable-length array of integer-string pairs +

    +
  • +
  • +

    +A(is)A(is) are two such arrays, completely independent of one another +

    +
  • +
  • +

    +S(ci) is a structure containing a char and integer +

    +
  • +
  • +

    +S(ci)# is a fixed-length array of the latter structure +

    +
  • +
  • +

    +A(A(i)) is a nested array, that is, an array of integer arrays +

    +
  • +
+

The tpl image

+

A tpl image is the serialized form of a tpl, stored in a memory buffer or file, +or written to a file descriptor.

+

What’s in a tpl image?

+

There is no need to understand the internal structure of the tpl image. But for the +curious, the image is a strictly defined binary buffer having two sections, +a header and the data. The header encodes the length of the image, its +format string, endian order and other flags. The data section contains the +packed data.

+

No framing needed

+

A property of the tpl image is that consecutive images can be written to a stream +without requiring any delimiter between them. The reader making use of +tpl_gather (or tpl_load in TPL_FD mode) will obtain exactly one tpl image at +a time. Therefore tpl images can be used as an IPC message format without any +higher-level framing protocol.

+

Data portability

+

A tpl image generated on one kind of CPU will generally be portable to other +CPU types when tpl is used properly. This may be a surprise considering that +tpl is a binary format. But tpl has been carefully designed to make this work. +Each format character has an associated explicitly-sized type. For +integer and floating point types, whose "endian" or byte-order convention varies +from one CPU to another, tpl automatically and transparently corrects the +endian order (if needed) during the unpacking process. Floating point numbers +present their own special difficulties. No guarantees +are made with regard to floating point portability. That said, because many +modern CPU’s use IEEE 754 floating point representation, data is likely to be +portable among them.

+

XML and Perl

+

Note: The tplxml utility and the Perl module are currently unsupported in tpl 1.5.

+

XML

+

While a tpl image is a binary entity, you can view any tpl image in XML format +using the included tplxml utility, located in the lang/perl directory.

+
+
+
tplxml file.tpl > file.xml
+tplxml file.xml > file.tpl
+
+

The utility is bidirectional, as shown. The file extension is not important; +tplxml inspects its input to see if it’s tpl or XML. You can also pipe data +into it instead of giving it a filename. The tplxml utility is slow. Its +purpose is two-fold: debugging (manual inspection of the data in a tpl), and +interoperability with XML-based programs. The resulting XML is often ten times +the size of the original binary tpl image.

+

Perl

+

There is a Perl module in lang/perl/Tpl.pm. The Perl API +is convenient for writing Perl scripts that interoperate with C programs, and +need to pass structured data back and forth. It is written in pure Perl.

+

Platforms

+

The tpl software was developed for POSIX systems and has been tested on 32- and 64-bit +platforms including:

+
    +
  • +

    +Linux +

    +
  • +
  • +

    +Solaris +

    +
  • +
  • +

    +Mac OS X +

    +
  • +
  • +

    +OpenBSD +

    +
  • +
  • +

    +Windows using Visual Studio 2008 or 2010, or Cygwin or MinGW +

    +
  • +
+

BSD licensed

+

This software is made available under the +revised BSD license. +It is free and open source.

+

Download

+

Please follow the link to download on the +tpl website.

+

Getting help

+

If you need help, you are welcome to email the author at +thanson@users.sourceforge.net.

+

Resources

+
+
+News +
+
+

+ The author has a news feed for software updates +(RSS) +. +

+
+
+
+

Build and install

+
+

Tpl has no dependencies on libraries other than the system C library. You +can simply copy the tpl source into your project, so you have no dependencies. +Alternatively, you can build tpl as a library and link it to your program.

+

As source

+

The simplest way to use tpl is to copy the source files tpl.h and tpl.c +(from the src/ directory) right into your project, and build them with the +rest of your source files. No special compiler flags are required.

+

As a library

+

Alternatively, to build tpl as a library, from the top-level directory, run:

+
+
+
./configure
+make
+make install
+
+

This installs a static library libtpl.a and a shared library (e.g., +libtpl.so), if your system supports them, in standard places. The installation +directory can be customized using ./configure --prefix=/some/directory. Run +configure --help for further options.

+

Test suite

+

You can compile and run the built-in test suite by running:

+
+
+
cd tests/
+make
+
+

On Windows

+

DLL

+

On the tpl home page, a Visual Studio 2008 solution package is available for +download. This zip file contains pre-built 32- and 64-bit versions of tpl as a +DLL. If you like, you can build the DLL yourself using VS2008 or VS2010 (the +free Express Edition is sufficient) by opening the solution file and choosing +Build Solution.

+

Non-DLL usage

+

Alternatively, tpl can be used directly (instead of as a DLL) by compiling +the tpl sources right into your program. To do this, add tpl.c, tpl.h, +win/mman.h and win/mmap.c to your program’s source and header files and +add the preprocessor definition TPL_NOLIB.

+

MinGW/Cygwin

+

Prior to tpl release 1.5, using tpl on Windows required building it with MinGW +or Cygwin. This is no longer necessary. If you want to build it that way anyway, +use the non-Windows (i.e. tar.bz2) tpl download and follow the "configure; make; +make install" approach.

+
+

API concepts

+
+

To use tpl, you need to know the order in which to call the API functions, and +the background concepts of format string, arrays and index numbers.

+

Order of functions

+

Creating a tpl is always the first step, and freeing it is the last step. In +between, you either pack and dump the tpl (if you’re serializing data) or you +load a tpl image and unpack it (if you’re deserializing data).

+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Order of usage
Step If you’re serializing… If you’re deserializing…

1.

tpl_map()

tpl_map()

2.

tpl_pack()

tpl_load()

3.

tpl_dump()

tpl_unpack()

4.

tpl_free()

tpl_free()

+
+

Format string

+

When a tpl is created using tpl_map(), its data type is expressed as a format +string. Each character in the format string has an associated argument of a +specific type. For example, this is how a format string and its arguments are +passed in to tpl_map:

+
+
+
tpl_node *tn;
+char c;
+int i[10];
+tn = tpl_map("ci#", &c, i, 10);  /* ci# is our format string */
+
+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 2. Supported format characters
Type Description Required argument type

j

16-bit signed int

int16_t* or equivalent

v

16-bit unsigned int

uint16_t* or equivalent

i

32-bit signed int

int32_t* or equivalent

u

32-bit unsigned int

uint32_t* or equivalent

I

64-bit signed int

int64_t* or equivalent

U

64-bit unsigned int

uint64_t* or equivalent

c

character (byte)

char*

s

string

char**

f

64-bit double precision float

double* (varies by platform)

#

array length; modifies preceding iujvIUcsf or S(...)

int

B

binary buffer (arbitrary-length)

tpl_bin*

S

structure (…)

struct *

$

nested structure (…)

none

A

array (…)

none

+
+

Explicit sizes

+

The sizes of data types such as long and double vary by platform. This must +be kept in mind because most tpl format characters require a pointer argument to +a specific-sized type, listed above. You can use explicit-sized types such as +int32_t (defined in inttypes.h) in your program if you find this helpful.

+
The trouble with double
+

Unfortunately there are no standard explicit-sized floating-point types-- no +float64_t, for example. If you plan to serialize double on your platform +using tpl’s f format character, first be sure that your double is 64 bits. +Second, if you plan to deserialize it on a different kind of CPU, be sure that +both CPU’s use the same floating-point representation such as IEEE 754.

+

Arrays

+

Arrays come in two kinds: fixed-length and variable-length arrays. +Intuitively, they can be thought of like conventional C arrays and linked lists. +In general, use fixed-length arrays if possible, and variable-length arrays +if necessary. The variable-length arrays support more complex data types, and +give or receive the elements to your program one by one.

+

Fixed-length vs. Variable-length arrays

+
+
+Notation +
+
+

+ Fixed-length arrays are denoted like i# (a simple type followed by one or + more # signs), but variable-length arrays are denoted like A(i). +

+
+
+Element handling +
+
+

+ All the elements of a fixed-length array are packed or unpacked at once. But + the elements of a variable-length array are packed or unpacked one by one. +

+
+
+Array length +
+
+

+ The number of elements in a fixed-length array is specified before use-- + before any data is packed. But variable-length arrays do not have a fixed + element count. They can have any number of elements packed into them. When + unpacking a variable-length array, they are unpacked one by one until they + are exhausted. +

+
+
+Element types +
+
+

+ Elements of fixed-length arrays can be the integer, byte, double, string + types or structures. (This excludes format characters BA). Fixed-length + arrays can also be multi-dimensional like i##. Variable-length arrays can + have simple or complex elements-- for example, an array of ints A(i), an + array of int/double pairs A(if), or even nested arrays like A(A(if)). +

+
+
+

Before explaining all the concepts, it’s illustrative to see how both kinds of +arrays are used. Let’s pack the integers 0 through 9 both ways.

+
+
Packing 0-9 as a fixed-length array
+
+
#include "tpl.h"
+int main() {
+    tpl_node *tn;
+    int x[] = {0,1,2,3,4,5,6,7,8,9};
+
+    tn = tpl_map("i#", x, 10);
+    tpl_pack(tn,0);                         /* pack all 10 elements at once */
+    tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl");
+    tpl_free(tn);
+}
+
+

Note that the length of the fixed-length array (10) was passed as an argument to +tpl_map(). The corresponding unpacking example is listed +further below. Now let’s see how we would pack 0-9 as a variable-length array:

+
+
Packing 0-9 as a variable-length array
+
+
#include "tpl.h"
+int main() {
+    tpl_node *tn;
+    int x;
+
+    tn = tpl_map("A(i)", &x);
+    for(x = 0; x < 10; x++) tpl_pack(tn,1);  /* pack one element at a time */
+    tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl");
+    tpl_free(tn);
+}
+
+

Notice how we called tpl_pack in a loop, once for each element 0-9. Again, +there is a corresponding unpacking example shown later in the +guide. You might also notice that this time, we passed 1 as the final argument +to tpl_pack. This is an index number designating which variable-length array +we’re packing. In this case, there is only one.

+

Index numbers

+

Index numbers identify a particular variable-length array in the format string. +Each A(...) in a format string has its own index number. The index numbers +are assigned left-to-right starting from 1. Examples:

+
+
+
A(i)        /* index number 1 */
+A(i)A(i)    /* index numbers 1 and 2 */
+A(A(i))     /* index numbers 1 and 2 (order is independent of nesting) */
+
+
Special index number 0
+

The special index number 0 designates all the format characters that are not +inside an A(...). Examples of what index 0 does (and does not) designate:

+
+
+
S(ius)      /* index 0 designates the whole thing */
+iA(c)u      /* index 0 designates the i and the u */
+c#A(i)S(ci) /* index 0 designates the c# and the S(ci) */
+
+

An index number is passed to tpl_pack and tpl_unpack to specify which +variable-length array (or non-array, in the case of index number 0) to act upon.

+

Integers

+

The array examples above demonstrated how integers could be +packed. We’ll show some further examples here of unpacking integers and dealing +with multi-dimensional arrays. The same program could be used to demonstrate +working with byte, 16-bit shorts, 32-bit or 64-bit signed and unsigned integers +with only a change to the data type and the format character.

+
+
Unpacking 0-9 from a fixed-length array
+
+
#include "tpl.h"
+int main() {
+    tpl_node *tn;
+    int x[10];
+
+    tn = tpl_map("i#", x, 10);
+    tpl_load(tn, TPL_FILE, "/tmp/fixed.tpl");
+    tpl_unpack(tn,0);  /* unpack all 10 elements at once */
+    tpl_free(tn);
+    /* now do something with x[0]...x[9].. (not shown */
+}
+
+

For completeness, let’s also see how to unpack a variable-length integer array.

+
+
Unpacking 0-9 from a variable-length array
+
+
#include "tpl.h"
+int main() {
+    tpl_node *tn;
+    int x;
+
+    tn = tpl_map("A(i)", &x);
+    tpl_load(tn, TPL_FILE, "/tmp/variable.tpl");
+    while (tpl_unpack(tn,1) > 0) printf("%d\n",x); /* unpack one by one */
+    tpl_free(tn);
+}
+
+

Multi-dimensional arrays

+

A multi-dimensional matrix of integers can be packed and unpacked the same way +as any fixed-length array.

+
+
+
int xy[XDIM][YDIM];
+...
+tn = tpl_map("i##", xy, XDIM, YDIM);
+tpl_pack(tn, 0);
+
+

This single call to tpl_pack packs the entire matrix.

+

Strings

+

Tpl can serialize C strings. A different format is used for char* vs. char[ ] +as described below. Let’s look at char* first:

+
+
Packing a string
+
+
    #include "tpl.h"
+
+    int main() {
+        tpl_node *tn;
+        char *s = "hello, world!";
+        tn = tpl_map("s", &s);
+        tpl_pack(tn,0);  /* copies "hello, world!" into the tpl */
+        tpl_dump(tn,TPL_FILE,"string.tpl");
+        tpl_free(tn);
+    }
+
+

The char* must point to a null-terminated string or be a NULL pointer.

+

When deserializing (unpacking) a C string, space for it will be allocated +automatically, but you are responsible for freeing it (unless it is NULL):

+
+
Unpacking a string
+
+
    #include "tpl.h"
+
+    int main() {
+        tpl_node *tn;
+        char *s;
+        tn = tpl_map("s", &s);
+        tpl_load(tn,TPL_FILE,"string.tpl");
+        tpl_unpack(tn,0);   /* allocates space, points s to "hello, world!" */
+        printf("unpacked %s\n", s);
+        free(s);            /* our responsibility to free s */
+        tpl_free(tn);
+    }
+
+

char* vs char[ ]

+

The s format character is only for use with char* types. In the example +above, s is a char*. If it had been a char s[14], we would use the format +characters c# to pack or unpack it, as a fixed-length character array. (This +unpacks the characters "in-place", instead of into a dynamically allocated +buffer). Also, a fixed-length buffer described by c# need not be +null-terminated.

+

Arrays of strings

+

You can use fixed- or variable-length arrays of strings in tpl. An example of +packing a fixed-length two-dimensional array of strings is shown here.

+
+
+
char *labels[2][3] = { {"one", "two", "three"},
+                       {"eins", "zwei", "drei" } };
+tpl_node *tn;
+tn = tpl_map("s##", labels, 2, 3);
+tpl_pack(tn,0);
+tpl_dump(tn,TPL_FILE,filename);
+tpl_free(tn);
+
+

Later, when unpacking these strings, the programmer must remember to free them +one by one, after they are no longer needed.

+
+
+
char *olabels[2][3];
+int i,j;
+
+
+
+
tn = tpl_map("s##", olabels, 2, 3);
+tpl_load(tn,TPL_FILE,filename);
+tpl_unpack(tn,0);
+tpl_free(tn);
+
+
+
+
for(i=0;i<2;i++) {
+  for(j=0;j<3;j++) {
+    printf("%s\n", olabels[i][j]);
+    free(olabels[i][j]);
+  }
+}
+
+

Binary buffers

+

Packing an arbitrary-length binary buffer (tpl format character B) makes use +of the tpl_bin structure. You must declare this structure and populate it +with the address and length of the binary buffer to be packed.

+
+
Packing a binary buffer
+
+
    #include "tpl.h"
+    #include <sys/time.h>
+
+    int main() {
+        tpl_node *tn;
+        tpl_bin tb;
+
+        /* we'll use a timeval as our guinea pig */
+        struct timeval tv;
+        gettimeofday(&tv,NULL);
+
+        tn = tpl_map( "B", &tb );
+        tb.sz = sizeof(struct timeval);  /* size of buffer to pack */
+        tb.addr = &tv;                   /* address of buffer to pack */
+        tpl_pack( tn, 0 );
+        tpl_dump(tn, TPL_FILE, "bin.tpl");
+        tpl_free(tn);
+    }
+
+

When you unpack a binary buffer, tpl will automatically allocate it, and will +populate your tpl_bin structure with its address and length. You are +responsible for eventually freeing the buffer.

+
+
Unpacking a binary buffer
+
+
    #include "tpl.h"
+
+    int main() {
+        tpl_node *tn;
+        tpl_bin tb;
+
+        tn = tpl_map( "B", &tb );
+        tpl_load( tn, TPL_FILE, "bin.tpl" );
+        tpl_unpack( tn, 0 );
+        tpl_free(tn);
+
+        printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr);
+        free(tb.addr);  /* our responsibility to free it */
+    }
+
+

Structures

+

You can use tpl to pack and unpack structures, and arrays of structures.

+
+
+
struct ci {
+    char c;
+    int i;
+};
+struct ci s = {'a', 1};
+
+
+
+
tn = tpl_map("S(ci)", &s);  /* pass structure address */
+tpl_pack(tn, 0);
+tpl_dump(tn, TPL_FILE, "struct.tpl");
+tpl_free(tn);
+
+

As shown, omit the individual arguments for the format characters inside the +parenthesis. The exception is for fixed-length arrays; when S(...) contains a +# character, its length argument is required: tpl_map("S(f#i)", &s, 10);

+

When using the S(...) format, the only characters allowed inside the +parentheses are iujvcsfIU#$().

+

Structure arrays

+

Arrays of structures are the same as simple arrays. Fixed- or variable- length +arrays are supported.

+
+
+
struct ci sa[100], one;
+
+
+
+
tn = tpl_map("S(ci)#", sa, 100);  /* fixed-length array of 100 structures */
+tn = tpl_map("A(S(ci))", &one);   /* variable-length array (one at a time)*/
+
+

The differences between fixed- and variable-length arrays are explained in the +Arrays section.

+

Nested structures

+

When dealing with nested structures, the outermost structure uses the S format +character, and the inner nested structures use the $ format. Only the +outermost structure’s address is given to tpl_map.

+
+
+
struct inner_t {
+  char a;
+}
+
+
+
+
struct outer_t {
+  char b;
+  struct inner_t i;
+}
+
+
+
+
tpl_node *tn;
+struct outer_t outer = {'b', {'a'}};
+
+
+
+
tn = tpl_map("S(c$(c))", &outer);
+
+

Structures can nest to any level. Currently tpl does not support fixed-length +array suffixes on inner structures. However the outermost structure can have a +length suffix even if it contains some nested structures.

+

Linked lists

+

While tpl has no specific data type for a linked list, the technique for +packing them is illustrated here. First describe your list element as a +format string and then surround it with A(...) to describe it as +variable-length array. Then, using a temporary variable, iterate over each +list element, copying it to the temporary variable and packing it.

+
+
+
struct element {
+  char c;
+  int i;
+  struct element *next;
+}
+
+
+
+
struct element *list, *i, tmp;
+tpl_node *tn;
+
+
+
+
/* add some elements to list.. (not shown)*/
+
+
+
+
tn = tpl_map("A(S(ci))", &tmp);
+for(i = list; i != NULL; i=i->next) {
+  tmp = *i;
+  tpl_pack(tn, 1);
+}
+tpl_dump(tn,TPL_FILE,"list.tpl");
+tpl_free(tn);
+
+

Unpacking is similar. The for loop is just replaced with:

+
+
+
while( tpl_unpack(tn,1) > 0) {
+  struct element *newelt = malloc(sizeof(struct element));
+  *newelt = tmp;
+  add_to_list(list, newelt);
+}
+
+

As you can see, tpl does not reinstate the whole list at once-- just one +element at a time. You need to link the elements manually. A future release of +tpl may support pointer swizzling to make this easier.

+
+

API

+
+

tpl_map

+

The only way to create a tpl is to call tpl_map(). The first argument is the +format string. This is followed by a list of arguments as required by +the particular characters in the format string. E.g,

+
+
+
tpl_node *tn;
+int i;
+tn = tpl_map( "A(i)", &i );
+
+

The function creates a mapping between the items in the format string and the C +program variables whose addresses are given. Later, the C variables will be read +or written as the tpl is packed or unpacked.

+

This function returns a tpl_node* on success, or NULL on failure.

+

tpl_pack

+

The function tpl_pack() packs data into a tpl. The arguments to +tpl_pack() are a tpl_node* and an index number.

+
+
+
tn = tpl_map("A(i)A(c)", &i, &c);
+for(i=0; i<10; i++) tpl_pack(tn, 1);    /* pack 0-9 into index 1 */
+for(c='a'; c<='z'; c++) tpl_pack(tn, 2); /* pack a-z into index 2 */
+
+
+
+

Index number 0

+

It is necessary to pack index number 0 only if the format string contains +characters that are not inside an A(...), such as the i in the format string +iA(c).

+

Variable-length arrays

+
Adding elements to an array
+

To add elements to a variable-length array, call tpl_pack() repeatedly. Each +call adds another element to the array.

+
Zero-length arrays are ok
+

It’s perfectly acceptable to pack nothing into a variable-length array, +resulting in a zero-length array.

+
Packing nested arrays
+

In a format string containing a nested, variable-length array, such as +A(A(s)), the inner, child array should be packed prior to the parent array.

+

When you pack a parent array, a "snapshot" of the current child array is placed +into the parent’s new element. Packing a parent array also empties the child +array. This way, you can pack new data into the child, then pack the parent +again. This creates distinct parent elements which each contain distinct child +arrays.

+
+ + + +
+
Tip
+
When dealing with nested arrays like A(A(i)), pack them from the "inside +out" (child first), but unpack them from the "outside in" (parent first).
+
+

The example below creates a tpl having the format string A(A(c)).

+
+
Packing nested arrays
+
+
#include "tpl.h"
+
+int main() {
+    char c;
+    tpl_node *tn;
+
+    tn = tpl_map("A(A(c))", &c);
+
+    for(c='a'; c<'c'; c++) tpl_pack(tn,2);  /* pack child (twice) */
+    tpl_pack(tn, 1);                        /* pack parent */
+
+    for(c='1'; c<'4'; c++) tpl_pack(tn,2);  /* pack child (three times) */
+    tpl_pack(tn, 1);                        /* pack parent */
+
+    tpl_dump(tn, TPL_FILE, "test40.tpl");
+    tpl_free(tn);
+}
+
+

This creates a nested array in which the parent has two elements: the first +element is the two-element nested array a, b; and the second element is +the three-element nested array 1, 2, 3. +The nested unpacking example shows how this tpl is unpacked.

+

tpl_dump

+

After packing a tpl, tpl_dump() is used to write the tpl image to a file, +memory buffer or file descriptor. The corresponding modes are shown below. A +final mode is for querying the output size without actually performing the dump.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Write to… Usage

file

tpl_dump(tn, TPL_FILE, "file.tpl" );

file descriptor

tpl_dump(tn, TPL_FD, 2);

memory

tpl_dump(tn, TPL_MEM, &addr, &len );

caller’s memory

tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, sizeof(buf));

just get size

tpl_dump(tn, TPL_GETSIZE, &sz);

+
+

The first argument is the tpl_node* and the second is one of these constants:

+
+
+TPL_FILE +
+
+

+ Writes the tpl to a file whose name is given in the following argument. + The file is created with permissions 664 (rw-rw-r--) unless further + restricted by the process umask. +

+
+
+TPL_FD +
+
+

+ Writes the tpl to the file descriptor given in the following argument. + The descriptor can be either blocking or non-blocking, but will busy-loop + if non-blocking and the contents cannot be written immediately. +

+
+
+TPL_MEM +
+
+

+ Writes the tpl to a memory buffer. The following two arguments must be a + void\*\* and a size_t*. The function will allocate a buffer and store + its address and length into these locations. The caller is responsible to + free() the buffer when done using it. +

+
+
+TPL_MEM|TPL_PREALLOCD +
+
+

+ Writes the tpl to a memory buffer that the caller has already allocated or + declared. The following two arguments must be a void* and a size_t + specifying the buffer address and size respectively. (If the buffer is of + insufficient size to receive the tpl dump, the function will return -1). + This mode can be useful in conjunction with tpl_load in TPL_EXCESS_OK + mode, as shown here. +

+
+
+TPL_GETSIZE +
+
+

+ This special mode does not actually dump the tpl. Instead it places the size + that the dump would require into the size_t pointed to by the + following argument. +

+
+
+

The return value is 0 on success, or -1 on error.

+

The tpl_dump() function does not free the tpl. Use tpl_free() to release +the tpl’s resources when done.

+
+ + + +
+
Tip
+
+
Back-to-back tpl images require no delimiter
If you want to store a series of tpl images, or transmit sequential tpl images +over a socket (perhaps as messages to another program), you can simply dump them +sequentially without needing to add any delimiter for the individual tpl images. +Tpl images are internally delimited, so tpl_load will read just one at a time +even if multiple images are contiguous.
+
+

tpl_load

+

This API function reads a previously-dumped tpl image from a file, memory +buffer or file descriptor, and prepares it for subsequent unpacking. The format +string specified in the preceding call to tpl_map() will be cross-checked +for equality with the format string stored in the tpl image.

+
+
+
tn = tpl_map( "A(i)", &i );
+tpl_load( tn, TPL_FILE, "demo.tpl" );
+
+

The first argument to tpl_load() is the tpl_node*. The second argument is +one of the constants:

+
+
+TPL_FILE +
+
+

+ Loads the tpl from the file named in the following argument. It is also + possible to bitwise-OR this flag with TPL_EXCESS_OK as explained below. +

+
+
+TPL_MEM +
+
+

+ Loads the tpl from a memory buffer. The following two arguments must be a + void* and a size_t, specifying the buffer address and size, + respectively. The caller must not free the memory buffer until after + freeing the tpl with tpl_free(). (If the caller wishes to hand over + responsibility for freeing the memory buffer, so that it’s automatically + freed along with the tpl when tpl_free() is called, the constant + TPL_UFREE may be bitwise-OR’d with TPL_MEM to achieve this). + Furthermore, TPL_MEM may be bitwise-OR’d with TPL_EXCESS_OK, explained + below. +

+
+
+TPL_FD +
+
+

+ Loads the tpl from the file descriptor given in the following argument. + The descriptor is read until one complete tpl image is loaded; no bytes + past the end of the tpl image will be read. The descriptor can be either + blocking or non-blocking, but will busy-loop if non-blocking and the + contents cannot be read immediately. +

+
+
+

During loading, the tpl image will be extensively checked for internal validity.

+

This function returns 0 on success or -1 on error.

+

TPL_EXCESS_OK

+

When reading a tpl image from a file or memory (but not from a file descriptor) +the size of the file or memory buffer must exactly equal that of the tpl image +stored therein. In other words, no excess trailing data beyond the tpl image is +permitted. The bit flag TPL_EXCESS_OK can be OR’d with TPL_MEM or TPL_FILE +to relax this requirement.

+

A situation where this flag can be useful is in conjunction with tpl_dump in +the TPL_MEM|TPL_PREALLOCD mode. In this example, the program does not concern +itself with the actual tpl size as long as LEN is sufficiently large.

+
+
+
char buf[LEN];  /* will store and read tpl images here */
+...
+tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, LEN);
+...
+tpl_load(tn, TPL_MEM|TPL_EXCESS_OK, buf, LEN);
+
+

tpl_unpack

+

The tpl_unpack() function unpacks data from the tpl. When data is unpacked, +it is copied to the C program variables originally specified in tpl_map(). +The first argument to tpl_unpack is the tpl_node* for the tpl and the +second argument is an index number.

+
+
+
tn = tpl_map( "A(i)A(c)", &i, &c );
+tpl_load( tn, TPL_FILE, "nested.tpl" );
+while (tpl_unpack( tn, 1) > 0) printf("i is %d\n", i); /* unpack index 1 */
+while (tpl_unpack( tn, 2) > 0) printf("c is %c\n", c); /* unpack index 2 */
+
+

Index number 0

+

It is necessary to unpack index number 0 only if the format string contains +characters that are not inside an A(...), such as the i in the format string +iA(c).

+

Variable-length arrays

+
Unpacking elements from an array
+

For variable-length arrays, each call to tpl_unpack() unpacks another element. +The return value can be used to tell when you’re done: if it’s positive, an +element was unpacked; if it’s 0, nothing was unpacked because there are no more +elements. A negative retun value indicates an error (e.g. invalid index number). +In this document, we usually unpack variable-length arrays using a while loop:

+
+
+
while( tpl_unpack( tn, 1 ) > 0 ) {
+    /* got another element */
+}
+
+
Array length
+

When unpacking a variable-length array, it may be convenient to know ahead of +time how many elements will need to be unpacked. You can use tpl_Alen() to +get this number.

+
Unpacking nested arrays
+

In a format string containing a nested variable-length array such as A(A(s)), +unpack the outer, parent array before unpacking the child array.

+

When you unpack a parent array, it prepares the child array for unpacking. +After unpacking the elements of the child array, the program can repeat the +process by unpacking another parent element, then the child elements, and so on. +The example below unpacks a tpl having the format string A(A(c)).

+
+
Unpacking nested arrays
+
+
#include "tpl.h"
+#include <stdio.h>
+
+int main() {
+    char c;
+    tpl_node *tn;
+
+    tn = tpl_map("A(A(c))", &c);
+
+    tpl_load(tn, TPL_FILE, "test40.tpl");
+    while (tpl_unpack(tn,1) > 0) {
+        while (tpl_unpack(tn,2) > 0) printf("%c ",c);
+        printf("\n");
+    }
+    tpl_free(tn);
+}
+
+

The file test40.tpl is from the nested packing example. When +run, this program prints:

+
+
+
a b
+1 2 3
+
+

tpl_free

+

The final step for any tpl is to release it using tpl_free(). Its only +argument is the the tpl_node* to free.

+
+
+
tpl_free( tn );
+
+

This function does not return a value (it is void).

+

tpl_Alen

+

This function takes a tpl_node* and an index number and returns an int +specifying the number of elements in the variable-length array.

+
+
+
num_elements = tpl_Alen(tn, index);
+
+

This is mainly useful for programs that unpack data and need to know ahead of +time the number of elements that will need to be unpacked. (It returns the +current number of elements; it will decrease as elements are unpacked).

+

tpl_peek

+

This function peeks into a file or a memory buffer containing a tpl image and +and returns a copy of its format string. It can also peek at the lengths of +any fixed-length arrays in the format string, or it can also peek into the data +stored in the tpl.

+

Format peek

+

The format string can be obtained +like this:

+
+
+
fmt = tpl_peek(TPL_FILE, "file.tpl");
+fmt = tpl_peek(TPL_MEM, addr, sz);
+
+

On success, a copy of the format string is returned. The caller must eventually +free it. On error, such as a non-existent file, or an invalid tpl image, it +returns NULL.

+

Array length peek

+

The lengths of all fixed-length arrays in the format string can be queried using +the TPL_FXLENS mode. It provides the number of such fixed-length arrays and +their lengths. If the former is non-zero, the caller must free the latter array +when finished. The format string itself must also be freed.

+
+
+
uint32_t num_fxlens, *fxlens, j;
+fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens);
+if (fmt) {
+  printf("format %s, num_fxlens %u\n", fmt, num_fxlens);
+  for(j=0; j<num_fxlens; j++) printf("fxlens[%u] %u\n", j, fxlens[j]);
+  if (num_fxlens > 0) free(fxlens);
+  free(fmt);
+}
+
+

The TPL_FXLENS mode is mutually exclusive with TPL_DATAPEEK.

+

Data peek

+

To peek into the data, additional arguments are used. This is a quick +alternative to mapping, loading and unpacking the tpl, but peeking is limited +to the data in index 0. In other words, no peeking into A(...) types. +Suppose the tpl image in file.tpl has the format string siA(i). Then the +index 0 format characters are si. This is how to peek at their content:

+
+
+
char *s;
+int i;
+fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "si", &s, &i);
+
+

Now s, i, and fmt have been populated with data. The caller must +eventually free fmt and s because they are allocated strings. +Of course, it works with TPL_MEM as well as TPL_FILE. Notice that +TPL_DATAPEEK was OR’d with the mode. You can also specify any leading +portion of the index 0 format if you don’t want to peek at the whole thing:

+
+
+
fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "s", &s);
+
+

The TPL_DATAPEEK mode is mutually exclusive with TPL_FXLENS.

+
Structure peek
+

Lastly you can peek into S(...) structures in index 0, but omit the +surrounding S(...) in the format, and specify an argument to receive +each structure member individually. You can specify any leading portion +of the structure format. For example if struct.tpl has the format string +S(si), you can peek at its data in these ways:

+
+
+
fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "s", &s);
+fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "si", &s, &i);
+
+

tpl_jot

+

This is a quick shortcut for generating a tpl. It can be used instead of the +usual "map, pack, dump, and free" lifecycle. With tpl_jot all those steps are +handled for you. It only works for simple formats-- namely, those without +A(...) in their format string. Here is how it is used:

+
+
+
char *hello = "hello", *world = "world";
+tpl_jot( TPL_FILE, "file.tpl", "ss", &hello, &world);
+
+

It supports the three standard modes, TPL_FILE, TPL_FD and TPL_MEM. +It returns -1 on failure (such as a bad format string or error writing the +file) or 0 on success.

+

tpl_hook

+

Most users will just leave these hooks at their default values. You can change +these hook values if you want to modify tpl’s internal memory management and +error reporting behavior.

+

A global structure called tpl_hook encapsulates the hooks. A program can +reconfigure any hook by specifying an alternative function whose prototype +matches the default. For example:

+
+
+
#include "tpl.h"
+extern tpl_hook_t tpl_hook;
+
+
+
+
int main() {
+    tpl_hook.oops = printf;
+    ...
+}
+
+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 3. Configurable hooks
Hook Description Default

tpl_hook.oops

log error messages

tpl_oops

tpl_hook.malloc

allocate memory

malloc

tpl_hook.realloc

reallocate memory

realloc

tpl_hook.free

free memory

free

tpl_hook.fatal

log fatal message and exit

tpl_fatal

tpl_hook.gather_max

tpl_gather max image size

0 (unlimited)

+
+

The oops hook

+

The oops has the same prototype as printf. The built-in default oops +handling function writes the error message to stderr.

+

The fatal hook

+

The fatal hook is invoked when a tpl function cannot continue because of an out- +of-memory condition or some other usage violation or inconsistency. It has this +prototype:

+
+
+
void fatal_fcn(char *fmt, ...);
+
+

The fatal hook must not return. It must either exit, or if the program needs +to handle the failure and keep executing, setjmp and longjmp can be used. +The default behavior is to exit(-1).

+
+
Using longjmp in a fatal error handler
+
+
#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "tpl.h"
+
+jmp_buf env;
+extern tpl_hook_t tpl_hook;
+
+void catch_fatal(char *fmt, ...) {
+  va_list ap;
+
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  longjmp(env,-1);                /* return to setjmp point */
+}
+
+int main() {
+  int err;
+  tpl_node *tn;
+  tpl_hook.fatal = catch_fatal;    /* install fatal handler */
+
+  err = setjmp(env); /* on error, control will return here  */
+  if (err) {
+    printf("caught error!\n");
+    return -1;
+  }
+
+  tn = tpl_map("@");              /* generate a fatal error */
+  printf("program ending, without error\n");
+  return 0;
+}
+
+

This example is included in tests/test123.c. When run, this program prints:

+
+
+
unsupported option @
+failed to parse @
+caught error!
+
+

tpl_gather

+
+
+

The prototype for this function is:

+
+
+
int tpl_gather( int mode, ...);
+
+

The mode argument is one of three constants listed below, which must be +followed by the mode-specific required arguments:

+
+
+
TPL_GATHER_BLOCKING,    int fd, void **img, size_t *sz
+TPL_GATHER_NONBLOCKING, int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data
+TPL_GATHER_MEM,         void *addr, size_t sz, tpl_gather_t **gs, tpl_gather_cb *cb, void *data
+
+
+ + + +
+
Note
+
+
tpl_hook.gather_max
All modes honor tpl_hook.gather_max, specifying the maximum byte size for a +tpl image to be gathered (the default is unlimited, signified by 0). If a source +attempts to send a tpl image larger than this maximum, whatever partial image +has been read will be discarded, and no further reading will take place; in this +case tpl_gather will return a negative (error) value to inform the caller that +it should stop gathering from this source, and close the originating file +descriptor if there is one. (The whole idea is to prevent untrusted sources from +sending extremely large tpl images which would consume too much memory.)
+
+

TPL_GATHER_BLOCKING

+

In this mode, tpl_gather blocks while reading file descriptor fd until one +complete tpl image is read. No bytes past the end of the tpl image will be read. +The address of the buffer containing the image is returned in img and its size +is placed in sz. The caller is responsible for eventually freeing the buffer. +The function returns 1 on success, 0 on end-of-file, or a negative number on +error.

+

TPL_GATHER_NONBLOCKING

+

This mode is for non-blocking, event-driven programs that implement their +own file descriptor readability testing using select() or the like. In this +mode, tpl images are gathered in chunks as data becomes readable. Whenever a +full tpl image has been gathered, it invokes a caller-specified callback to do +something with the image. The arguments are the file descriptor fd which the +caller has determined to be readable and which must be in non-blocking mode, a +pointer to a file-descriptor-specific handle which the caller has declared +(explained below); a callback to invoke when a tpl image has been read; and an +opaque pointer that will passed to the callback.

+

For each file descriptor on which tpl_gather will be used, the caller must +declare a tpl_gather_t* and initialize it to NULL. Thereafter it will be +used internally by tpl_gather whenever data is readable on the descriptor.

+

The callback will only be invoked whenever tpl_gather() has accumulated one +complete tpl image. It must have this prototype:

+
+
+
int (tpl_gather_cb)(void *img, size_t sz, void *data);
+
+

The callback can do anything with the tpl image but it must not free it. It can +be copied if it needs to survive past the callback’s return. The callback should +return 0 under normal circumstances, or a negative number to abort; that is, +returning a negative number causes tpl_gather itself to discard any remaining +full or partial tpl images that have been read, and to return a negative number +(-4 in particular) to signal its caller to close the file descriptor.

+

The return value of tpl_gather() is negative if an error occured or 0 if a +normal EOF was encountered-- both cases require that the caller close the file +descriptor (and stop monitoring it for readability, obviously). If the return +value is positive, the function succeeded in gathering whatever data was +currently readable, which may have been a partial tpl image, or one or more +complete images.

+
Typical Usage
+

The program will have established a file descriptor in non-blocking mode and +be monitoring it for readability, using select(). Whenever it’s readable, the +program calls tpl_gather(). In skeletal terms:

+
+
+
tpl_gather_t *gt=NULL;
+int rc;
+
+
+
+
void fd_is_readable(int fd) {
+  rc = tpl_gather( TPL_GATHER_NONBLOCKING, fd, &gt, callback, NULL );
+  if (rc <= 0) {
+      close(fd);               /* got eof or fatal */
+      stop_watching_fd(fd);
+  }
+}
+
+
+
+
int callback( void *img, size_t sz, void *data ) {
+  printf("got a tpl image\n"); /* do something with img. do not free it. */
+  return 0;                    /* normal (no error) */
+}
+
+

TPL_GATHER_MEM

+

This mode is identical to TPL_GATHER_NONBLOCKING except that it gathers from a +memory buffer instead of from a file descriptor. In other words, if some other +layer of code-- say, a decryption function (that is decrypting fixed-size +blocks) produces tpl fragments one-by-one, this mode can be used to reconstitute +the tpl images and invoke the callback for each one. Its parameters are the same +as for the TPL_GATHER_NONBLOCKING mode except that instead of a file +descriptor, it takes a buffer address and size. The return values are also the +same as for TPL_GATHER_NONBLOCKING noting of course there is no file +descriptor to close on a non-positive return value.

+
+
+

+ + + diff --git a/doc/pdf/userguide.pdf b/doc/pdf/userguide.pdf new file mode 100644 index 0000000..ddac2c8 Binary files /dev/null and b/doc/pdf/userguide.pdf differ diff --git a/doc/txt/ChangeLog.txt b/doc/txt/ChangeLog.txt new file mode 100644 index 0000000..eb55014 --- /dev/null +++ b/doc/txt/ChangeLog.txt @@ -0,0 +1,70 @@ +tpl ChangeLog +============= + +Version 1.6 (2011-??-??) +-------------------------- +* Added `const` to a number of API parameters and exposed tpl_map_va (thanks, Simon Dawson!) +* Fixed a bug in the Windows version of tpl that prevented an application + from serializing more than once to the same file- the file stayed locked until + the application was closed. (thanks, Werner Krattenthaler!) +* Fixed a documentation error to indicate that `tpl_dump` when used in the + `TPL_GETSIZE` mode stores its result in a `size_t` rather than a `uint32_t` + (thanks, M. Nunberge!) +* Fixed a typo in the User Guide example of packing a linked link (thanks, Bryan Mishkin!) + +Version 1.5 (2010-02-05) +-------------------------- +* tpl now builds as a DLL under Microsoft Visual Studio! (thanks, degski and Zhang Yafei!) +* there are now two download options: the http://downloads.sourceforge.net/tpl/libtpl-1.5.tar.bz2[tarball] and the Visual Studio http://downloads.sourceforge.net/tpl/tpl-1.5-vs2008.zip[solution] +* a crash in `tpl_free` on certain format strings has been fixed (thanks, Eric Rose!) +* fixed a bug in `tpl_dump` on 64-bit, big-endian platforms +* changed some pointer casts from `long` to `uintptr_t` since 64-bit Windows has 32-bit longs +* tpl has been downloaded 4,195 times. + + +Version 1.4 (2009-04-21) +-------------------------- +* fixed-length arrays can now be multi-dimensional like `i##` +* fixed-length string arrays like `s#` are now supported +* nested structures can now be expressed, using the dollar symbol, e.g. `S(ci$(cc))` +* `tpl_dump` can use a caller-allocated output buffer (`TPL_MEM|TPL_PREALLOCD`) +* `tpl_load` can tolerate excess space in input buffer (`TPL_MEM|TPL_EXCESS_OK`) +* implement `TPL_FXLENS` flag for `tpl_peek` to get lengths of fixed-length arrays +* implement `TPL_GETSIZE` flag for `tpl_dump` to get dump size without dumping +* fix success return code from `tpl_dump(TPL_FD,...)` (thanks, Max Lapan!) +* deprecated the wildcard unpacking `S(*)` feature + +Version 1.3 (2009-02-10) +-------------------------- +* added `TPL_DATAPEEK` mode for `tpl_peek` +* added support for `NULL` strings +* added support for 16-bit integer types (`j`,`v`) +* added `tpl_jot` +* added support for fixed-length arrays of structures `S(...)#` +* added support for pre-C99 compilers (thanks, Wei Wei!) +* improved structure alignment calculation (thanks, Wu Yongwei!) +* added RPM spec file (thanks, Alessandro Ren!) +* compiles cleanly with `-Wall` and `-pedantic` and with `-O3` +* made link:license.html[BSD license] terms even more permissive +* test suite: exit with status zero when all tests pass +* added PDF user guide +* added http://troydhanson.wordpress.com/feed/[update news] image:img/rss.png[(RSS)] +* added http://apps.sourceforge.net/mediawiki/tpl/[tpl wiki] + +Version 1.2 (2007-04-27) +-------------------------- +* Perl API and XML converter support 64-bit types + +Version 1.1 (2007-04-25) +-------------------------- +* support for serializing C structures +* support for serializing fixed-length arrays +* MinGW support (thanks, Horea Haitonic!) +* revised User Guide + +Version 1.0 (2006-09-28) +-------------------------- +* Initial version + +// vim: set tw=80 wm=2 nowrap syntax=asciidoc: + diff --git a/doc/txt/compiling.txt b/doc/txt/compiling.txt new file mode 100644 index 0000000..abb73ec --- /dev/null +++ b/doc/txt/compiling.txt @@ -0,0 +1,38 @@ +Compiling libtpl.a and libtpl.so +-------------------------------- + +******************************************************************************** +Normally in the top-level directory you simply run: + + ./configure + make + make install + +The rest of the document is not needed if you use this method. +******************************************************************************** + + +Manual compilation of static and shared library on a GNU Linux system: +---------------------------------------------------------------------- + +First cd into the "src" directory. + +Static library +~~~~~~~~~~~~~~ +You can build the static library `libtpl.a` using these commands: + + cc -c tpl.c + ar rc libtpl.a tpl.o + ranlib libtpl.a + +Dynamic library +~~~~~~~~~~~~~~ +You can build the dynamic library `libtpl.so` using these commands: + + cc -fpic -c tpl.c + cc -shared -o libtpl.so tpl.o + +Keep in mind that you need to set the `LD_LIBRARY_PATH` environment variable +to include the directory where `libtpl.so` is installed in order for your +program to run. Alternatively you can put `libtpl.so` in a standard place like +`/usr/lib` and regenerate `ld.so.cache` using `ldconfig`. diff --git a/doc/txt/examples.txt b/doc/txt/examples.txt new file mode 100644 index 0000000..9faca3f --- /dev/null +++ b/doc/txt/examples.txt @@ -0,0 +1,297 @@ +tpl examples +============ +Troy D. Hanson > +v1.0, October 2006 + +include::sflogo.txt[] +include::topnav.txt[] + +Examples +-------- +include::toc.txt[] + +This document is a set of representative examples demonstrating how to use +tpl. If you're looking for a more explanatory document, please read the +link:userguide.html[User Guide]. + +An integer array +~~~~~~~~~~~~~~~~ + +.Storing an array of integers to file +------------------------------------------------------------------------------- +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map( "A(i)", &i ); + for( i=0; i<10; i++ ) { + tpl_pack( tn, 1 ); + } + tpl_dump( tn, TPL_FILE, "demo.tpl" ); + tpl_free( tn ); +} +------------------------------------------------------------------------------- + +A program that unpacks this tpl data file is shown below. + +.Re-reading an array of integers from file +------------------------------------------------------------------------------- +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map( "A(i)", &i ); + tpl_load( tn, TPL_FILE, "demo.tpl" ); + while (tpl_unpack( tn, 1 ) > 0) { + printf("%d ", i); + } + tpl_free( tn ); +} +------------------------------------------------------------------------------- + +When run, this program prints: + + 0 1 2 3 4 5 6 7 8 9 + + +A nested array +~~~~~~~~~~~~~~ + +.Packing nested arrays +-------------------------------------------------------------------------------- +#include "tpl.h" + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + for(c='a'; c<'c'; c++) tpl_pack(tn,2); + tpl_pack(tn, 1); + + for(c='1'; c<'4'; c++) tpl_pack(tn,2); + tpl_pack(tn, 1); + + tpl_dump(tn, TPL_FILE, "test40.tpl"); + tpl_free(tn); +} +-------------------------------------------------------------------------------- + +This creates a nested array in which the parent has two elements: the first +element is the two-element nested array 'a', 'b'; and the second element is +the three-element nested array '1', '2', '3'. + +.Unpacking nested arrays +-------------------------------------------------------------------------------- +#include "tpl.h" +#include + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + tpl_load(tn, TPL_FILE, "test40.tpl"); + while (tpl_unpack(tn,1) > 0) { + while (tpl_unpack(tn,2) > 0) printf("%c ",c); + printf("\n"); + } + tpl_free(tn); +} +-------------------------------------------------------------------------------- + + +When run, this program prints: + + a b + 1 2 3 + +A string array +~~~~~~~~~~~~~~ + +.Packing a string array +------------------------------------------------------------------------------- + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s; + + tn = tpl_map( "A(s)", &s ); + + s = "bob"; + tpl_pack(tn, 1); + + s = "betty"; + tpl_pack(tn, 1); + + tpl_dump(tn, TPL_FILE, "strings.tpl"); + tpl_free(tn); + } +------------------------------------------------------------------------------- + +.Unpacking a string array +------------------------------------------------------------------------------- + #include + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s; + + tn = tpl_map( "A(s)", &s ); + tpl_load( tn, TPL_FILE, "strings.tpl" ); + + while (tpl_unpack( tn, 1 ) > 0) { + printf("%s\n", s); + free(s); /* important! */ + } + + tpl_free(tn); + } +------------------------------------------------------------------------------- + +When run, this program prints: + + bob + betty + +Integer/string pairs +~~~~~~~~~~~~~~~~~~~~ + +.Packing integer/string pairs +------------------------------------------------------------------------------- +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + int id; + char *name, *names[] = { "joe", "bob", "mary" }; + + tn = tpl_map("A(is)", &id, &name); + + for(id=0,name=names[id]; id < 3; name=names[++id]) + tpl_pack(tn,1); + + tpl_dump(tn, TPL_FILE, "/tmp/test35.tpl"); + tpl_free(tn); +} +------------------------------------------------------------------------------- + +.Unpacking integer/string pairs +------------------------------------------------------------------------------- +#include +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + int id; + char *name; + + tn = tpl_map("A(is)", &id, &name); + tpl_load(tn, TPL_FILE, "/tmp/test35.tpl"); + + while ( tpl_unpack(tn,1) > 0 ) + printf("id %d, user %s\n", id, name); + + tpl_free(tn); +} +------------------------------------------------------------------------------- + +When run, this program prints: + + id 0, user joe + id 1, user bob + id 2, user mary + +A binary buffer +~~~~~~~~~~~~~~~ + +.Packing a binary buffer +------------------------------------------------------------------------------- + #include "tpl.h" + #include + + int main() { + tpl_node *tn; + tpl_bin tb; + struct timeval tv; /* we'll pack this structure as raw binary */ + gettimeofday(&tv,NULL); /* populate the structure with some data */ + + tn = tpl_map( "B", &tb ); + tb.sz = sizeof(struct timeval); + tb.addr = &tv; + tpl_pack( tn, 0 ); + + tpl_dump(tn, TPL_FILE, "bin.tpl"); + tpl_free(tn); + } +------------------------------------------------------------------------------- + + +.Unpacking a binary buffer +------------------------------------------------------------------------------- + #include "tpl.h" + + int main() { + tpl_node *tn; + tpl_bin tb; + + tn = tpl_map( "B", &tb ); + tpl_load( tn, TPL_FILE, "bin.tpl" ); + + tpl_unpack( tn, 0 ); + printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr); + free(tb.addr); /* important! */ + + tpl_free(tn); + } +------------------------------------------------------------------------------- + + +Simple pipe IPC +~~~~~~~~~~~~~~~ + +This is a simple example of inter-process communication (IPC) over a pipe. + +.IPC over a pipe +------------------------------------------------------------------------------- +int main() { + tpl_node *tn; + unsigned i, sum=0; + int fd[2], pid; + + pipe(fd); + if ( (pid = fork()) == 0) { /* child */ + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd[0]); + while (tpl_unpack(tn,1) > 0) sum += i; + tpl_free(tn); + printf("sum is %d\n", sum); + + } else if (pid > 0) { /* parent */ + + tn = tpl_map("A(u)",&i); + for(i=0;i<10000;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + waitpid(pid,NULL,0); + } +} +------------------------------------------------------------------------------- + +The child unpacks the integers in the message, and sums them, printing: + + 49995000 + +The example above (with `#include` headers omitted here) is included in the +file `tests/test28.c`. diff --git a/doc/txt/future.txt b/doc/txt/future.txt new file mode 100644 index 0000000..dc2240f --- /dev/null +++ b/doc/txt/future.txt @@ -0,0 +1,52 @@ +For future reference these are some API design ideas- not working code! + +-------------------------------------------------------------------------------- +Java API ideas +-------------------------------------------------------------------------------- +http://www.ioplex.com/~miallen/encdec/ (binary pack/unpack utilities) + +The Java API would take an object, and a list of field names, then use the +Java Reflection API to read or write those fields during packing and +unpacking. + +I.e. if you are going to unpack a tpl with format string A(if), you +might create a Java class that has two instance variables (an int and +a double). Then you create an object of that class, and pass it to +tpl_map (or perhaps a constructor for the Tpl class), like + +class Unpacker { + int count; + double weight; +} +... +Unpacker up = new Unpacker(); +Tpl tn = new Tpl("A(if)", up, "count", "weight"); +tn.tpl_unpack(1); // stores unpacked values into count,weight using Reflection + +-------------------------------------------------------------------------------- +Ruby API ideas +-------------------------------------------------------------------------------- +#!/usr/local/ruby/bin/ruby -w + +class Tpl < Hash + def initialize(fmt, *args) + @fmt = fmt + @args = args + end + + def pack + @args.each {|key| puts "#{key} #{self[key]}"} + end +end + + +p = Tpl.new("A(i)", :id); +10.times do |i| + p[:id] = i + p.pack +end +p.dump("/tmp/file.tpl") # p.dump(arg) checks arg.respond_to?(:write) + +p = Tpl.new("A(i)", :id); +p.load("/tmp/file.tpl") +p.unpack(1) {|h| puts h[:id]} diff --git a/doc/txt/perl.txt b/doc/txt/perl.txt new file mode 100644 index 0000000..5c66843 --- /dev/null +++ b/doc/txt/perl.txt @@ -0,0 +1,302 @@ +tpl Perl API +============ +Troy D. Hanson +v1.1, April 2007 + +include::sflogo.txt[] +include::topnav.txt[] + +Perl API +-------- +include::toc.txt[] + +The Perl API for reading and writing tpl is nearly identical to the C API. This +document will briefly explain the Perl API and provide examples. The chief +motivation for having a Perl API is to communicate with C programs that use tpl. + +[TIP] +.Start with the C API +This document assumes familiarity with the C API. The concepts of using tpl +are not explained here. For an introduction to tpl and its C API, see the +link:userguide.html[User Guide]. + +Tpl.pm +~~~~~~ +The `Tpl.pm` file (in the `lang/perl`) directory contains the Perl module. You +can copy it to another directory if you wish. Your Perl program may need to +include a `use lib` statement to find the module. + + #!/usr/bin/perl + use lib "/some/directory"; + use Tpl; + +tpl_map +~~~~~~~ +This function resembles the C version, except that it's invoked via the `Tpl` +module, and it takes references to Perl variables after the format string. + + my $i; + my $tpl = Tpl->tpl_map("A(i)",\$i); + +The return value is a tpl object; all other API calls are object methods. +Incidentally, there is no `tpl_free()` method corresponding to the C API. + +Fixed-length arrays +^^^^^^^^^^^^^^^^^^^ +Format strings such as `i#` denote a fixed-length array. In the Perl API, +fixed-length arrays require two arguments: a list reference, and the fixed +length. For example: + + my @x; + my $tpl = Tpl->tpl_map("i#", \@x, 10); + +When fixed-length arrays are packed or unpacked, the specified number of +elements will be copied from (or placed into) the designated list. + +Structures +^^^^^^^^^^ +Format strings containing `S(...)` are handled in the Perl API as if only the +interior, parenthesized part was present. (It does not work like the C API). So +simply ignore the `S(...)` and consider only its interior format characters when +constructing the argument list: + + my ($str, $int); + my $tpl = Tpl->tpl_map("S(si)", \$str, \$int); + +It really only makes sense to use `S(...)` in a format string in the Perl API if +you are communicating with a C program that uses structures. + +tpl_pack +~~~~~~~~ +This is nearly identical to the C version. The only argument is the index +number to pack. + + $tpl->tpl_pack(1); + +tpl_dump +~~~~~~~~ +This method is a little different than the C version. Given no arguments, it +returns the tpl image; given one argument it writes a file with that name. + + $tpl->tpl_dump("demo.tpl"); # writes demo.tpl + +Or, + + my $img = $tpl->tpl_dump(); + +The tpl image is a binary buffer. You can do whatever you want with it, such as +write it to a socket or pipe (probably to C program listening on the other end), +or save it somewhere and later re-load it using `tpl_load()`. + +tpl_load +~~~~~~~~ +This method loads a tpl image from a file or from a Perl variable. It takes +one argument. If it's not a reference, it's assumed to be a filename to load. + + $tpl->tpl_load("demo.tpl"); + +Otherwise, if the argument is a Perl reference, it's construed as a variable +containing the tpl image: + + $tpl->tpl_load(\$img); + +The method will `die` if the image is invalid or the file doesn't exist. You +can wrap it with `eval` to catch such errors: + + eval { $tpl->tpl_load(\$img); }; + print "failed to load\n" if $@; + +tpl_unpack +~~~~~~~~~~ +This is nearly identical to the C version. The only argument is the index +number to unpack. + + $tpl->tpl_unpack(1); + +Examples +-------- + +Integer array +~~~~~~~~~~~~~ + +.Packing A(i) to file +-------------------------------------------------------------------------------- +#!/usr/bin/perl + +use strict; +use warnings; + +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { + $tpl->tpl_pack(1); +} +$tpl->tpl_dump("demo.tpl"); +-------------------------------------------------------------------------------- + +.Unpacking A(i) from file +-------------------------------------------------------------------------------- +#!/usr/bin/perl + +use strict; +use warnings; + +use Tpl; + +my $j; +my $tpl2 = Tpl->tpl_map("A(i)",\$j); +$tpl2->tpl_load("demo.tpl"); +while($tpl2->tpl_unpack(1) > 0) { + print "$j\n"; +} +-------------------------------------------------------------------------------- + +Message-passing +~~~~~~~~~~~~~~~ +While the bulk of this example is socket handling, it demonstrates how you can +use tpl as a message-passing format. In the real-world, you might have a C +server and a Perl client, for example. In this example, we'll code both a client +and a server in Perl. + +.A server that sums integers +******************************************************************************** +Programming literature is rife with contrived examples so we will follow in that +tradition. Our server will do no more than sum a list of integers. But in doing +so it will demonstrate message passing adequately. Both its input (the integer +array) and its output (an integer) are tpl images, passed over a TCP/IP socket. +******************************************************************************** + +Server +^^^^^^ +The server waits for a connection from a client. When it gets one, it accepts +the connection and immediately forks a child process to handle it. Then it goes +back to waiting for another new connection. + +The server child process handles the client by loading and unpacking the tpl +image sent by the client (containing an array of integers). It calculates their +sum and constructs a new tpl image containing the sum, which it sends back to +the client. + +.Server +-------------------------------------------------------------------------------- +#!/usr/bin/perl + +use strict; +use warnings; + +use IO::Socket::INET; +use Tpl; + +$SIG{CHLD} = "IGNORE"; # don't create zombies + +our $port = 2000; + +sub handle_client { + my $client = shift; + + undef $/; + my $request = <$client>; # get request (slurp) + + # read input array, and calculate total + my ($i,$total); + my $tpl = Tpl->tpl_map("A(i)", \$i); + eval { $tpl->tpl_load(\$request); }; + die "received invalid tpl" if $@; + $total += $i while $tpl->tpl_unpack(1) > 0; + + # formulate response and send + my $tpl2 = Tpl->tpl_map("i", \$total); + $tpl2->tpl_pack(0); + my $response = $tpl2->tpl_dump(); + print $client $response; + close $client; +} + +my $server = IO::Socket::INET->new(LocalPort => $port, + Type => SOCK_STREAM, + Reuse => 1, + Listen => 10 ) + or die "Can't listen on port $port: $!\n"; + +while (1) { + my $client = $server->accept(); + next unless $client; + # new connection + my $pid = fork; + die "can't fork: $!\n" unless defined $pid; + if ($pid > 0) { + # parent + close $client; + } elsif ($pid == 0) { + # child + handle_client($client); + exit(0); + } +} +close ($server); +-------------------------------------------------------------------------------- + +Client +^^^^^^ + +The client is a simpler program. It constructs the tpl image containing the +integer array (taken from its command-line arguments), connects to the server +and sends the tpl image to it, and then awaits the response tpl. The response +containing the sum is loaded, unpacked and printed. + +.Client +-------------------------------------------------------------------------------- +#!/usr/bin/perl + +use strict; +use warnings; + +use IO::Socket::INET; +use Tpl; + +our $port = 2000; + +# construct tpl +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +$tpl->tpl_pack(1) while ($i=shift @ARGV); +my $request = $tpl->tpl_dump(); + +# send to server, get response +my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect"; +print $socket $request; +shutdown($socket,1); # done writing (half-close) +undef $/; +my $response = <$socket>; # get reply (slurp) + +# decode response (or print error) +my $total; +my $tpl2 = Tpl->tpl_map("i", \$total); +eval { $tpl2->tpl_load(\$response); }; +die "invalid response\n" if $@; +$tpl2->tpl_unpack(0); +print "total is $total\n"; +-------------------------------------------------------------------------------- + +Running thise example +^^^^^^^^^^^^^^^^^^^^^ +If the client and server programs are in `client.pl` and `server.pl`, then +you can run the example by starting the server in one window: + + ./server.pl + +Then run the client in another window. E.g., + + ./client.pl 1 2 3 4 5 + +The client runs and then exits, printing: + + total is 15 + +You can re-run the client with different arguments. When done, type `Ctrl-C` in +the server window to terminate it. + +// vim: set tw=80 wm=2 syntax=asciidoc: + diff --git a/doc/txt/sflogo.txt b/doc/txt/sflogo.txt new file mode 100644 index 0000000..68b153b --- /dev/null +++ b/doc/txt/sflogo.txt @@ -0,0 +1,5 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +SourceForge.net ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/doc/txt/toc.txt b/doc/txt/toc.txt new file mode 100644 index 0000000..458eed1 --- /dev/null +++ b/doc/txt/toc.txt @@ -0,0 +1,85 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/doc/txt/topnav.txt b/doc/txt/topnav.txt new file mode 100644 index 0000000..257e665 --- /dev/null +++ b/doc/txt/topnav.txt @@ -0,0 +1,10 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ sf.net summary page > + tpl home > + {doctitle} + [View PDF] +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/doc/txt/userguide.txt b/doc/txt/userguide.txt new file mode 100644 index 0000000..e9aad6b --- /dev/null +++ b/doc/txt/userguide.txt @@ -0,0 +1,1264 @@ +tpl User Guide +============== +Troy D. Hanson +v1.5, February 2010 + +include::sflogo.txt[] +include::topnav.txt[] + +Overview +-------- +include::toc.txt[] + +Serialization in C +~~~~~~~~~~~~~~~~~~ +Tpl is a library for serializing C data. The data is stored in its natural +binary form. The API is small and tries to stay "out of the way". +Tpl can serialize many C data types, including structures. + +Uses for tpl +~~~~~~~~~~~~ +Tpl makes a convenient file format. For example, suppose a program needs to +store a list of user names and ids. This can be expressed using the format +string `A(si)`. If the program needs two such lists (say, one for regular +users and one for administrators) this could be expressed as `A(si)A(si)`. It +is easy to read and write this kind of structured data using tpl. + +Tpl can also be used as an IPC message format. It handles byte order issues +and deframing individual messages off of a stream automatically. + +Expressing type +~~~~~~~~~~~~~~~ +The "data type" of a tpl is explicitly stated as a format string. There is +never any ambiguity about the type of data stored in a tpl. Some examples: + +* `A(is)` is a variable-length array of integer-string pairs +* `A(is)A(is)` are two such arrays, completely independent of one another +* `S(ci)` is a structure containing a char and integer +* `S(ci)#` is a fixed-length array of the latter structure +* `A(A(i))` is a nested array, that is, an array of integer arrays + +The tpl image +~~~~~~~~~~~~~ +A tpl image is the serialized form of a tpl, stored in a memory buffer or file, +or written to a file descriptor. + +What's in a tpl image? +^^^^^^^^^^^^^^^^^^^^^^ +There is no need to understand the internal structure of the tpl image. But for the +curious, the image is a strictly defined binary buffer having two sections, +a header and the data. The header encodes the length of the image, its +format string, endian order and other flags. The data section contains the +packed data. + +No framing needed +^^^^^^^^^^^^^^^^^ +A property of the tpl image is that consecutive images can be written to a stream +without requiring any delimiter between them. The reader making use of +`tpl_gather` (or `tpl_load` in `TPL_FD` mode) will obtain exactly one tpl image at +a time. Therefore tpl images can be used as an IPC message format without any +higher-level framing protocol. + +Data portability +^^^^^^^^^^^^^^^^ +A tpl image generated on one kind of CPU will generally be portable to other +CPU types when tpl is used properly. This may be a surprise considering that +tpl is a binary format. But tpl has been carefully designed to make this work. +Each <> has an associated explicitly-sized type. For +integer and floating point types, whose "endian" or byte-order convention varies +from one CPU to another, tpl automatically and transparently corrects the +endian order (if needed) during the unpacking process. Floating point numbers +present their own <>. 'No guarantees +are made with regard to floating point portability.' That said, because many +modern CPU's use IEEE 754 floating point representation, data is likely to be +portable among them. + +XML and Perl +~~~~~~~~~~~~ +'Note: The `tplxml` utility and the Perl module are currently unsupported in tpl 1.5.' + +XML +^^^ +While a tpl image is a binary entity, you can view any tpl image in XML format +using the included `tplxml` utility, located in the `lang/perl` directory. + + tplxml file.tpl > file.xml + tplxml file.xml > file.tpl + +The utility is bidirectional, as shown. The file extension is not important; +`tplxml` inspects its input to see if it's tpl or XML. You can also pipe data +into it instead of giving it a filename. The `tplxml` utility is slow. Its +purpose is two-fold: debugging (manual inspection of the data in a tpl), and +interoperability with XML-based programs. The resulting XML is often ten times +the size of the original binary tpl image. + +Perl +^^^^ +There is a Perl module in `lang/perl/Tpl.pm`. The link:perl.html[Perl API] +is convenient for writing Perl scripts that interoperate with C programs, and +need to pass structured data back and forth. It is written in pure Perl. + +Platforms +~~~~~~~~~ +The tpl software was developed for POSIX systems and has been tested on 32- and 64-bit +platforms including: + + * Linux + * Solaris + * Mac OS X + * OpenBSD + * Windows using Visual Studio 2008 or 2010, or Cygwin or MinGW + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Download +~~~~~~~~ +Please follow the link to download on the +http://tpl.sourceforge.net[tpl website]. + +Getting help +~~~~~~~~~~~~ +If you need help, you are welcome to email the author at +mailto:thanson@users.sourceforge.net[]. + +Resources +~~~~~~~~~ +News:: + The author has a news feed for http://troydhanson.wordpress.com/feed/[software updates] image:img/rss.png[(RSS)]. + +Build and install +----------------- + +Tpl has no dependencies on libraries other than the system C library. You +can simply copy the tpl source into your project, so you have no dependencies. +Alternatively, you can build tpl as a library and link it to your program. + +As source +~~~~~~~~~ +The simplest way to use tpl is to copy the source files `tpl.h` and `tpl.c` +(from the `src/` directory) right into your project, and build them with the +rest of your source files. No special compiler flags are required. + +As a library +~~~~~~~~~~~~ +Alternatively, to build tpl as a library, from the top-level directory, run: + + ./configure + make + make install + +This installs a static library `libtpl.a` and a shared library (e.g., +`libtpl.so`), if your system supports them, in standard places. The installation +directory can be customized using `./configure --prefix=/some/directory`. Run +`configure --help` for further options. + +Test suite +^^^^^^^^^^ +You can compile and run the built-in test suite by running: + + cd tests/ + make + +On Windows +~~~~~~~~~~ + +DLL +^^^ +On the tpl home page, a Visual Studio 2008 solution package is available for +download. This zip file contains pre-built 32- and 64-bit versions of tpl as a +DLL. If you like, you can build the DLL yourself using VS2008 or VS2010 (the +free Express Edition is sufficient) by opening the solution file and choosing +Build Solution. + +Non-DLL usage +^^^^^^^^^^^^^ +Alternatively, tpl can be used directly (instead of as a DLL) by compiling +the tpl sources right into your program. To do this, add `tpl.c`, `tpl.h`, +`win/mman.h` and `win/mmap.c` to your program's source and header files and +add the preprocessor definition `TPL_NOLIB`. + +MinGW/Cygwin +^^^^^^^^^^^^ +Prior to tpl release 1.5, using tpl on Windows required building it with MinGW +or Cygwin. This is no longer necessary. If you want to build it that way anyway, +use the non-Windows (i.e. tar.bz2) tpl download and follow the "configure; make; +make install" approach. + +API concepts +------------ +To use tpl, you need to know the order in which to call the API functions, and +the background concepts of format string, arrays and index numbers. + +Order of functions +~~~~~~~~~~~~~~~~~~ +Creating a tpl is always the first step, and freeing it is the last step. In +between, you either pack and dump the tpl (if you're serializing data) or you +load a tpl image and unpack it (if you're deserializing data). + +.Order of usage +[width="50%",cols="^1,^5m,^5m",grid="none",options="header"] +|=============================================================================== +|Step | If you're serializing...| If you're deserializing... +| 1. | tpl_map() | tpl_map() +| 2. | tpl_pack() | tpl_load() +| 3. | tpl_dump() | tpl_unpack() +| 4. | tpl_free() | tpl_free() +|=============================================================================== + +[[format]] +Format string +~~~~~~~~~~~~~ +When a tpl is created using `tpl_map()`, its data type is expressed as a format +string. Each character in the format string has an associated argument of a +specific type. For example, this is how a format string and its arguments are +passed in to `tpl_map`: + + tpl_node *tn; + char c; + int i[10]; + tn = tpl_map("ci#", &c, i, 10); /* ci# is our format string */ + +[[types]] +.Supported format characters +[width="90%",grid="none",options="header",cols="5^m,20,20"] +|================================================================================ +|Type | Description | Required argument type +| j | 16-bit signed int | int16_t* or equivalent +| v | 16-bit unsigned int | uint16_t* or equivalent +| i | 32-bit signed int | int32_t* or equivalent +| u | 32-bit unsigned int | uint32_t* or equivalent +| I | 64-bit signed int | int64_t* or equivalent +| U | 64-bit unsigned int | uint64_t* or equivalent +| c | character (byte) | char* +| s | string | char** +| f | 64-bit double precision float | double* (varies by platform) +| # | array length; modifies preceding `iujvIUcsf` or `S(...)`| int +| B | binary buffer (arbitrary-length) | tpl_bin* +| S | structure (...) | struct * +| $ | nested structure (...) | none +| A | array (...) | none +|================================================================================ + +Explicit sizes +^^^^^^^^^^^^^^ +The sizes of data types such as `long` and `double` vary by platform. This must +be kept in mind because most tpl format characters require a pointer argument to +a specific-sized type, listed above. You can use explicit-sized types such as +`int32_t` (defined in `inttypes.h`) in your program if you find this helpful. + +[[trouble_with_double]] +The trouble with double ++++++++++++++++++++++++ +Unfortunately there are no standard explicit-sized floating-point types-- no +`float64_t`, for example. If you plan to serialize `double` on your platform +using tpl's `f` format character, first be sure that your `double` is 64 bits. +Second, if you plan to deserialize it on a different kind of CPU, be sure that +both CPU's use the same floating-point representation such as IEEE 754. + +[[arrays]] +Arrays +~~~~~~ +Arrays come in two kinds: *fixed-length* and *variable-length* arrays. +Intuitively, they can be thought of like conventional C arrays and linked lists. +In general, use fixed-length arrays if possible, and variable-length arrays +if necessary. The variable-length arrays support more complex data types, and +give or receive the elements to your program one by one. + +Fixed-length vs. Variable-length arrays +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Notation:: + Fixed-length arrays are denoted like `i#` (a simple type followed by one or + more `#` signs), but variable-length arrays are denoted like `A(i)`. +Element handling:: + All the elements of a fixed-length array are packed or unpacked at once. But + the elements of a variable-length array are packed or unpacked one by one. +Array length:: + The number of elements in a fixed-length array is specified before use-- + before any data is packed. But variable-length arrays do not have a fixed + element count. They can have any number of elements packed into them. When + unpacking a variable-length array, they are unpacked one by one until they + are exhausted. +Element types:: + Elements of fixed-length arrays can be the integer, byte, double, string + types or structures. (This excludes format characters `BA`). Fixed-length + arrays can also be multi-dimensional like `i##`. Variable-length arrays can + have simple or complex elements-- for example, an array of ints `A(i)`, an + array of int/double pairs `A(if)`, or even nested arrays like `A(A(if))`. + +Before explaining all the concepts, it's illustrative to see how both kinds of +arrays are used. Let's pack the integers 0 through 9 both ways. + +//|================================================================================ +//|Fixed-length array packing | Variable-length array packing +//|#include "tpl.h" | #include "tpl.h" +//|int main() { | int main() { +//| tpl_node *tn; | tpl_node *tn; +//| int x[] = {0,1,2,3,4,5,6,7,8,9}; | int x; +//| | +//| tn = tpl_map("i#", x, 10); | tn = tpl_map("A(i)", &x); +//| /* pack all 10 elements at once */ | /* pack one element at a time */ +//| tpl_pack(tn,0); | for(x = 0; x < 10; x++) tpl_pack(tn,1); +//| tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl");| tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl"); +//| tpl_free(tn); | tpl_free(tn); +//|} | } +//|================================================================================ + +[[fixed_pack]] +.Packing 0-9 as a fixed-length array +------------------------------------------------------------------------------- +#include "tpl.h" +int main() { + tpl_node *tn; + int x[] = {0,1,2,3,4,5,6,7,8,9}; + + tn = tpl_map("i#", x, 10); + tpl_pack(tn,0); /* pack all 10 elements at once */ + tpl_dump(tn, TPL_FILE, "/tmp/fixed.tpl"); + tpl_free(tn); +} +------------------------------------------------------------------------------- + +Note that the length of the fixed-length array (10) was passed as an argument to +`tpl_map()`. The corresponding unpacking <> is listed +further below. Now let's see how we would pack 0-9 as a variable-length array: + +.Packing 0-9 as a variable-length array +------------------------------------------------------------------------------- +#include "tpl.h" +int main() { + tpl_node *tn; + int x; + + tn = tpl_map("A(i)", &x); + for(x = 0; x < 10; x++) tpl_pack(tn,1); /* pack one element at a time */ + tpl_dump(tn, TPL_FILE, "/tmp/variable.tpl"); + tpl_free(tn); +} +------------------------------------------------------------------------------- + +Notice how we called `tpl_pack` in a loop, once for each element 0-9. Again, +there is a corresponding unpacking <> shown later in the +guide. You might also notice that this time, we passed 1 as the final argument +to tpl_pack. This is an index number designating which variable-length array +we're packing. In this case, there is only one. + +[[index]] +Index numbers +^^^^^^^^^^^^^ +Index numbers identify a particular variable-length array in the format string. +Each `A(...)` in a format string has its own index number. The index numbers +are assigned left-to-right starting from 1. Examples: + + A(i) /* index number 1 */ + A(i)A(i) /* index numbers 1 and 2 */ + A(A(i)) /* index numbers 1 and 2 (order is independent of nesting) */ + +Special index number 0 +++++++++++++++++++++++ +The special index number 0 designates all the format characters that are not +inside an `A(...)`. Examples of what index 0 does (and does not) designate: + + S(ius) /* index 0 designates the whole thing */ + iA(c)u /* index 0 designates the i and the u */ + c#A(i)S(ci) /* index 0 designates the c# and the S(ci) */ + +An index number is passed to `tpl_pack` and `tpl_unpack` to specify which +variable-length array (or non-array, in the case of index number 0) to act upon. + +Integers +~~~~~~~~ +The array examples <> demonstrated how integers could be +packed. We'll show some further examples here of unpacking integers and dealing +with multi-dimensional arrays. The same program could be used to demonstrate +working with byte, 16-bit shorts, 32-bit or 64-bit signed and unsigned integers +with only a change to the data type and the format character. + +[[fixed_unpack]] +.Unpacking 0-9 from a fixed-length array +-------------------------------------------------------------------------------- +#include "tpl.h" +int main() { + tpl_node *tn; + int x[10]; + + tn = tpl_map("i#", x, 10); + tpl_load(tn, TPL_FILE, "/tmp/fixed.tpl"); + tpl_unpack(tn,0); /* unpack all 10 elements at once */ + tpl_free(tn); + /* now do something with x[0]...x[9].. (not shown */ +} +-------------------------------------------------------------------------------- + +For completeness, let's also see how to unpack a variable-length integer array. + +[[var_unpack]] +.Unpacking 0-9 from a variable-length array +------------------------------------------------------------------------------- +#include "tpl.h" +int main() { + tpl_node *tn; + int x; + + tn = tpl_map("A(i)", &x); + tpl_load(tn, TPL_FILE, "/tmp/variable.tpl"); + while (tpl_unpack(tn,1) > 0) printf("%d\n",x); /* unpack one by one */ + tpl_free(tn); +} +------------------------------------------------------------------------------- + +[[multidim_int]] +Multi-dimensional arrays +^^^^^^^^^^^^^^^^^^^^^^^^ +A multi-dimensional matrix of integers can be packed and unpacked the same way +as any fixed-length array. + + int xy[XDIM][YDIM]; + ... + tn = tpl_map("i##", xy, XDIM, YDIM); + tpl_pack(tn, 0); + +This single call to `tpl_pack` packs the entire matrix. + +Strings +~~~~~~~ +Tpl can serialize C strings. A different format is used for `char*` vs. `char[ ]` +as described below. Let's look at `char*` first: + +.Packing a string +------------------------------------------------------------------------------- + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s = "hello, world!"; + tn = tpl_map("s", &s); + tpl_pack(tn,0); /* copies "hello, world!" into the tpl */ + tpl_dump(tn,TPL_FILE,"string.tpl"); + tpl_free(tn); + } +------------------------------------------------------------------------------- + +The `char*` must point to a null-terminated string or be a `NULL` pointer. + +When deserializing (unpacking) a C string, space for it will be allocated +automatically, but you are responsible for freeing it (unless it is `NULL`): + +.Unpacking a string +------------------------------------------------------------------------------- + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s; + tn = tpl_map("s", &s); + tpl_load(tn,TPL_FILE,"string.tpl"); + tpl_unpack(tn,0); /* allocates space, points s to "hello, world!" */ + printf("unpacked %s\n", s); + free(s); /* our responsibility to free s */ + tpl_free(tn); + } +------------------------------------------------------------------------------- + +char* vs char[ ] +^^^^^^^^^^^^^^^^ +The `s` format character is only for use with `char*` types. In the example +above, `s` is a `char*`. If it had been a `char s[14]`, we would use the format +characters `c#` to pack or unpack it, as a fixed-length character array. (This +unpacks the characters "in-place", instead of into a dynamically allocated +buffer). Also, a fixed-length buffer described by `c#` need not be +null-terminated. + +Arrays of strings +^^^^^^^^^^^^^^^^^ +You can use fixed- or variable-length arrays of strings in tpl. An example of +packing a fixed-length two-dimensional array of strings is shown here. + + char *labels[2][3] = { {"one", "two", "three"}, + {"eins", "zwei", "drei" } }; + tpl_node *tn; + tn = tpl_map("s##", labels, 2, 3); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + +Later, when unpacking these strings, the programmer must remember to free them +one by one, after they are no longer needed. + + char *olabels[2][3]; + int i,j; + + tn = tpl_map("s##", olabels, 2, 3); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + for(i=0;i<2;i++) { + for(j=0;j<3;j++) { + printf("%s\n", olabels[i][j]); + free(olabels[i][j]); + } + } + +Binary buffers +~~~~~~~~~~~~~~ +Packing an arbitrary-length binary buffer (tpl format character `B`) makes use +of the `tpl_bin` structure. You must declare this structure and populate it +with the address and length of the binary buffer to be packed. + +.Packing a binary buffer +------------------------------------------------------------------------------- + #include "tpl.h" + #include + + int main() { + tpl_node *tn; + tpl_bin tb; + + /* we'll use a timeval as our guinea pig */ + struct timeval tv; + gettimeofday(&tv,NULL); + + tn = tpl_map( "B", &tb ); + tb.sz = sizeof(struct timeval); /* size of buffer to pack */ + tb.addr = &tv; /* address of buffer to pack */ + tpl_pack( tn, 0 ); + tpl_dump(tn, TPL_FILE, "bin.tpl"); + tpl_free(tn); + } +------------------------------------------------------------------------------- + +When you unpack a binary buffer, tpl will automatically allocate it, and will +populate your `tpl_bin` structure with its address and length. You are +responsible for eventually freeing the buffer. + +.Unpacking a binary buffer +------------------------------------------------------------------------------- + #include "tpl.h" + + int main() { + tpl_node *tn; + tpl_bin tb; + + tn = tpl_map( "B", &tb ); + tpl_load( tn, TPL_FILE, "bin.tpl" ); + tpl_unpack( tn, 0 ); + tpl_free(tn); + + printf("binary buffer of length %d at address %p\n", tb.sz, tb.addr); + free(tb.addr); /* our responsibility to free it */ + } +------------------------------------------------------------------------------- + +Structures +~~~~~~~~~~ +You can use tpl to pack and unpack structures, and arrays of structures. + + struct ci { + char c; + int i; + }; + struct ci s = {'a', 1}; + + tn = tpl_map("S(ci)", &s); /* pass structure address */ + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, "struct.tpl"); + tpl_free(tn); + +As shown, omit the individual arguments for the format characters inside the +parenthesis. The exception is for fixed-length arrays; when `S(...)` contains a +`#` character, its length argument is required: `tpl_map("S(f#i)", &s, 10);` + +When using the `S(...)` format, the only characters allowed inside the +parentheses are `iujvcsfIU#$()`. + +Structure arrays +^^^^^^^^^^^^^^^^ +Arrays of structures are the same as simple arrays. Fixed- or variable- length +arrays are supported. + + struct ci sa[100], one; + + tn = tpl_map("S(ci)#", sa, 100); /* fixed-length array of 100 structures */ + tn = tpl_map("A(S(ci))", &one); /* variable-length array (one at a time)*/ + +The differences between fixed- and variable-length arrays are explained in the +<> section. + +Nested structures +^^^^^^^^^^^^^^^^^ +When dealing with nested structures, the outermost structure uses the `S` format +character, and the inner nested structures use the `$` format. Only the +'outermost' structure's address is given to `tpl_map`. + + struct inner_t { + char a; + } + + struct outer_t { + char b; + struct inner_t i; + } + + tpl_node *tn; + struct outer_t outer = {'b', {'a'}}; + + tn = tpl_map("S(c$(c))", &outer); + +Structures can nest to any level. Currently tpl does not support fixed-length +array suffixes on inner structures. However the outermost structure can have a +length suffix even if it contains some nested structures. + +Linked lists +~~~~~~~~~~~~ +While tpl has no specific data type for a linked list, the technique for +packing them is illustrated here. First describe your list element as a +format string and then surround it with `A(...)` to describe it as +variable-length array. Then, using a temporary variable, iterate over each +list element, copying it to the temporary variable and packing it. + + struct element { + char c; + int i; + struct element *next; + } + + struct element *list, *i, tmp; + tpl_node *tn; + + /* add some elements to list.. (not shown)*/ + + tn = tpl_map("A(S(ci))", &tmp); + for(i = list; i != NULL; i=i->next) { + tmp = *i; + tpl_pack(tn, 1); + } + tpl_dump(tn,TPL_FILE,"list.tpl"); + tpl_free(tn); + +Unpacking is similar. The `for` loop is just replaced with: + + while( tpl_unpack(tn,1) > 0) { + struct element *newelt = malloc(sizeof(struct element)); + *newelt = tmp; + add_to_list(list, newelt); + } + +As you can see, tpl does not reinstate the whole list at once-- just one +element at a time. You need to link the elements manually. A future release of +tpl may support 'pointer swizzling' to make this easier. + +API +--- + +[[tpl_map]] +tpl_map +~~~~~~~ +The only way to create a tpl is to call `tpl_map()`. The first argument is the +<>. This is followed by a list of arguments as required by +the particular characters in the format string. E.g, + + tpl_node *tn; + int i; + tn = tpl_map( "A(i)", &i ); + +The function creates a mapping between the items in the format string and the C +program variables whose addresses are given. Later, the C variables will be read +or written as the tpl is packed or unpacked. + +This function returns a `tpl_node*` on success, or `NULL` on failure. + +[[tpl_pack]] +tpl_pack +~~~~~~~~ +The function `tpl_pack()` packs data into a tpl. The arguments to +`tpl_pack()` are a `tpl_node*` and an <>. + + tn = tpl_map("A(i)A(c)", &i, &c); + for(i=0; i<10; i++) tpl_pack(tn, 1); /* pack 0-9 into index 1 */ + for(c='a'; c<='z'; c++) tpl_pack(tn, 2); /* pack a-z into index 2 */ + +.Data is copied when packed +******************************************************************************** +Every call to `tpl_pack()` immediately 'copies' the data being packed. Thus +the program is free to immediately overwrite or re-use the packed variables. +******************************************************************************** + +Index number 0 +^^^^^^^^^^^^^^ +It is necessary to pack index number 0 only if the format string contains +characters that are not inside an `A(...)`, such as the `i` in the format string +`iA(c)`. + +Variable-length arrays +^^^^^^^^^^^^^^^^^^^^^^ + +Adding elements to an array ++++++++++++++++++++++++++++ +To add elements to a variable-length array, call `tpl_pack()` repeatedly. Each +call adds another element to the array. + +Zero-length arrays are ok ++++++++++++++++++++++++++ +It's perfectly acceptable to pack nothing into a variable-length array, +resulting in a zero-length array. + +[[nested_pack]] +Packing nested arrays ++++++++++++++++++++++ +In a format string containing a nested, variable-length array, such as +`A(A(s))`, the inner, child array should be packed prior to the parent array. + +When you pack a parent array, a "snapshot" of the current child array is placed +into the parent's new element. Packing a parent array also empties the child +array. This way, you can pack new data into the child, then pack the parent +again. This creates distinct parent elements which each contain distinct child +arrays. + +[TIP] +When dealing with nested arrays like `A(A(i))`, 'pack' them from the "inside +out" (child first), but 'unpack' them from the "outside in" (parent first). + +The example below creates a tpl having the format string `A(A(c))`. + +.Packing nested arrays +-------------------------------------------------------------------------------- +#include "tpl.h" + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + for(c='a'; c<'c'; c++) tpl_pack(tn,2); /* pack child (twice) */ + tpl_pack(tn, 1); /* pack parent */ + + for(c='1'; c<'4'; c++) tpl_pack(tn,2); /* pack child (three times) */ + tpl_pack(tn, 1); /* pack parent */ + + tpl_dump(tn, TPL_FILE, "test40.tpl"); + tpl_free(tn); +} +-------------------------------------------------------------------------------- + +This creates a nested array in which the parent has two elements: the first +element is the two-element nested array 'a', 'b'; and the second element is +the three-element nested array '1', '2', '3'. +The <> shows how this tpl is unpacked. + +[[tpl_dump]] +tpl_dump +~~~~~~~~ +After packing a tpl, `tpl_dump()` is used to write the tpl image to a file, +memory buffer or file descriptor. The corresponding modes are shown below. A +final mode is for querying the output size without actually performing the dump. + +[width="80%",options="header",cols="30^d,70m",grid="none"] +|================================================================================ +|Write to... |Usage +|file |tpl_dump(tn, TPL_FILE, "file.tpl" ); +|file descriptor |tpl_dump(tn, TPL_FD, 2); +|memory |tpl_dump(tn, TPL_MEM, &addr, &len ); +|caller's memory |tpl_dump(tn, TPL_MEM\|TPL_PREALLOCD, buf, sizeof(buf)); +|just get size |tpl_dump(tn, TPL_GETSIZE, &sz); +|================================================================================ + +The first argument is the `tpl_node*` and the second is one of these constants: + +`TPL_FILE`:: + Writes the tpl to a file whose name is given in the following argument. + The file is created with permissions 664 (`rw-rw-r--`) unless further + restricted by the process `umask`. +`TPL_FD`:: + Writes the tpl to the file descriptor given in the following argument. + The descriptor can be either blocking or non-blocking, but will busy-loop + if non-blocking and the contents cannot be written immediately. +`TPL_MEM`:: + Writes the tpl to a memory buffer. The following two arguments must be a + `void\*\*` and a `size_t*`. The function will allocate a buffer and store + its address and length into these locations. The caller is responsible to + `free()` the buffer when done using it. +`TPL_MEM|TPL_PREALLOCD`:: + Writes the tpl to a memory buffer that the caller has already allocated or + declared. The following two arguments must be a `void*` and a `size_t` + specifying the buffer address and size respectively. (If the buffer is of + insufficient size to receive the tpl dump, the function will return -1). + This mode can be useful in conjunction with `tpl_load` in `TPL_EXCESS_OK` + mode, as shown <> +`TPL_GETSIZE`:: + This special mode does not actually dump the tpl. Instead it places the size + that the dump 'would' require into the `size_t` pointed to by the + following argument. + +The return value is 0 on success, or -1 on error. + +The `tpl_dump()` function does not free the tpl. Use `tpl_free()` to release +the tpl's resources when done. + +[TIP] +.Back-to-back tpl images require no delimiter +If you want to store a series of tpl images, or transmit sequential tpl images +over a socket (perhaps as messages to another program), you can simply dump them +sequentially without needing to add any delimiter for the individual tpl images. +Tpl images are internally delimited, so `tpl_load` will read just one at a time +even if multiple images are contiguous. + +[[tpl_load]] +tpl_load +~~~~~~~~ +This API function reads a previously-dumped tpl image from a file, memory +buffer or file descriptor, and prepares it for subsequent unpacking. The format +string specified in the preceding call to `tpl_map()` will be cross-checked +for equality with the format string stored in the tpl image. + + tn = tpl_map( "A(i)", &i ); + tpl_load( tn, TPL_FILE, "demo.tpl" ); + +The first argument to `tpl_load()` is the `tpl_node*`. The second argument is +one of the constants: + +`TPL_FILE`:: + Loads the tpl from the file named in the following argument. It is also + possible to bitwise-OR this flag with `TPL_EXCESS_OK` as explained below. +`TPL_MEM`:: + Loads the tpl from a memory buffer. The following two arguments must be a + `void*` and a `size_t`, specifying the buffer address and size, + respectively. The caller must not free the memory buffer until after + freeing the tpl with `tpl_free()`. (If the caller wishes to hand over + responsibility for freeing the memory buffer, so that it's automatically + freed along with the tpl when `tpl_free()` is called, the constant + `TPL_UFREE` may be bitwise-OR'd with `TPL_MEM` to achieve this). + Furthermore, `TPL_MEM` may be bitwise-OR'd with `TPL_EXCESS_OK`, explained + below. +`TPL_FD`:: + Loads the tpl from the file descriptor given in the following argument. + The descriptor is read until one complete tpl image is loaded; no bytes + past the end of the tpl image will be read. The descriptor can be either + blocking or non-blocking, but will busy-loop if non-blocking and the + contents cannot be read immediately. + +During loading, the tpl image will be extensively checked for internal validity. + +This function returns 0 on success or -1 on error. + +[[excess_ok]] +`TPL_EXCESS_OK` +^^^^^^^^^^^^^^^ +When reading a tpl image from a file or memory (but not from a file descriptor) +the size of the file or memory buffer must exactly equal that of the tpl image +stored therein. In other words, no excess trailing data beyond the tpl image is +permitted. The bit flag `TPL_EXCESS_OK` can be OR'd with `TPL_MEM` or `TPL_FILE` +to relax this requirement. + +A situation where this flag can be useful is in conjunction with `tpl_dump` in +the `TPL_MEM|TPL_PREALLOCD` mode. In this example, the program does not concern +itself with the actual tpl size as long as `LEN` is sufficiently large. + + char buf[LEN]; /* will store and read tpl images here */ + ... + tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, LEN); + ... + tpl_load(tn, TPL_MEM|TPL_EXCESS_OK, buf, LEN); + + + + +[[tpl_unpack]] +tpl_unpack +~~~~~~~~~~ +The `tpl_unpack()` function unpacks data from the tpl. When data is unpacked, +it is copied to the C program variables originally specified in `tpl_map()`. +The first argument to `tpl_unpack` is the `tpl_node*` for the tpl and the +second argument is an <>. + + tn = tpl_map( "A(i)A(c)", &i, &c ); + tpl_load( tn, TPL_FILE, "nested.tpl" ); + while (tpl_unpack( tn, 1) > 0) printf("i is %d\n", i); /* unpack index 1 */ + while (tpl_unpack( tn, 2) > 0) printf("c is %c\n", c); /* unpack index 2 */ + +Index number 0 +^^^^^^^^^^^^^^ +It is necessary to unpack index number 0 only if the format string contains +characters that are not inside an `A(...)`, such as the `i` in the format string +`iA(c)`. + +Variable-length arrays +^^^^^^^^^^^^^^^^^^^^^^ + +Unpacking elements from an array +++++++++++++++++++++++++++++++++ +For variable-length arrays, each call to `tpl_unpack()` unpacks another element. +The return value can be used to tell when you're done: if it's positive, an +element was unpacked; if it's 0, nothing was unpacked because there are no more +elements. A negative retun value indicates an error (e.g. invalid index number). +In this document, we usually unpack variable-length arrays using a `while` loop: + + while( tpl_unpack( tn, 1 ) > 0 ) { + /* got another element */ + } + +Array length +++++++++++++ +When unpacking a variable-length array, it may be convenient to know ahead of +time how many elements will need to be unpacked. You can use `tpl_Alen()` to +get this number. + +[[nested_unpack]] +Unpacking nested arrays ++++++++++++++++++++++++ +In a format string containing a nested variable-length array such as `A(A(s))`, +unpack the outer, parent array before unpacking the child array. + +When you unpack a parent array, it prepares the child array for unpacking. +After unpacking the elements of the child array, the program can repeat the +process by unpacking another parent element, then the child elements, and so on. +The example below unpacks a tpl having the format string `A(A(c))`. + +.Unpacking nested arrays +-------------------------------------------------------------------------------- +#include "tpl.h" +#include + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + tpl_load(tn, TPL_FILE, "test40.tpl"); + while (tpl_unpack(tn,1) > 0) { + while (tpl_unpack(tn,2) > 0) printf("%c ",c); + printf("\n"); + } + tpl_free(tn); +} +-------------------------------------------------------------------------------- + +The file `test40.tpl` is from the <>. When +run, this program prints: + + a b + 1 2 3 + +[[tpl_free]] +tpl_free +~~~~~~~~ +The final step for any tpl is to release it using `tpl_free()`. Its only +argument is the the `tpl_node*` to free. + + tpl_free( tn ); + +This function does not return a value (it is `void`). + +[[tpl_alen]] +tpl_Alen +~~~~~~~~ +This function takes a `tpl_node*` and an index number and returns an `int` +specifying the number of elements in the variable-length array. + + num_elements = tpl_Alen(tn, index); + +This is mainly useful for programs that unpack data and need to know ahead of +time the number of elements that will need to be unpacked. (It returns the +current number of elements; it will decrease as elements are unpacked). + +[[tpl_peek]] +tpl_peek +~~~~~~~~ +This function peeks into a file or a memory buffer containing a tpl image and +and returns a copy of its format string. It can also peek at the lengths of +any fixed-length arrays in the format string, or it can also peek into the data +stored in the tpl. + +Format peek +^^^^^^^^^^^ +The format string can be obtained +like this: + + fmt = tpl_peek(TPL_FILE, "file.tpl"); + fmt = tpl_peek(TPL_MEM, addr, sz); + +On success, a copy of the format string is returned. The caller must eventually +free it. On error, such as a non-existent file, or an invalid tpl image, it +returns `NULL`. + +Array length peek +^^^^^^^^^^^^^^^^^ +The lengths of all fixed-length arrays in the format string can be queried using +the `TPL_FXLENS` mode. It provides the number of such fixed-length arrays and +their lengths. If the former is non-zero, the caller must free the latter array +when finished. The format string itself must also be freed. + + uint32_t num_fxlens, *fxlens, j; + fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens); + if (fmt) { + printf("format %s, num_fxlens %u\n", fmt, num_fxlens); + for(j=0; j 0) free(fxlens); + free(fmt); + } + +The `TPL_FXLENS` mode is mutually exclusive with `TPL_DATAPEEK`. + + +Data peek +^^^^^^^^^ +To peek into the data, additional arguments are used. This is a quick +alternative to mapping, loading and unpacking the tpl, but peeking is limited +to the data in index 0. In other words, no peeking into `A(...)` types. +Suppose the tpl image in `file.tpl` has the format string `siA(i)`. Then the +index 0 format characters are `si`. This is how to peek at their content: + + char *s; + int i; + fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "si", &s, &i); + +Now `s`, `i`, and `fmt` have been populated with data. The caller must +eventually free `fmt` and `s` because they are allocated strings. +Of course, it works with `TPL_MEM` as well as `TPL_FILE`. Notice that +`TPL_DATAPEEK` was OR'd with the mode. You can also specify 'any leading +portion' of the index 0 format if you don't want to peek at the whole thing: + + fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "s", &s); + +The `TPL_DATAPEEK` mode is mutually exclusive with `TPL_FXLENS`. + +Structure peek +++++++++++++++ +Lastly you can peek into `S(...)` structures in index 0, but omit the +surrounding `S(...)` in the format, and specify an argument to receive +each structure member individually. You can specify any leading portion +of the structure format. For example if `struct.tpl` has the format string +`S(si)`, you can peek at its data in these ways: + + fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "s", &s); + fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "si", &s, &i); + +[[tpl_jot]] +tpl_jot +~~~~~~~ +This is a quick shortcut for generating a tpl. It can be used instead of the +usual "map, pack, dump, and free" lifecycle. With `tpl_jot` all those steps are +handled for you. It only works for simple formats-- namely, those without +`A(...)` in their format string. Here is how it is used: + + char *hello = "hello", *world = "world"; + tpl_jot( TPL_FILE, "file.tpl", "ss", &hello, &world); + +It supports the three standard modes, `TPL_FILE`, `TPL_FD` and `TPL_MEM`. +It returns -1 on failure (such as a bad format string or error writing the +file) or 0 on success. + +[[hooks]] +tpl_hook +~~~~~~~~ +Most users will just leave these hooks at their default values. You can change +these hook values if you want to modify tpl's internal memory management and +error reporting behavior. + +A global structure called `tpl_hook` encapsulates the hooks. A program can +reconfigure any hook by specifying an alternative function whose prototype +matches the default. For example: + + #include "tpl.h" + extern tpl_hook_t tpl_hook; + + int main() { + tpl_hook.oops = printf; + ... + } + +.Configurable hooks +[width="90%",options="header",cols="m,d,m",grid="none"] +|================================================================================ +|Hook |Description | Default +|tpl_hook.oops |log error messages | tpl_oops +|tpl_hook.malloc |allocate memory | malloc +|tpl_hook.realloc |reallocate memory | realloc +|tpl_hook.free |free memory | free +|tpl_hook.fatal |log fatal message and exit | tpl_fatal +|tpl_hook.gather_max |tpl_gather max image size | 0 (unlimited) +|================================================================================ + +The oops hook +^^^^^^^^^^^^^ +The `oops` has the same prototype as `printf`. The built-in default oops +handling function writes the error message to `stderr`. + +The fatal hook +^^^^^^^^^^^^^^ +The fatal hook is invoked when a tpl function cannot continue because of an out- +of-memory condition or some other usage violation or inconsistency. It has this +prototype: + + void fatal_fcn(char *fmt, ...); + +The `fatal` hook must not return. It must either exit, 'or' if the program needs +to handle the failure and keep executing, `setjmp` and `longjmp` can be used. +The default behavior is to `exit(-1)`. + +.Using longjmp in a fatal error handler +-------------------------------------------------------------------------------- +#include +#include +#include +#include "tpl.h" + +jmp_buf env; +extern tpl_hook_t tpl_hook; + +void catch_fatal(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + longjmp(env,-1); /* return to setjmp point */ +} + +int main() { + int err; + tpl_node *tn; + tpl_hook.fatal = catch_fatal; /* install fatal handler */ + + err = setjmp(env); /* on error, control will return here */ + if (err) { + printf("caught error!\n"); + return -1; + } + + tn = tpl_map("@"); /* generate a fatal error */ + printf("program ending, without error\n"); + return 0; +} +-------------------------------------------------------------------------------- + +This example is included in `tests/test123.c`. When run, this program prints: + + unsupported option @ + failed to parse @ + caught error! + + +tpl_gather +~~~~~~~~~~ + +.Most programs don't need this +******************************************************************************** +Normally, `tpl_load()` is used to read a tpl image having an expected format +string. A more generic operation is to acquire a tpl image whose format string is +unknown. E.g., a generic message-receiving function might gather tpl images of +varying format and route them to their final destination. This is the purpose of +`tpl_gather`. It produces a memory buffer containing one tpl image. If there +are multiple contiguous images in the input, it gathers exactly one image at a +time. +******************************************************************************** + +The prototype for this function is: + + int tpl_gather( int mode, ...); + +The `mode` argument is one of three constants listed below, which must be +followed by the mode-specific required arguments: + + TPL_GATHER_BLOCKING, int fd, void **img, size_t *sz + TPL_GATHER_NONBLOCKING, int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data + TPL_GATHER_MEM, void *addr, size_t sz, tpl_gather_t **gs, tpl_gather_cb *cb, void *data + +[NOTE] +.`tpl_hook.gather_max` +All modes honor `tpl_hook.gather_max`, specifying the maximum byte size for a +tpl image to be gathered (the default is unlimited, signified by 0). If a source +attempts to send a tpl image larger than this maximum, whatever partial image +has been read will be discarded, and no further reading will take place; in this +case `tpl_gather` will return a negative (error) value to inform the caller that +it should stop gathering from this source, and close the originating file +descriptor if there is one. (The whole idea is to prevent untrusted sources from +sending extremely large tpl images which would consume too much memory.) + + +`TPL_GATHER_BLOCKING` +^^^^^^^^^^^^^^^^^^^ +In this mode, `tpl_gather` blocks while reading file descriptor `fd` until one +complete tpl image is read. No bytes past the end of the tpl image will be read. +The address of the buffer containing the image is returned in `img` and its size +is placed in `sz`. The caller is responsible for eventually freeing the buffer. +The function returns 1 on success, 0 on end-of-file, or a negative number on +error. + +`TPL_GATHER_NONBLOCKING` +^^^^^^^^^^^^^^^^^^^^^^ +This mode is for non-blocking, event-driven programs that implement their +own file descriptor readability testing using `select()` or the like. In this +mode, tpl images are gathered in chunks as data becomes readable. Whenever a +full tpl image has been gathered, it invokes a caller-specified callback to do +something with the image. The arguments are the file descriptor `fd` which the +caller has determined to be readable and which must be in non-blocking mode, a +pointer to a file-descriptor-specific handle which the caller has declared +(explained below); a callback to invoke when a tpl image has been read; and an +opaque pointer that will passed to the callback. + +For each file descriptor on which `tpl_gather` will be used, the caller must +declare a `tpl_gather_t*` and initialize it to `NULL`. Thereafter it will be +used internally by `tpl_gather` whenever data is readable on the descriptor. + +The callback will only be invoked whenever `tpl_gather()` has accumulated one +complete tpl image. It must have this prototype: + + int (tpl_gather_cb)(void *img, size_t sz, void *data); + +The callback can do anything with the tpl image but it must not free it. It can +be copied if it needs to survive past the callback's return. The callback should +return 0 under normal circumstances, or a negative number to abort; that is, +returning a negative number causes `tpl_gather` itself to discard any remaining +full or partial tpl images that have been read, and to return a negative number +(-4 in particular) to signal its caller to close the file descriptor. + +The return value of `tpl_gather()` is negative if an error occured or 0 if a +normal EOF was encountered-- both cases require that the caller close the file +descriptor (and stop monitoring it for readability, obviously). If the return +value is positive, the function succeeded in gathering whatever data was +currently readable, which may have been a partial tpl image, or one or more +complete images. + +Typical Usage ++++++++++++++ +The program will have established a file descriptor in non-blocking mode and +be monitoring it for readability, using `select()`. Whenever it's readable, the +program calls `tpl_gather()`. In skeletal terms: + + tpl_gather_t *gt=NULL; + int rc; + + void fd_is_readable(int fd) { + rc = tpl_gather( TPL_GATHER_NONBLOCKING, fd, >, callback, NULL ); + if (rc <= 0) { + close(fd); /* got eof or fatal */ + stop_watching_fd(fd); + } + } + + int callback( void *img, size_t sz, void *data ) { + printf("got a tpl image\n"); /* do something with img. do not free it. */ + return 0; /* normal (no error) */ + } + +`TPL_GATHER_MEM` +^^^^^^^^^^^^^^ +This mode is identical to `TPL_GATHER_NONBLOCKING` except that it gathers from a +memory buffer instead of from a file descriptor. In other words, if some other +layer of code-- say, a decryption function (that is decrypting fixed-size +blocks) produces tpl fragments one-by-one, this mode can be used to reconstitute +the tpl images and invoke the callback for each one. Its parameters are the same +as for the `TPL_GATHER_NONBLOCKING` mode except that instead of a file +descriptor, it takes a buffer address and size. The return values are also the +same as for `TPL_GATHER_NONBLOCKING` noting of course there is no file +descriptor to close on a non-positive return value. + + +// vim: set tw=80 wm=2 syntax=asciidoc: + diff --git a/lang/perl/Tpl.pm b/lang/perl/Tpl.pm new file mode 100644 index 0000000..b6c4104 --- /dev/null +++ b/lang/perl/Tpl.pm @@ -0,0 +1,475 @@ +package Tpl; + +# Copyright (c) 2005-2007, Troy Hanson http://tpl.sourceforge.net +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use strict; +use warnings; +use Config; # to get the size of "double" on this platform + +use bytes; # always use byte (not unicode char) offsets w/tpl images + +our $VERSION = 1.1; + +# tpl object is a reference to a hash with these keys: +# +# A(0): +# ... : +# A(n): +# +# where each A(i) refers to an A node, except A(0) is the root node. +# +# For each hash key (A node or root node), the value of that key is +# a list reference. The members are of the list are the node's children. +# They're represented as "Ai" (for A nodes) where i is a positive integer; +# for non-A nodes the representation is [type,addr] e.g. [ "i", \$some_integer] +# +# For example, +# Tpl->map("iA(ib)", \$x, \$y, \$z); +# returns a tpl object which is a reference to a hash with these keys/values: +# +# $self->{A0} = [ [ "i", \$x ], "A1" ]; +# $self->{A1} = [ [ "i", \$y ], [ "b", \$z ] ]; +# +# Now if A1 (that is, the "A(ib)" node) is packed, the tpl object acquires +# another hash key/value: +# $self->{P1} = [ $binary_int, $binary_byte ]; +# and repeated calls to pack A1 append further $binary elements. +# +sub tpl_map { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $fmt = shift; + my @astack = (0); # stack of current A node's lineage in tpl tree + my $a_count=0; # running count of A's, thus an index of them + my $self = {}; # populate below + my ($lparen_level,$expect_lparen,$in_structure)=(0,0,0); + for (my $i=0; $i < length $fmt; $i++) { + my $c = substr($fmt,$i,1); + if ($c eq 'A') { + $a_count++; + push @{ $self->{"A" . $astack[-1]} }, "A$a_count"; + push @astack, $a_count; + $expect_lparen=1; + } elsif ($c eq '(') { + die "invalid format $fmt" unless $expect_lparen; + $expect_lparen=0; + $lparen_level++; + } elsif ($c eq ')') { + $lparen_level--; + die "invalid format $fmt" if $lparen_level < 0; + die "invalid format $fmt" if substr($fmt,$i-1,1) eq '('; + if ($in_structure && ($in_structure-1 == $lparen_level)) { + $in_structure=0; + } else { + pop @astack; # rparen ends A() type, not S() type + } + } elsif ($c eq 'S') { + # in perl we just parse and ignore the S() construct + $expect_lparen=1; + $in_structure=1+$lparen_level; # so we can tell where S fmt ends + } elsif ($c =~ /^(i|u|B|s|c|f|I|U)$/) { + die "invalid format $fmt" if $expect_lparen; + my $r = shift; + die "no reference for $c (position $i of $fmt)" unless ref($r); + if (($c eq "f") and ($Config{doublesize} != 8)) { + die "double not 8 bytes on this platform"; + } + if (($c =~ /(U|I)/) and not defined ($Config{use64bitint})) { + die "Tpl.pm: this 32-bit Perl can't pack/unpack 64-bit I/U integers\n"; + } + push @{ $self->{"A" . $astack[-1]} }, [ $c , $r ]; + } elsif ($c eq "#") { + # test for previous iucfIU + die "unallowed length modifer" unless $self->{"A" . $astack[-1]}->[-1]->[0] =~ /^(i|u|c|I|U|f)$/; + my $n = shift; + die "non-numeric # length modifer" unless $n =~ /^\d+$/; + push @{ $self->{"A" . $astack[-1]}->[-1] }, $n; + push @{ $self->{"#"}}, $n; # master array of octothorpe lengths + } else { + die "invalid character $c in format $fmt"; + } + } + die "invalid format $fmt" if $lparen_level != 0; + $self->{fmt} = $fmt; + bless $self; + return $self; +} + +sub tpl_format { + my $self = shift; + return $self->{fmt}; +} + +sub tpl_pack { + my $self = shift; + my $i = shift; + die "invalid index" unless defined $self->{"A$i"}; + die "tpl for unpacking only" if defined $self->{"loaded"}; + $self->{"packed"}++; + $self->{"P$i"} = undef if $i == 0; # node 0 doesn't accumulate + my @bb; + foreach my $node (@{ $self->{"A$i"} }) { + if (ref($node)) { + my ($type,$addr,$fxlen) = @{ $node }; + if (defined $fxlen) { # octothorpic array + push @bb, CORE::pack("l$fxlen",@$addr) if $type eq "i"; # int + push @bb, CORE::pack("L$fxlen",@$addr) if $type eq "u"; # uint + push @bb, CORE::pack("C$fxlen",@$addr) if $type eq "c"; # byte + push @bb, CORE::pack("d$fxlen",@$addr) if $type eq "f"; # double + push @bb, CORE::pack("q$fxlen",@$addr) if $type eq "I"; # int64 + push @bb, CORE::pack("Q$fxlen",@$addr) if $type eq "U"; # uint64 + } else { + # non-octothorpic singleton + push @bb, CORE::pack("l",$$addr) if $type eq "i"; # int + push @bb, CORE::pack("L",$$addr) if $type eq "u"; # uint + push @bb, CORE::pack("C",$$addr) if $type eq "c"; # byte + push @bb, CORE::pack("d",$$addr) if $type eq "f"; # double (8 byte) + push @bb, CORE::pack("q",$$addr) if $type eq "I"; # int64 + push @bb, CORE::pack("Q",$$addr) if $type eq "U"; # uint64 + if ($type =~ /^(B|s)$/) { # string/binary + push @bb, CORE::pack("L", length($$addr)); + push @bb, CORE::pack("a*", $$addr); + } + } + } elsif ($node =~ /^A(\d+)$/) { + # encode array length (int) and the array data into one scalar + my $alen = pack("l", scalar @{ $self->{"P$1"} or [] }); + my $abod = (join "", @{ $self->{"P$1"} or [] }); + push @bb, $alen . $abod; + $self->{"P$1"} = undef; + } else { + die "internal error; invalid node symbol $node"; + } + } + push @{ $self->{"P$i"} }, (join "", @bb); +} + +sub big_endian { + return (CORE::unpack("C", CORE::pack("L",1)) == 1) ? 0 : 1; +} + +sub tpl_dump { + my $self = shift; + my $filename = shift; + + $self->tpl_pack(0) if not defined $self->{"P0"}; + my $format = $self->tpl_format; + my $octothorpe_lens = CORE::pack("L*", @{ $self->{"#"} or [] }); + my $data = (join "", @{ $self->{"P0"} }); + my $ov_len = length($format) + 1 + length($octothorpe_lens) + length($data) + 8; + my $flags = big_endian() ? 1 : 0; + my $preamble = CORE::pack("CLZ*", $flags, $ov_len, $format); + my $tpl = "tpl" . $preamble . $octothorpe_lens . $data; + return $tpl unless $filename; + + # here for file output + open TPL, ">$filename" or die "can't open $filename: $!"; + print TPL $tpl; + close TPL; +} + +sub tpl_peek { + my $invocant = shift; + my $class = ref($invocant) || $invocant; + my $tplhandle = shift; + my $tpl; + + if (ref($tplhandle)) { + $tpl = $$tplhandle; + } else { + open TPL, "<$tplhandle" or die "can't open $tplhandle: $!"; + undef $/; # slurp + $tpl = ; + close TPL; + } + die "invalid tpl file" unless ($tpl =~ /^tpl/); + return (unpack("Z*", substr($tpl,8))); +} + +sub tpl_load { + my $self = shift; + my $tplhandle = shift; + + die "tpl for packing only" if $self->{"packed"}; + die "tpl reloading not supported" if $self->{"loaded"}; + + # read tpl image from file or was it passed directly via ref? + my $tpl; + if (ref($tplhandle)) { + $tpl = $$tplhandle; + } else { + open TPL, "<$tplhandle" or die "can't open $tplhandle: $!"; + undef $/; # slurp + $tpl = ; + close TPL; + } + + $self->{"TI"} = $tpl; + $self->{"TL"} = length $tpl; + # verify preamble + die "invalid image -1" unless length($tpl) >= 9; + die "invalid image -2" unless $tpl =~ /^tpl/; + my $flags = CORE::unpack("C", substr($tpl,3,1)); + $self->{"xendian"} = 1 if (big_endian() != ($flags & 1)); + $self->{"UF"} = ($flags & 1) ? "N" : "V"; + my $ov_len = CORE::unpack($self->{"UF"}, substr($tpl,4,4)); + die "invalid image -3" unless $ov_len == length($tpl); + my $format = CORE::unpack("Z*", substr($tpl,8)); + die "format mismatch" unless $format eq $self->tpl_format(); + my @octothorpe_lens = @{ $self->{"#"} or [] }; + my $ol = 8 + length($format) + 1; # start of octothorpe lengths + for (my $i=0; $i < (scalar @octothorpe_lens); $i++) { + my $len = CORE::unpack($self->{"UF"}, substr($tpl,$ol,4)); + my $olen = $octothorpe_lens[$i]; + die "fixed-length array size mismatch" unless $olen == $len; + $ol += 4; + } + my $dv = $ol; # start of packed data + my $len = $self->serlen("A0",$dv); + die "invalid image -4" if $len == -1; + die "invalid image -5" if (length($tpl) != $len + $dv); + $self->{"C0"} = $dv; + $self->{"loaded"} = 1; + $self->unpackA0; # prepare root child nodes for use +} + +# byte reverse a word (any length) +sub reversi { + my $word = shift; + my @w = split //, $word; + my $r = join "", (reverse @w); + return $r; +} + +# +# while unpacking, the object has these keys in its hash: +# C0 +# C1 +# ... +# C +# These are indices (into the tpl image $self->{"TI"}) from which node n +# is being unpacked. I.e. as array elements of node n are unpacked, C +# advances through the tpl image. +# +# Similarly, elements +# N1 +# N2 +# ... +# N +# refer to the remaining array count for node n. +# +sub tpl_unpack { + my $self = shift; + my $n = shift; + my $ax = "A$n"; + my $cx = "C$n"; + my $nx = "N$n"; + my $rc; + + die "tpl for packing only" if $self->{"packed"}; + die "tpl not loaded" unless $self->{"loaded"}; + + # decrement count for non root array nodes + if ($n > 0) { + return 0 if $self->{$nx} <= 0; + $rc = $self->{$nx}--; + } + + for my $c (@{ $self->{$ax} }) { + if (ref($c)) { + my ($type,$addr,$fxlen) = @$c; + if (defined $fxlen) { # octothorpic unpack + @{ $addr } = (); # empty existing list before pushing elements + for(my $i=0; $i < $fxlen; $i++) { + if ($type eq "u") { # uint + push @{ $addr }, CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$self->{$cx},4)); + $self->{$cx} += 4; + } elsif ($type eq "i") { #int (see note below re:signed int) + my $intbytes = substr($self->{"TI"},$self->{$cx},4); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + push @{ $addr }, CORE::unpack("l", $intbytes); + $self->{$cx} += 4; + } elsif ($type eq "c") { # byte + push @{ $addr }, CORE::unpack("C", + substr($self->{"TI"},$self->{$cx},1)); + $self->{$cx} += 1; + } elsif ($type eq "f") { # double + my $double_bytes = substr($self->{"TI"},$self->{$cx},8); + $double_bytes = reversi($double_bytes) if $self->{"xendian"}; + push @{ $addr }, CORE::unpack("d", $double_bytes ); + $self->{$cx} += 8; + } elsif ($type eq "I") { #int64 + my $intbytes = substr($self->{"TI"},$self->{$cx},8); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + push @{ $addr }, CORE::unpack("q", $intbytes); + $self->{$cx} += 8; + } elsif ($type eq "U") { #uint64 + my $intbytes = substr($self->{"TI"},$self->{$cx},8); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + push @{ $addr }, CORE::unpack("Q", $intbytes); + $self->{$cx} += 8; + } + } + } else { + # non-octothorpe (singleton) + if ($type eq "u") { # uint + ${$addr} = CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$self->{$cx},4)); + $self->{$cx} += 4; + } elsif ($type eq "i") { # int + # while perl's N or V conversions unpack an unsigned + # long from either big or little endian format + # respectively, when it comes to *signed* int, perl + # only has 'l' (which assumes native endianness). + # So we have to manually reverse the bytes in a + # cross-endian 'int' unpacking scenario. + my $intbytes = substr($self->{"TI"},$self->{$cx},4); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + ${$addr} = CORE::unpack("l", $intbytes); + $self->{$cx} += 4; + } elsif ($type eq 'c') { # byte + ${$c->[1]} = CORE::unpack("C", + substr($self->{"TI"},$self->{$cx},1)); + $self->{$cx} += 1; + } elsif ($type eq 'f') { # double + ${$addr} = CORE::unpack("d", + substr($self->{"TI"},$self->{$cx},8)); + $self->{$cx} += 8; + } elsif ($type =~ /^(B|s)$/) { # string/binary + my $slen = CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$self->{$cx},4)); + $self->{$cx} += 4; + ${$addr} = CORE::unpack("a$slen", + substr($self->{"TI"},$self->{$cx},$slen)); + $self->{$cx} += $slen; + } elsif ($type eq "I") { # int64 + my $intbytes = substr($self->{"TI"},$self->{$cx},8); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + ${$addr} = CORE::unpack("q", $intbytes); + $self->{$cx} += 8; + } elsif ($type eq "U") { # uint64 + my $intbytes = substr($self->{"TI"},$self->{$cx},8); + $intbytes = reversi($intbytes) if $self->{"xendian"}; + ${$addr} = CORE::unpack("Q", $intbytes); + $self->{$cx} += 8; + } else { die "internal error"; } + } + } elsif ($c =~ /^A(\d+)$/) { + my $alen = $self->serlen($c,$self->{$cx}); + $self->{"N$1"} = CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$self->{$cx},4)); # get array count + $self->{"C$1"} = $self->{$cx} + 4; # set array node's data start + $self->{$cx} += $alen; # step over array node's data + } else { die "internal error"; } + } + + return $rc; +} + +# specialized function to prepare root's child A nodes for initial use +sub unpackA0 { + my $self = shift; + my $ax = "A0"; + my $cx = "C0"; + my $c0 = $self->{$cx}; + + for my $c (@{ $self->{$ax} }) { + next if ref($c); # skip non-A nodes + if ($c =~ /^A(\d+)$/) { + my $alen = $self->serlen($c,$c0); + $self->{"N$1"} = CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$c0,4)); # get array count + $self->{"C$1"} = $c0 + 4; # set array node's data start + $c0 += $alen; # step over array node's data + } else { die "internal error"; } + } +} + +# ascertain serialized length of given node by walking +sub serlen { + my $self = shift; + my $ax = shift; + my $dv = shift; + + my $len = 0; + + my $num; + if ($ax eq "A0") { + $num = 1; + } else { + return -1 unless $self->{"TL"} >= $dv + 4; + $num = CORE::unpack($self->{"UF"},substr($self->{"TI"},$dv,4)); + $dv += 4; + $len += 4; + } + + while ($num-- > 0) { + for my $c (@{ $self->{$ax} }) { + if (ref($c)) { + my $n = 1; + $n = $c->[2] if (@$c > 2); # octothorpic array length + if ($c->[0] =~ /^(i|u)$/) { # int/uint + return -1 unless $self->{"TL"} >= $dv + 4*$n; + $len += 4*$n; + $dv += 4*$n; + } elsif ($c->[0] eq "c") { # byte + return -1 unless $self->{"TL"} >= $dv + 1*$n; + $len += 1*$n; + $dv += 1*$n; + } elsif ($c->[0] eq "f") { # double + return -1 unless $self->{"TL"} >= $dv + 8*$n; + $len += 8*$n; + $dv += 8*$n; + } elsif ($c->[0] =~ /(I|U)/) { # int64/uint64 + return -1 unless $self->{"TL"} >= $dv + 8*$n; + $len += 8*$n; + $dv += 8*$n; + } elsif ($c->[0] =~ /^(B|s)$/) { # string/binary + return -1 unless $self->{"TL"} >= $dv + 4; + my $slen = CORE::unpack($self->{"UF"}, + substr($self->{"TI"},$dv,4)); + $len += 4; + $dv += 4; + return -1 unless $self->{"TL"} >= $dv + $slen; + $len += $slen; + $dv += $slen; + } else { die "internal error" } + } elsif ($c =~ /^A/) { + my $alen = $self->serlen($c,$dv); + return -1 if $alen == -1; + $dv += $alen; + $len += $alen; + } else { die "internal error"; } + } + } + return $len; +} + +1 diff --git a/lang/perl/tests/Makefile b/lang/perl/tests/Makefile new file mode 100644 index 0000000..c2dd552 --- /dev/null +++ b/lang/perl/tests/Makefile @@ -0,0 +1,9 @@ +all: run_tests + +run_tests: + perl ./do_tests + +.PHONY: clean + +clean: + rm -f test*.out diff --git a/lang/perl/tests/README b/lang/perl/tests/README new file mode 100644 index 0000000..8f10443 --- /dev/null +++ b/lang/perl/tests/README @@ -0,0 +1,29 @@ +Run 'make' to run the tests. + +Run 'make clean' to clean up the temporary files. + +Description of tests +============================================================================= +test1 pack A(i) to file, unpack +test2 pack A(i) to memory, unpack +test3 pack A(i) to memory, pipe through tplxml (to make XML) +test4 pack A(i) to file, convert to XML, convert back to tpl (using tplxml) +test5 pack A(b) to file, unpack +test6 pack A(b) to memory, pipe through tplxml (to make XML) +test7 pack A(b) to file, convert to XML, convert back to tpl (using tplxml) +test8 pack A(s) (has embedded &, < and > in strings to test quoting in XML) +test9 pack A(u) to file, unpack +test10 pack A(u) to memory, convert via tplxml +test11 pack format B using a four-byte binary buffer, unpack and print +test12 pack A(d) to file, unpack +test13 pack A(d) to memory, convert via tplxml +test14 unpack big-endian i (-2) (on little-endian machine, tests reversi) +test15 unpack little-endian i (-3) (on big-endian machine, tests reversi) +test16 pack to mem format B using a four-byte binary buffer, unpack and print +test17 pack and unpack S(ic) +test18 pack and unpack i# +test19 pack and unpack i#i# +test20 pack A(S(ci#)) to file, convert to XML, then back to tpl (cf test81.c) +test21 Tpl->tpl_peek in-memory image +test22 Tpl->tpl_peek file image +test23 test I/U (only succeeds on 64-bit perl) diff --git a/lang/perl/tests/client.pl b/lang/perl/tests/client.pl new file mode 100755 index 0000000..4d471e7 --- /dev/null +++ b/lang/perl/tests/client.pl @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use IO::Socket::INET; +use lib ".."; +use Tpl; + +our $port = 2000; + +# construct tpl +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +$tpl->tpl_pack(1) while ($i=shift @ARGV); +my $request = $tpl->tpl_dump(); + +# send to server, get response +my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect"; +print $socket $request; +shutdown($socket,1); # done writing (half-close) +undef $/; +my $response = <$socket>; # get reply (slurp) + +# decode response (or print error) +my $total; +my $tpl2 = Tpl->tpl_map("i", \$total); +eval { $tpl2->tpl_load(\$response); }; +die "invalid response\n" if $@; +$tpl2->tpl_unpack(0); +print "total is $total\n"; diff --git a/lang/perl/tests/do_tests b/lang/perl/tests/do_tests new file mode 100755 index 0000000..3ca08bb --- /dev/null +++ b/lang/perl/tests/do_tests @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "test*[0-9]") { + push @tests, $_ if -e "$_.ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out`; + `diff $test.out $test.ans`; + print "$test failed\n" if $?; + $num_failed++ if $?; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; diff --git a/lang/perl/tests/server.pl b/lang/perl/tests/server.pl new file mode 100755 index 0000000..4faa9c6 --- /dev/null +++ b/lang/perl/tests/server.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use IO::Socket::INET; +use lib ".."; +use Tpl; + +$SIG{CHLD} = "IGNORE"; # don't create zombies + +our $port = 2000; + +sub handle_client { + my $client = shift; + + undef $/; + my $request = <$client>; # get request (slurp) + + # read input array, and calculate total + my ($i,$total); + my $tpl = Tpl->tpl_map("A(i)", \$i); + eval { $tpl->tpl_load(\$request); }; + die "received invalid tpl" if $@; + $total += $i while $tpl->tpl_unpack(1) > 0; + + # formulate response and send + my $tpl2 = Tpl->tpl_map("i", \$total); + $tpl2->tpl_pack(0); + my $response = $tpl2->tpl_dump(); + print $client $response; + close $client; +} + +my $server = IO::Socket::INET->new(LocalPort => $port, + Type => SOCK_STREAM, + Reuse => 1, + Listen => 10 ) + or die "Can't listen on port $port: $!\n"; + +while (1) { + my $client = $server->accept(); + next unless $client; + # new connection + my $pid = fork; + die "can't fork: $!\n" unless defined $pid; + if ($pid > 0) { + #p arent + close $client; + } elsif ($pid == 0) { + # child + handle_client($client); + exit(0); + } +} +close ($server); diff --git a/lang/perl/tests/test1 b/lang/perl/tests/test1 new file mode 100755 index 0000000..f559e4b --- /dev/null +++ b/lang/perl/tests/test1 @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + +my $j; +my $tpl2 = Tpl->tpl_map("A(i)",\$j); +$tpl2->tpl_load($tmp1); +while($tpl2->tpl_unpack(1) > 0) { print "$j\n" } + diff --git a/lang/perl/tests/test1.ans b/lang/perl/tests/test1.ans new file mode 100644 index 0000000..8b1acc1 --- /dev/null +++ b/lang/perl/tests/test1.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/lang/perl/tests/test10 b/lang/perl/tests/test10 new file mode 100755 index 0000000..49be955 --- /dev/null +++ b/lang/perl/tests/test10 @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(u)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +open TPLXML, "|../tplxml" or die "can't open tplxml: $!"; +print TPLXML $img; +close TPLXML; diff --git a/lang/perl/tests/test10.ans b/lang/perl/tests/test10.ans new file mode 100644 index 0000000..4832472 --- /dev/null +++ b/lang/perl/tests/test10.ans @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + ]> + + + + 0 + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 9 + + + diff --git a/lang/perl/tests/test11 b/lang/perl/tests/test11 new file mode 100755 index 0000000..1198015 --- /dev/null +++ b/lang/perl/tests/test11 @@ -0,0 +1,24 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my $i; +my $tpl = Tpl->tpl_map("B",\$i); +$i = pack("CCCC", 0xA, 0xB, 0xC, 0xD); +$tpl->tpl_pack(0); +$tpl->tpl_dump($tmp1); + + +$tpl = Tpl->tpl_map("B",\$i); +$tpl->tpl_load($tmp1); +$tpl->tpl_unpack(0); +print "$i\n"; diff --git a/lang/perl/tests/test11.ans b/lang/perl/tests/test11.ans new file mode 100644 index 0000000..b04dd2e --- /dev/null +++ b/lang/perl/tests/test11.ans @@ -0,0 +1,2 @@ + + diff --git a/lang/perl/tests/test12 b/lang/perl/tests/test12 new file mode 100755 index 0000000..4dfed01 --- /dev/null +++ b/lang/perl/tests/test12 @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(f)",\$i); +for($i=0; $i<10.0; $i+=2/3.0) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + +my $j; +my $tpl2 = Tpl->tpl_map("A(f)",\$j); +$tpl2->tpl_load($tmp1); +while($tpl2->tpl_unpack(1) > 0) { printf("%.6f\n", $j); } + diff --git a/lang/perl/tests/test12.ans b/lang/perl/tests/test12.ans new file mode 100644 index 0000000..3849b4f --- /dev/null +++ b/lang/perl/tests/test12.ans @@ -0,0 +1,16 @@ +0.000000 +0.666667 +1.333333 +2.000000 +2.666667 +3.333333 +4.000000 +4.666667 +5.333333 +6.000000 +6.666667 +7.333333 +8.000000 +8.666667 +9.333333 +10.000000 diff --git a/lang/perl/tests/test13 b/lang/perl/tests/test13 new file mode 100755 index 0000000..65f2c37 --- /dev/null +++ b/lang/perl/tests/test13 @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(f)",\$i); +for($i=0; $i<10.0; $i+=2/3.0) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +open TPLXML, "|../tplxml" or die "can't open tplxml: $!"; +print TPLXML $img; +close TPLXML; diff --git a/lang/perl/tests/test13.ans b/lang/perl/tests/test13.ans new file mode 100644 index 0000000..f8b97db --- /dev/null +++ b/lang/perl/tests/test13.ans @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + ]> + + + + 0 + + + 0.666666666666667 + + + 1.33333333333333 + + + 2 + + + 2.66666666666667 + + + 3.33333333333333 + + + 4 + + + 4.66666666666667 + + + 5.33333333333333 + + + 6 + + + 6.66666666666667 + + + 7.33333333333333 + + + 8 + + + 8.66666666666667 + + + 9.33333333333333 + + + 10 + + + diff --git a/lang/perl/tests/test14 b/lang/perl/tests/test14 new file mode 100755 index 0000000..920bd80 --- /dev/null +++ b/lang/perl/tests/test14 @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $tmp1 = "test14.tpl"; + +my $j; +my $tpl2 = Tpl->tpl_map("i",\$j); +$tpl2->tpl_load($tmp1); +$tpl2->tpl_unpack(0); +print "$j\n"; + + diff --git a/lang/perl/tests/test14.ans b/lang/perl/tests/test14.ans new file mode 100644 index 0000000..3fbedf6 --- /dev/null +++ b/lang/perl/tests/test14.ans @@ -0,0 +1 @@ +-2 diff --git a/lang/perl/tests/test14.tpl b/lang/perl/tests/test14.tpl new file mode 100644 index 0000000..f11ee5f Binary files /dev/null and b/lang/perl/tests/test14.tpl differ diff --git a/lang/perl/tests/test15 b/lang/perl/tests/test15 new file mode 100755 index 0000000..e65437d --- /dev/null +++ b/lang/perl/tests/test15 @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $tmp1 = "test15.tpl"; + +my $j; +my $tpl2 = Tpl->tpl_map("i",\$j); +$tpl2->tpl_load($tmp1); +$tpl2->tpl_unpack(0); +print "$j\n"; + + diff --git a/lang/perl/tests/test15.ans b/lang/perl/tests/test15.ans new file mode 100644 index 0000000..a83d1d5 --- /dev/null +++ b/lang/perl/tests/test15.ans @@ -0,0 +1 @@ +-3 diff --git a/lang/perl/tests/test15.tpl b/lang/perl/tests/test15.tpl new file mode 100644 index 0000000..69249f7 Binary files /dev/null and b/lang/perl/tests/test15.tpl differ diff --git a/lang/perl/tests/test16 b/lang/perl/tests/test16 new file mode 100755 index 0000000..2d24fc7 --- /dev/null +++ b/lang/perl/tests/test16 @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $i; +my $tpl = Tpl->tpl_map("B",\$i); +$i = pack("CCCC", 0xA, 0xB, 0xC, 0xD); +$tpl->tpl_pack(0); +my $img = $tpl->tpl_dump(); + + +$tpl = Tpl->tpl_map("B",\$i); +$tpl->tpl_load(\$img); +$tpl->tpl_unpack(0); +print "$i\n"; diff --git a/lang/perl/tests/test16.ans b/lang/perl/tests/test16.ans new file mode 100644 index 0000000..b04dd2e --- /dev/null +++ b/lang/perl/tests/test16.ans @@ -0,0 +1,2 @@ + + diff --git a/lang/perl/tests/test17 b/lang/perl/tests/test17 new file mode 100755 index 0000000..18ab7d8 --- /dev/null +++ b/lang/perl/tests/test17 @@ -0,0 +1,25 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my ($i,$j)=(1,ord('a')); +my $tpl = Tpl->tpl_map("S(ic)",\$i, \$j); +$tpl->tpl_pack(0); +$tpl->tpl_dump($tmp1); + +($i,$j)=(-9,"x"); +my $tpl2 = Tpl->tpl_map("S(ic)",\$i,\$j); +$tpl2->tpl_load($tmp1); +$tpl2->tpl_unpack(0); +$j = chr($j); +print "$i,$j\n"; + diff --git a/lang/perl/tests/test17.ans b/lang/perl/tests/test17.ans new file mode 100644 index 0000000..a441d18 --- /dev/null +++ b/lang/perl/tests/test17.ans @@ -0,0 +1 @@ +1,a diff --git a/lang/perl/tests/test18 b/lang/perl/tests/test18 new file mode 100755 index 0000000..e6f8e04 --- /dev/null +++ b/lang/perl/tests/test18 @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my @i=(1,2,3,4); +my $tpl = Tpl->tpl_map("i#",\@i, 3); +$tpl->tpl_pack(0); +$tpl->tpl_dump($tmp1); + +my @j; +my $tpl2 = Tpl->tpl_map("i#",\@j,3); +$tpl2->tpl_load($tmp1); +$tpl2->tpl_unpack(0); +print "$_\n" for @j; diff --git a/lang/perl/tests/test18.ans b/lang/perl/tests/test18.ans new file mode 100644 index 0000000..01e79c3 --- /dev/null +++ b/lang/perl/tests/test18.ans @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/lang/perl/tests/test19 b/lang/perl/tests/test19 new file mode 100755 index 0000000..79e8b00 --- /dev/null +++ b/lang/perl/tests/test19 @@ -0,0 +1,25 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my @i=(1,2,3,4); +my @j=(-1,-2,-3, -4); +my $tpl = Tpl->tpl_map("i#i#",\@i, 3, \@j, 4); +$tpl->tpl_pack(0); +$tpl->tpl_dump($tmp1); + +my (@x,@y); +my $tpl2 = Tpl->tpl_map("i#i#",\@x, 3, \@y, 4); +$tpl2->tpl_load($tmp1); +$tpl2->tpl_unpack(0); +print "$_\n" for @x; +print "$_\n" for @y; diff --git a/lang/perl/tests/test19.ans b/lang/perl/tests/test19.ans new file mode 100644 index 0000000..eee553c --- /dev/null +++ b/lang/perl/tests/test19.ans @@ -0,0 +1,7 @@ +1 +2 +3 +-1 +-2 +-3 +-4 diff --git a/lang/perl/tests/test2 b/lang/perl/tests/test2 new file mode 100755 index 0000000..e552cf7 --- /dev/null +++ b/lang/perl/tests/test2 @@ -0,0 +1,18 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +my $j; +my $tpl2 = Tpl->tpl_map("A(i)",\$j); +$tpl2->tpl_load(\$img); +while($tpl2->tpl_unpack(1) > 0) { print "$j\n" } + diff --git a/lang/perl/tests/test2.ans b/lang/perl/tests/test2.ans new file mode 100644 index 0000000..8b1acc1 --- /dev/null +++ b/lang/perl/tests/test2.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/lang/perl/tests/test20 b/lang/perl/tests/test20 new file mode 100755 index 0000000..e78edc7 --- /dev/null +++ b/lang/perl/tests/test20 @@ -0,0 +1,36 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; +my $tmp2 = "$pwd/$0_2.out"; +my $tmp3 = "$pwd/$0_3.out"; + +my ($c,@i); +my $tpl = Tpl->tpl_map("A(S(ci#))",\$c,\@i,10); + +# make element 1 +$c = 97; +@i = (0,1,2,3,4,5,6,7,8,9); +$tpl->tpl_pack(1); + +# make element 2 +$c = 98; +@i = (1,2,3,4,5,6,7,8,9,10); +$tpl->tpl_pack(1); + +$tpl->tpl_dump($tmp1); + + +`../tplxml $tmp1 > $tmp2`; # convert tpl to xml +`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl +`diff $tmp1 $tmp3`; +print "tpl files ", ($? ? "differ" : "identical"), "\n"; + diff --git a/lang/perl/tests/test20.ans b/lang/perl/tests/test20.ans new file mode 100644 index 0000000..1d74042 --- /dev/null +++ b/lang/perl/tests/test20.ans @@ -0,0 +1 @@ +tpl files identical diff --git a/lang/perl/tests/test21 b/lang/perl/tests/test21 new file mode 100755 index 0000000..814ff1f --- /dev/null +++ b/lang/perl/tests/test21 @@ -0,0 +1,15 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +my $fmt = Tpl->tpl_peek(\$img); +print("$fmt\n"); diff --git a/lang/perl/tests/test21.ans b/lang/perl/tests/test21.ans new file mode 100644 index 0000000..bc9aa33 --- /dev/null +++ b/lang/perl/tests/test21.ans @@ -0,0 +1 @@ +A(i) diff --git a/lang/perl/tests/test22 b/lang/perl/tests/test22 new file mode 100755 index 0000000..416978f --- /dev/null +++ b/lang/perl/tests/test22 @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my @i=(1,2,3,4); +my @j=(-1,-2,-3, -4); +my $tpl = Tpl->tpl_map("i#i#",\@i, 3, \@j, 4); +$tpl->tpl_pack(0); +$tpl->tpl_dump($tmp1); + +my $fmt = Tpl->tpl_peek($tmp1); +print "$fmt\n"; diff --git a/lang/perl/tests/test22.ans b/lang/perl/tests/test22.ans new file mode 100644 index 0000000..7dd3f3f --- /dev/null +++ b/lang/perl/tests/test22.ans @@ -0,0 +1 @@ +i#i# diff --git a/lang/perl/tests/test23 b/lang/perl/tests/test23 new file mode 100755 index 0000000..13c67e9 --- /dev/null +++ b/lang/perl/tests/test23 @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +# this test only works on 64-bit Perl + +my ($i,$j) = (-4294967296,4294967296); # 2^32 (can't fit in a 32-bit value) +my $tpl = Tpl->tpl_map("IU",\$i,\$j); +$tpl->tpl_pack(0); +my $img = $tpl->tpl_dump(); + +my ($x,$y); +my $tpl2 = Tpl->tpl_map("IU",\$x,\$y); +$tpl2->tpl_load(\$img); +$tpl2->tpl_unpack(0); +print "$x $y\n"; + diff --git a/lang/perl/tests/test3 b/lang/perl/tests/test3 new file mode 100755 index 0000000..9bbc0b1 --- /dev/null +++ b/lang/perl/tests/test3 @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +open TPLXML, "|../tplxml" or die "can't open tplxml: $!"; +print TPLXML $img; +close TPLXML; diff --git a/lang/perl/tests/test3.ans b/lang/perl/tests/test3.ans new file mode 100644 index 0000000..4399a64 --- /dev/null +++ b/lang/perl/tests/test3.ans @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + ]> + + + + 0 + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 9 + + + diff --git a/lang/perl/tests/test4 b/lang/perl/tests/test4 new file mode 100755 index 0000000..2a71a25 --- /dev/null +++ b/lang/perl/tests/test4 @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; +my $tmp2 = "$pwd/$0_2.out"; +my $tmp3 = "$pwd/$0_3.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(i)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + + +`../tplxml $tmp1 > $tmp2`; # convert tpl to xml +`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl +`diff $tmp1 $tmp3`; +print "tpl files ", ($? ? "differ" : "identical"), "\n"; + diff --git a/lang/perl/tests/test4.ans b/lang/perl/tests/test4.ans new file mode 100644 index 0000000..1d74042 --- /dev/null +++ b/lang/perl/tests/test4.ans @@ -0,0 +1 @@ +tpl files identical diff --git a/lang/perl/tests/test5 b/lang/perl/tests/test5 new file mode 100755 index 0000000..0638503 --- /dev/null +++ b/lang/perl/tests/test5 @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(c)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + +my $j; +my $tpl2 = Tpl->tpl_map("A(c)",\$j); +$tpl2->tpl_load($tmp1); +while($tpl2->tpl_unpack(1) > 0) { printf("%d\n", $j); } + diff --git a/lang/perl/tests/test5.ans b/lang/perl/tests/test5.ans new file mode 100644 index 0000000..8b1acc1 --- /dev/null +++ b/lang/perl/tests/test5.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/lang/perl/tests/test6 b/lang/perl/tests/test6 new file mode 100755 index 0000000..595a800 --- /dev/null +++ b/lang/perl/tests/test6 @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $i; +my $tpl = Tpl->tpl_map("A(c)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +my $img = $tpl->tpl_dump(); + +open TPLXML, "|../tplxml" or die "can't open tplxml: $!"; +print TPLXML $img; +close TPLXML; diff --git a/lang/perl/tests/test6.ans b/lang/perl/tests/test6.ans new file mode 100644 index 0000000..a84432e --- /dev/null +++ b/lang/perl/tests/test6.ans @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + ]> + + + + 0 + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 9 + + + diff --git a/lang/perl/tests/test7 b/lang/perl/tests/test7 new file mode 100755 index 0000000..b6b0850 --- /dev/null +++ b/lang/perl/tests/test7 @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; +my $tmp2 = "$pwd/$0_2.out"; +my $tmp3 = "$pwd/$0_3.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(c)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + + +`../tplxml $tmp1 > $tmp2`; # convert tpl to xml +`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl +`diff $tmp1 $tmp3`; +print "tpl files ", ($? ? "differ" : "identical"), "\n"; + diff --git a/lang/perl/tests/test7.ans b/lang/perl/tests/test7.ans new file mode 100644 index 0000000..1d74042 --- /dev/null +++ b/lang/perl/tests/test7.ans @@ -0,0 +1 @@ +tpl files identical diff --git a/lang/perl/tests/test8 b/lang/perl/tests/test8 new file mode 100755 index 0000000..f6c194f --- /dev/null +++ b/lang/perl/tests/test8 @@ -0,0 +1,29 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; +my $tmp2 = "$pwd/$0_2.out"; +my $tmp3 = "$pwd/$0_3.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(s)",\$i); +for (qw(normal has&ersand )) { + $i = $_; + $tpl->tpl_pack(1); +} +$tpl->tpl_dump($tmp1); + + +`../tplxml $tmp1 > $tmp2`; # convert tpl to xml +`../tplxml $tmp2 > $tmp3`; # convert xml back to tpl +`diff $tmp1 $tmp3`; +print "tpl files ", ($? ? "differ" : "identical"), "\n"; + diff --git a/lang/perl/tests/test8.ans b/lang/perl/tests/test8.ans new file mode 100644 index 0000000..1d74042 --- /dev/null +++ b/lang/perl/tests/test8.ans @@ -0,0 +1 @@ +tpl files identical diff --git a/lang/perl/tests/test9 b/lang/perl/tests/test9 new file mode 100755 index 0000000..75ec15e --- /dev/null +++ b/lang/perl/tests/test9 @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +use strict; +use warnings; + +use lib ".."; +use Tpl; + +my $pwd = `pwd`; +chomp $pwd; + +my $tmp1 = "$pwd/$0_1.out"; + +my $i; +my $tpl = Tpl->tpl_map("A(u)",\$i); +for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } +$tpl->tpl_dump($tmp1); + +my $j; +my $tpl2 = Tpl->tpl_map("A(u)",\$j); +$tpl2->tpl_load($tmp1); +while($tpl2->tpl_unpack(1) > 0) { print "$j\n" } + diff --git a/lang/perl/tests/test9.ans b/lang/perl/tests/test9.ans new file mode 100644 index 0000000..8b1acc1 --- /dev/null +++ b/lang/perl/tests/test9.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/lang/perl/tplfmt b/lang/perl/tplfmt new file mode 100755 index 0000000..b3b0603 --- /dev/null +++ b/lang/perl/tplfmt @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +# tplfmt +# by Troy Hanson Feb 2006 +# print the format string of a tpl image file + +use strict; +use warnings; + +sub peek_fmt { + my $buf = shift; + die "invalid tpl file" unless ($$buf =~ /^tpl/); + return (unpack("Z*", substr($$buf,8))); +} + +die "usage: $0 [ ...]" unless (@ARGV > 0); + +undef $/; # slurp +for (@ARGV) { + open TPL, "<$_" or die "can't open $_: $!"; + my $tpl = ; + print "$_: ", peek_fmt(\$tpl), "\n"; + close TPL; +} diff --git a/lang/perl/tplxml b/lang/perl/tplxml new file mode 100755 index 0000000..5037dc3 --- /dev/null +++ b/lang/perl/tplxml @@ -0,0 +1,306 @@ +#!/usr/bin/perl + +# tplxml +# by Troy Hanson 27 Feb 2006 +# convert between tpl and XML + +# Copyright (c) 2005-2006, Troy Hanson http://tpl.sourceforge.net +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use strict; +use warnings; +use XML::Parser; +use FindBin; +use lib "$FindBin::Bin"; #locate Tpl.pm in same directory as tplxml +use Tpl; +use bytes; + +sub quote_chars { + my $str = shift; + $$str =~ s/&/&/g; #order matters + $$str =~ s//>/g; +} +sub unquote_chars { + my $str = shift; + $$str =~ s/<//g; + $$str =~ s/&/&/g; +} +sub hex_chars { + my $str = shift; + my $hex; + for(my $i=0; $i < length $$str; $i++) { + my $byte = unpack("C",substr($$str,$i,1)); + $hex .= sprintf("%02x", $byte); + } + $$str = $hex; +} +sub unhex_chars { + my $str = shift; + my $bytes; + for(my $i=0; $i < length $$str; $i+=2) { + my $hexbyte = substr($$str,$i,2); + $bytes .= pack("C", hex($hexbyte)); + } + $$str= $bytes; +} + +sub tpl2xml { + my $src = shift; + my (@out,@args); + + # build list of references to hold output of unpacking + my ($fmt,@fxlens) = peek_fmt($src); + for(my ($i,$j,$k)=(0,0,0);$itpl_map($fmt,@args); + $tpl->tpl_load($src); + $tpl->tpl_unpack(0); + + # construct xml preamble + my $pre = qq{ + + + + + + + + + + + + + + ]>\n}; + print $pre; + my $fxattr = join ",", @fxlens; + print qq{\n}; + tpl2xml_node($tpl,"A0",1); + print qq{\n}; +} + +sub tpl2xml_node { + my $tpl = shift; + my $node = shift; + my $indent = shift; + my $i = " " x $indent; + for my $c (@{ $tpl->{$node} }) { + if (ref($c)) { + my ($type,$addr,$fxlen) = @$c; + quote_chars $addr if $type eq 's'; + hex_chars $addr if $type eq 'B'; + if (not defined $fxlen) { + print qq{$i<$type>$$addr\n}; # singleton + } else { + # all elements of octothorpic fixed-len array + print qq{$i\n}; + print qq{$i <$type>$addr->[$_]\n} for (0..$fxlen-1); + print qq{$i\n}; + } + } else { + # A node + print qq{$i\n}; + my $idx = $1 if $c =~ /^A(\d+)$/; + while($tpl->tpl_unpack($idx) > 0) { + print qq{$i\n}; + tpl2xml_node($tpl,$c,$indent+1); + print qq{$i\n}; + } + print qq{$i\n}; + } + } +} + +sub xml2tpl { + my $src = shift; + my $p = new XML::Parser( Style => 'Tree' ); + my $tree = $p->parse($$src); + die "not a tpl xml document" unless $tree->[0] eq 'tplxml'; + die "no format attribute" unless defined $tree->[1][0]->{format}; + my $fmt = $tree->[1][0]->{format}; + die "no fxlens attribute" unless defined $tree->[1][0]->{fxlens}; + my @fxlens = split /,/, $tree->[1][0]->{fxlens}; + + # build list of references to variables for use in packing + my (@args,@out); + for(my ($i,$j,$k)=(0,0,0);$itpl_map($fmt,@args); + xml2tpl_dfs($tpl,$tree->[1]); + $tpl->tpl_pack(0); + print $tpl->tpl_dump; +} + +sub xml2tpl_dfs { + my $tpl = shift; + my $xml = shift; + + my @next = @$xml; # ($attr,@tagvals) = $$xml; + shift @next; # discard attributes + my @tpltoks = @{ $tpl->{"A0"} }; #expected tokens when parsing + + TAG: while (@next) { + my $xmltag = shift @next; + my $xmlval = shift @next; + + # skip whitespace/newlines embedded between tags + next TAG if ($xmltag eq "0" and $xmlval =~ /^\s+$/); + + # pack if necessary. consume tokens by look-ahead until non-pack token. + while (@tpltoks > 0 and $tpltoks[0] =~ /^P(\d+)$/) { + shift @tpltoks; + $tpl->tpl_pack($1); + } + + # If tpl format specifies a non-array type should appear at this point + # in the XML tree, then validate the type matches the format and assign + # the value from the XML to the variable from which it'll be packed + my $tpltoken = shift @tpltoks; + my $octothorpic=0; + if (ref $tpltoken) { + my ($tpltype,$tpladdr,$fxlen) = @$tpltoken; + + # This block is how we handle octothorpic (fixed length) arrays. + # If $fxlen is defined then an octothorpic node is expected. + # After finding the node we put its subnodes (the array elements) + # onto the @next array for immediate parsing and we use $fxlen:$remaining + # as a signet version of the $fxlen to induce the element-processing loop. + if (defined $fxlen) { + if ($fxlen =~ /^(\d+):(\d+)$/) { # $1==orig $fxlen, $2==remain $fxlen + $octothorpic=1; + unshift @tpltoks, [$tpltype, $tpladdr, $1.":".($2-1)] if $2 > 1; + } else { # octothorpic array expected; look for parent node + die "expected '' but got '<$xmltag>'" unless $xmltag eq 'fx'; + @{ $tpladdr } = (); # Empty accumulator array for octothorpic values + unshift @tpltoks, [$tpltype, $tpladdr, "$fxlen:$fxlen"]; # x:x signet + shift @$xmlval; # discard 'A' attributes + unshift @next, @$xmlval; #parse xml subtree now (dfs) + next TAG; # proceed to children of node + } + } + + if ($tpltype ne $xmltag) { + die "mismatch: xml has '$xmltag' where format specifies '$tpltype'"; + } + # expect @$xmlval to be ({},0,'value') i.e. a single, terminal text node + if (@$xmlval > 3 || $xmlval->[1] ne '0') { + die "error: xml tag '$xmltag' cannot enclose sub-tags"; + } + if ($octothorpic) { + push @{ $tpladdr }, $xmlval->[2]; + } else { + $$tpladdr = $xmlval->[2]; + } + unquote_chars $tpladdr if $tpltype eq 's'; + unhex_chars $tpladdr if $tpltype eq 'B'; + } elsif ($tpltoken =~ /^A(\d+)$/) { + # tpl format specifies an array should appear at this point in the XML + if ($xmltag ne 'A') { + die "mismatch: xml has '$xmltag' where format specifies 'A'"; + } + shift @$xmlval; # discard 'A' attributes + + # form token that means "replace me with tokens from A(n), x times" + # (where x is the number of elements contained by this array). + my $array_count=0; + for(my $i=0; $i < @$xmlval; $i+=2) { + $array_count++ if $xmlval->[$i] eq 'el'; + } + + unshift @tpltoks, "N$1:$array_count" if $array_count > 0; + unshift @next, @$xmlval; #parse xml subtree now (dfs) + } elsif ($tpltoken =~ /^N(\d+):(\d+)$/) { + if ($xmltag ne "el") { + die "mismatch: xml has '$xmltag' where array 'el' is expected"; + } + # prepend A$1's tokens (and decremented N:count) to expected tokens + my ($n,$elsleft) = ($1, ($2 - 1)); + unshift @tpltoks, "N$n:$elsleft" if $elsleft > 0; + unshift @tpltoks, "P$n"; # "pack me now" token + unshift @tpltoks, @{ $tpl->{"A$1"} }; + + shift @$xmlval; # discard 'el' attributes + unshift @next, @$xmlval; # proceed to parse el subtree (dfs) + } else { + die "internal error, unexpected token $tpltoken"; + } + } + + # pack if necessary. consume tokens by look-ahead until non-pack token. + while (@tpltoks > 0 and $tpltoks[0] =~ /^P(\d+)$/) { + shift @tpltoks; + $tpl->tpl_pack($1); + } + + if (@tpltoks > 0) { + die "error: end of xml document reached but format requires more data"; + } +} + +sub peek_fmt { + my $buf = shift; + die "invalid tpl file" unless ($$buf =~ /^tpl/); + my $flags = CORE::unpack("C", substr($$buf,3,1)); + my $UF = ($flags & 1) ? "N" : "V"; # big or little endian fxlens + my $fmt = (CORE::unpack("Z*", substr($$buf,8))); + my $num_octothorpes = scalar (my @o = ($fmt =~ /#/g)); + my @fxlens; + my $fx = 8 + length($fmt) + 1; + for(my $i=0; $i < $num_octothorpes; $i++) { + my $fxlen_bytes = substr($$buf,$fx,4); + my $fxlen = unpack($UF, $fxlen_bytes); + push @fxlens, $fxlen; + $fx += 4; + } + return ($fmt,@fxlens); +} + +########################################################################## +# Slurp input file, auto-detect if conversion is to tpl or XML, and run. +########################################################################## + +undef $/; +my $src = <>; +our $to = (substr($src,0,3) eq "tpl") ? "xml" : "tpl"; +xml2tpl(\$src) if $to eq "tpl"; +tpl2xml(\$src) if $to eq "xml"; + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5719afc --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = win +lib_LTLIBRARIES = libtpl.la +libtpl_la_SOURCES = tpl.c +include_HEADERS = tpl.h +libtpl_la_LDFLAGS = -no-undefined -version-info 0:0:0 +libtpl_la_LIBADD = win/libwinmmap.la + diff --git a/src/tpl.c b/src/tpl.c new file mode 100644 index 0000000..2d4adbf --- /dev/null +++ b/src/tpl.c @@ -0,0 +1,2478 @@ +/* +Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define TPL_VERSION 1.5 + +static const char id[]="$Id: tpl.c 192 2009-04-24 10:35:30Z thanson $"; + + +#include /* malloc */ +#include /* va_list */ +#include /* memcpy, memset, strchr */ +#include /* printf (tpl_hook.oops default function) */ + +#ifndef _WIN32 +#include /* for ftruncate */ +#else +#include +#define ftruncate(x,y) _chsize(x,y) +#endif +#include /* for 'open' */ +#include /* for 'open' */ +#include /* for 'open' */ +#include +#ifndef _WIN32 +#include /* uint32_t, uint64_t, etc */ +#else +typedef unsigned short ushort; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#endif + + +#if ( defined __CYGWIN__ || defined __MINGW32__ || defined _WIN32 ) +#include "win/mman.h" /* mmap */ +#else +#include /* mmap */ +#endif + +#include "tpl.h" + +#define TPL_GATHER_BUFLEN 8192 +#define TPL_MAGIC "tpl" + +/* macro to add a structure to a doubly-linked list */ +#define DL_ADD(head,add) \ + do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ + } while (0); + +#define fatal_oom() tpl_hook.fatal("out of memory\n") + +/* bit flags (internal). preceded by the external flags in tpl.h */ +#define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */ +#define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */ +#define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */ +#define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */ + +/* values for the flags byte that appears after the magic prefix */ +#define TPL_SUPPORTED_BITFLAGS 3 +#define TPL_FL_BIGENDIAN (1 << 0) +#define TPL_FL_NULLSTRINGS (1 << 1) + +/* char values for node type */ +#define TPL_TYPE_ROOT 0 +#define TPL_TYPE_INT32 1 +#define TPL_TYPE_UINT32 2 +#define TPL_TYPE_BYTE 3 +#define TPL_TYPE_STR 4 +#define TPL_TYPE_ARY 5 +#define TPL_TYPE_BIN 6 +#define TPL_TYPE_DOUBLE 7 +#define TPL_TYPE_INT64 8 +#define TPL_TYPE_UINT64 9 +#define TPL_TYPE_INT16 10 +#define TPL_TYPE_UINT16 11 +#define TPL_TYPE_POUND 12 + +/* error codes */ +#define ERR_NOT_MINSIZE (-1) +#define ERR_MAGIC_MISMATCH (-2) +#define ERR_INCONSISTENT_SZ (-3) +#define ERR_FMT_INVALID (-4) +#define ERR_FMT_MISSING_NUL (-5) +#define ERR_FMT_MISMATCH (-6) +#define ERR_FLEN_MISMATCH (-7) +#define ERR_INCONSISTENT_SZ2 (-8) +#define ERR_INCONSISTENT_SZ3 (-9) +#define ERR_INCONSISTENT_SZ4 (-10) +#define ERR_UNSUPPORTED_FLAGS (-11) + +/* access to A(...) nodes by index */ +typedef struct tpl_pidx { + struct tpl_node *node; + struct tpl_pidx *next,*prev; +} tpl_pidx; + +/* A(...) node datum */ +typedef struct tpl_atyp { + uint32_t num; /* num elements */ + size_t sz; /* size of each backbone's datum */ + struct tpl_backbone *bb,*bbtail; + void *cur; +} tpl_atyp; + +/* backbone to extend A(...) lists dynamically */ +typedef struct tpl_backbone { + struct tpl_backbone *next; + /* when this structure is malloc'd, extra space is alloc'd at the + * end to store the backbone "datum", and data points to it. */ +#if __STDC_VERSION__ < 199901 + char *data; +#else + char data[]; +#endif +} tpl_backbone; + +/* mmap record */ +typedef struct tpl_mmap_rec { + int fd; + void *text; + size_t text_sz; +} tpl_mmap_rec; + +/* root node datum */ +typedef struct tpl_root_data { + int flags; + tpl_pidx *pidx; + tpl_mmap_rec mmap; + char *fmt; + int *fxlens, num_fxlens; +} tpl_root_data; + +/* node type to size mapping */ +struct tpl_type_t { + char c; + int sz; +}; + + +/* Internal prototypes */ +static tpl_node *tpl_node_new(tpl_node *parent); +static tpl_node *tpl_find_i(tpl_node *n, int i); +static void *tpl_cpv(void *datav, const void *data, size_t sz); +static void *tpl_extend_backbone(tpl_node *n); +static char *tpl_fmt(tpl_node *r); +static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv); +static size_t tpl_ser_osz(tpl_node *n); +static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp); +static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz); +static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec); +static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out); +static int tpl_cpu_bigendian(void); +static int tpl_needs_endian_swap(void *); +static void tpl_byteswap(void *word, int len); +static void tpl_fatal(const char *fmt, ...); +static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen); +static int tpl_unpackA0(tpl_node *r); +static int tpl_oops(const char *fmt, ...); +static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); +static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); +static int tpl_gather_blocking(int fd, void **img, size_t *sz); + +/* This is used internally to help calculate padding when a 'double' + * follows a smaller datatype in a structure. Normally under gcc + * on x86, d will be aligned at +4, however use of -malign-double + * causes d to be aligned at +8 (this is actually faster on x86). + * Also SPARC and x86_64 seem to align always on +8. + */ +struct tpl_double_alignment_detector { + char a; + double d; /* some platforms align this on +4, others on +8 */ +}; + +/* this is another case where alignment varies. mac os x/gcc was observed + * to align the int64_t at +4 under -m32 and at +8 under -m64 */ +struct tpl_int64_alignment_detector { + int i; + int64_t j; /* some platforms align this on +4, others on +8 */ +}; + +typedef struct { + size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */ + tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */ + size_t iternum; /* current iteration number (total req'd. iter's in n->num) */ +} tpl_pound_data; + +/* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */ +tpl_hook_t tpl_hook = { + /* .oops = */ tpl_oops, + /* .malloc = */ malloc, + /* .realloc = */ realloc, + /* .free = */ free, + /* .fatal = */ tpl_fatal, + /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */ +}; + +static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */ +static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */ +static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */ +static const struct tpl_type_t tpl_types[] = { + /* [TPL_TYPE_ROOT] = */ {'r', 0}, + /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)}, + /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)}, + /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)}, + /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)}, + /* [TPL_TYPE_ARY] = */ {'A', 0}, + /* [TPL_TYPE_BIN] = */ {'B', 0}, + /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */ + /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)}, + /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)}, + /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)}, + /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)}, + /* [TPL_TYPE_POUND] = */ {'#', 0}, +}; + +/* default error-reporting function. Just writes to stderr. */ +static int tpl_oops(const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + return 0; +} + + +static tpl_node *tpl_node_new(tpl_node *parent) { + tpl_node *n; + if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) { + fatal_oom(); + } + n->addr=NULL; + n->data=NULL; + n->num=1; + n->ser_osz=0; + n->children=NULL; + n->next=NULL; + n->parent=parent; + return n; +} + +/* Used in S(..) formats to pack several fields from a structure based on + * only the structure address. We need to calculate field addresses + * manually taking into account the size of the fields and intervening padding. + * The wrinkle is that double is not normally aligned on x86-32 but the + * -malign-double compiler option causes it to be. Double are aligned + * on Sparc, and apparently on 64 bit x86. We use a helper structure + * to detect whether double is aligned in this compilation environment. + */ +char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) { + tpl_node *prev; + int offset; + int align_sz; + + if (ordinal == 1) return struct_addr; /* first field starts on structure address */ + + /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */ + prev = parent->children->prev; + switch(type) { + case TPL_TYPE_DOUBLE: + align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4; + break; + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4; + break; + default: + align_sz = tpl_types[type].sz; + break; + } + offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr) + + (tpl_types[prev->type].sz * prev->num); + offset = (offset + align_sz - 1) / align_sz * align_sz; + return struct_addr + offset; +} + +TPL_API tpl_node *tpl_map(char *fmt,...) { + va_list ap; + tpl_node *tn; + + va_start(ap,fmt); + tn = tpl_map_va(fmt, ap); + va_end(ap); + return tn; +} + +TPL_API tpl_node *tpl_map_va(char *fmt, va_list ap) { + int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0; + int in_nested_structure=0; + char *c, *peek, *struct_addr=NULL, *struct_next; + tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL, + *struct_widest_node=NULL, *np; tpl_pidx *pidx; + tpl_pound_data *pd; + int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct; + int contig_fxlens[10]; /* temp space for contiguous fxlens */ + int num_contig_fxlens, i, j; + ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */ + + + root = tpl_node_new(NULL); + root->type = TPL_TYPE_ROOT; + root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data)); + if (!root->data) fatal_oom(); + memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data)); + + /* set up root nodes special ser_osz to reflect overhead of preamble */ + root->ser_osz = sizeof(uint32_t); /* tpl leading length */ + root->ser_osz += strlen(fmt) + 1; /* fmt + NUL-terminator */ + root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */ + + parent=root; + + c=fmt; + while (*c != '\0') { + switch (*c) { + case 'c': + case 'i': + case 'u': + case 'j': + case 'v': + case 'I': + case 'U': + case 'f': + if (*c=='c') t=TPL_TYPE_BYTE; + else if (*c=='i') t=TPL_TYPE_INT32; + else if (*c=='u') t=TPL_TYPE_UINT32; + else if (*c=='j') t=TPL_TYPE_INT16; + else if (*c=='v') t=TPL_TYPE_UINT16; + else if (*c=='I') t=TPL_TYPE_INT64; + else if (*c=='U') t=TPL_TYPE_UINT64; + else if (*c=='f') t=TPL_TYPE_DOUBLE; + + if (expect_lparen) goto fail; + n = tpl_node_new(parent); + n->type = t; + if (in_structure) { + if (ordinal == 1) { + /* for S(...)# iteration. Apply any changes to case 's' too!!! */ + iter_start_node = n; + struct_widest_node = n; + } + if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { + struct_widest_node = n; + } + n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); + } else n->addr = (void*)va_arg(ap,void*); + n->data = tpl_hook.malloc(tpl_types[t].sz); + if (!n->data) fatal_oom(); + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz; + DL_ADD(parent->children,n); + break; + case 's': + if (expect_lparen) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_STR; + if (in_structure) { + if (ordinal == 1) { + iter_start_node = n; /* for S(...)# iteration */ + struct_widest_node = n; + } + if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { + struct_widest_node = n; + } + n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); + } else n->addr = (void*)va_arg(ap,void*); + n->data = tpl_hook.malloc(sizeof(char*)); + if (!n->data) fatal_oom(); + *(char**)(n->data) = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); + DL_ADD(parent->children,n); + break; + case '#': + /* apply a 'num' to preceding atom */ + if (!parent->children) goto fail; + preceding = parent->children->prev; /* first child's prev is 'last child'*/ + t = preceding->type; + applies_to_struct = (*(c-1) == ')') ? 1 : 0; + if (!applies_to_struct) { + if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 || + t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE || + t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 || + t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 || + t == TPL_TYPE_STR )) goto fail; + } + /* count up how many contiguous # and form their product */ + pound_prod=1; + num_contig_fxlens=0; + for(peek=c; *peek == '#'; peek++) { + pound_num = va_arg(ap, int); + if (pound_num < 1) { + tpl_hook.fatal("non-positive iteration count %d\n", pound_num); + } + if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) { + tpl_hook.fatal("contiguous # exceeds hardcoded limit\n"); + } + contig_fxlens[num_contig_fxlens++] = pound_num; + pound_prod *= pound_num; + } + /* increment c to skip contiguous # so its points to last one */ + c = peek-1; + /* differentiate atom-# from struct-# by noting preceding rparen */ + if (applies_to_struct) { /* insert # node to induce looping */ + n = tpl_node_new(parent); + n->type = TPL_TYPE_POUND; + n->num = pound_prod; + n->data = tpl_hook.malloc(sizeof(tpl_pound_data)); + if (!n->data) fatal_oom(); + pd = (tpl_pound_data*)n->data; + pd->inter_elt_len = inter_elt_len; + pd->iter_start_node = iter_start_node; + pd->iternum = 0; + DL_ADD(parent->children,n); + /* multiply the 'num' and data space on each atom in the structure */ + for(np = iter_start_node; np != n; np = np->next) { + if (n->parent->type == TPL_TYPE_ARY) { + ((tpl_atyp*)(n->parent->data))->sz += + tpl_types[np->type].sz * (np->num * (n->num - 1)); + } + np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz * + np->num * n->num); + if (!np->data) fatal_oom(); + memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num); + } + } else { /* simple atom-# form does not require a loop */ + preceding->num = pound_prod; + preceding->data = tpl_hook.realloc(preceding->data, + tpl_types[t].sz * preceding->num); + if (!preceding->data) fatal_oom(); + memset(preceding->data,0,tpl_types[t].sz * preceding->num); + if (n->parent->type == TPL_TYPE_ARY) { + ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz * + (preceding->num-1); + } + } + root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens); + + j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */ + (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens; + num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */ + fxlens = ((tpl_root_data*)root->data)->fxlens; + fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens); + if (!fxlens) fatal_oom(); + ((tpl_root_data*)root->data)->fxlens = fxlens; + for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i]; + + break; + case 'B': + if (expect_lparen) goto fail; + if (in_structure) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_BIN; + n->addr = (tpl_bin*)va_arg(ap,void*); + n->data = tpl_hook.malloc(sizeof(tpl_bin*)); + if (!n->data) fatal_oom(); + *((tpl_bin**)n->data) = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin); + DL_ADD(parent->children,n); + break; + case 'A': + if (in_structure) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_ARY; + DL_ADD(parent->children,n); + parent = n; + expect_lparen=1; + pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx)); + if (!pidx) fatal_oom(); + pidx->node = n; + pidx->next = NULL; + DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx); + /* set up the A's tpl_atyp */ + n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); + if (!n->data) fatal_oom(); + ((tpl_atyp*)(n->data))->num = 0; + ((tpl_atyp*)(n->data))->sz = 0; + ((tpl_atyp*)(n->data))->bb = NULL; + ((tpl_atyp*)(n->data))->bbtail = NULL; + ((tpl_atyp*)(n->data))->cur = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); + break; + case 'S': + if (in_structure) goto fail; + expect_lparen=1; + ordinal=1; /* index upcoming atoms in S(..) */ + in_structure=1+lparen_level; /* so we can tell where S fmt ends */ + struct_addr = (char*)va_arg(ap,void*); + break; + case '$': /* nested structure */ + if (!in_structure) goto fail; + expect_lparen=1; + in_nested_structure++; + break; + case ')': + lparen_level--; + if (lparen_level < 0) goto fail; + if (*(c-1) == '(') goto fail; + if (in_nested_structure) in_nested_structure--; + else if (in_structure && (in_structure-1 == lparen_level)) { + /* calculate delta between contiguous structures in array */ + struct_next = calc_field_addr(parent, struct_widest_node->type, + struct_addr, ordinal++); + inter_elt_len = struct_next - struct_addr; + in_structure=0; + } + else parent = parent->parent; /* rparen ends A() type, not S() type */ + break; + case '(': + if (!expect_lparen) goto fail; + expect_lparen=0; + lparen_level++; + break; + default: + tpl_hook.oops("unsupported option %c\n", *c); + goto fail; + } + c++; + } + if (lparen_level != 0) goto fail; + + /* copy the format string, save for convenience */ + ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(strlen(fmt)+1); + if (((tpl_root_data*)(root->data))->fmt == NULL) + fatal_oom(); + memcpy(((tpl_root_data*)(root->data))->fmt,fmt,strlen(fmt)+1); + + return root; + +fail: + tpl_hook.oops("failed to parse %s\n", fmt); + tpl_free(root); + return NULL; +} + +static int tpl_unmap_file( tpl_mmap_rec *mr) { + + if ( munmap( mr->text, mr->text_sz ) == -1 ) { + tpl_hook.oops("Failed to munmap: %s\n", strerror(errno)); + } + close(mr->fd); + mr->text = NULL; + mr->text_sz = 0; + return 0; +} + +static void tpl_free_keep_map(tpl_node *r) { + int mmap_bits = (TPL_RDONLY|TPL_FILE); + int ufree_bits = (TPL_MEM|TPL_UFREE); + tpl_node *nxtc,*c; + int find_next_node=0,looking,i; + size_t sz; + + /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ + if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); + } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { + tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); + } + + c = r->children; + if (c) { + while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ + switch (c->type) { + case TPL_TYPE_BIN: + /* free any binary buffer hanging from tpl_bin */ + if ( *((tpl_bin**)(c->data)) ) { + if ( (*((tpl_bin**)(c->data)))->addr ) { + tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); + } + *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */ + } + find_next_node=1; + break; + case TPL_TYPE_STR: + /* free any packed (copied) string */ + for(i=0; i < c->num; i++) { + char *str = ((char**)c->data)[i]; + if (str) { + tpl_hook.free(str); + ((char**)c->data)[i] = NULL; + } + } + find_next_node=1; + break; + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + case TPL_TYPE_POUND: + find_next_node=1; + break; + case TPL_TYPE_ARY: + c->ser_osz = 0; /* zero out the serialization output size */ + + sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */ + tpl_free_atyp(c,c->data); + + /* make new atyp */ + c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); + if (!c->data) fatal_oom(); + ((tpl_atyp*)(c->data))->num = 0; + ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */ + ((tpl_atyp*)(c->data))->bb = NULL; + ((tpl_atyp*)(c->data))->bbtail = NULL; + ((tpl_atyp*)(c->data))->cur = NULL; + + c = c->children; + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + if (find_next_node) { + find_next_node=0; + looking=1; + while(looking) { + if (c->next) { + nxtc=c->next; + c=nxtc; + looking=0; + } else { + if (c->type == TPL_TYPE_ROOT) break; /* root node */ + else { + nxtc=c->parent; + c=nxtc; + } + } + } + } + } + } + + ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */ +} + +TPL_API void tpl_free(tpl_node *r) { + int mmap_bits = (TPL_RDONLY|TPL_FILE); + int ufree_bits = (TPL_MEM|TPL_UFREE); + tpl_node *nxtc,*c; + int find_next_node=0,looking,i; + tpl_pidx *pidx,*pidx_nxt; + + /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ + if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); + } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { + tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); + } + + c = r->children; + if (c) { + while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ + switch (c->type) { + case TPL_TYPE_BIN: + /* free any binary buffer hanging from tpl_bin */ + if ( *((tpl_bin**)(c->data)) ) { + if ( (*((tpl_bin**)(c->data)))->sz != 0 ) { + tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); + } + tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */ + } + tpl_hook.free(c->data); /* free tpl_bin* */ + find_next_node=1; + break; + case TPL_TYPE_STR: + /* free any packed (copied) string */ + for(i=0; i < c->num; i++) { + char *str = ((char**)c->data)[i]; + if (str) { + tpl_hook.free(str); + ((char**)c->data)[i] = NULL; + } + } + tpl_hook.free(c->data); + find_next_node=1; + break; + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + case TPL_TYPE_POUND: + tpl_hook.free(c->data); + find_next_node=1; + break; + case TPL_TYPE_ARY: + tpl_free_atyp(c,c->data); + if (c->children) c = c->children; /* normal case */ + else find_next_node=1; /* edge case, handle bad format A() */ + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + if (find_next_node) { + find_next_node=0; + looking=1; + while(looking) { + if (c->next) { + nxtc=c->next; + tpl_hook.free(c); + c=nxtc; + looking=0; + } else { + if (c->type == TPL_TYPE_ROOT) break; /* root node */ + else { + nxtc=c->parent; + tpl_hook.free(c); + c=nxtc; + } + } + } + } + } + } + + /* free root */ + for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) { + pidx_nxt = pidx->next; + tpl_hook.free(pidx); + } + tpl_hook.free(((tpl_root_data*)(r->data))->fmt); + if (((tpl_root_data*)(r->data))->num_fxlens > 0) { + tpl_hook.free(((tpl_root_data*)(r->data))->fxlens); + } + tpl_hook.free(r->data); /* tpl_root_data */ + tpl_hook.free(r); +} + + +/* Find the i'th packable ('A' node) */ +static tpl_node *tpl_find_i(tpl_node *n, int i) { + int j=0; + tpl_pidx *pidx; + if (n->type != TPL_TYPE_ROOT) return NULL; + if (i == 0) return n; /* packable 0 is root */ + for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) { + if (++j == i) return pidx->node; + } + return NULL; +} + +static void *tpl_cpv(void *datav, const void *data, size_t sz) { + if (sz>0) memcpy(datav,data,sz); + return (void*)((uintptr_t)datav + sz); +} + +static void *tpl_extend_backbone(tpl_node *n) { + tpl_backbone *bb; + bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) + + ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */ + if (!bb) fatal_oom(); +#if __STDC_VERSION__ < 199901 + bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone)); +#endif + memset(bb->data,0,((tpl_atyp*)(n->data))->sz); + bb->next = NULL; + /* Add the new backbone to the tail, also setting head if necessary */ + if (((tpl_atyp*)(n->data))->bb == NULL) { + ((tpl_atyp*)(n->data))->bb = bb; + ((tpl_atyp*)(n->data))->bbtail = bb; + } else { + ((tpl_atyp*)(n->data))->bbtail->next = bb; + ((tpl_atyp*)(n->data))->bbtail = bb; + } + + ((tpl_atyp*)(n->data))->num++; + return bb->data; +} + +/* Get the format string corresponding to a given tpl (root node) */ +static char *tpl_fmt(tpl_node *r) { + return ((tpl_root_data*)(r->data))->fmt; +} + +/* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */ +static int *tpl_fxlens(tpl_node *r, int *num_fxlens) { + *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens; + return ((tpl_root_data*)(r->data))->fxlens; +} + +/* called when serializing an 'A' type node into a buffer which has + * already been set up with the proper space. The backbone is walked + * which was obtained from the tpl_atyp header passed in. + */ +static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) { + tpl_backbone *bb; + tpl_node *c; + void *datav; + uint32_t slen; + tpl_bin *binp; + char *strp; + tpl_atyp *atypp; + tpl_pound_data *pd; + int i; + size_t itermax; + + /* handle 'A' nodes */ + dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */ + for(bb=at->bb; bb; bb=bb->next) { + datav = bb->data; + c=n->children; + while(c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num); + datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num); + break; + case TPL_TYPE_BIN: + /* dump the buffer length followed by the buffer */ + memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */ + slen = binp->sz; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); + dv = tpl_cpv(dv,binp->addr,slen); + datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*)); + break; + case TPL_TYPE_STR: + /* dump the string length followed by the string */ + for(i=0; i < c->num; i++) { + memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */ + slen = strp ? (strlen(strp)+1) : 0; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); + if (slen > 1) dv = tpl_cpv(dv,strp,slen-1); + datav = (void*)((uintptr_t)datav + sizeof(char*)); + } + break; + case TPL_TYPE_ARY: + memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */ + dv = tpl_dump_atyp(c,atypp,dv); + datav = (void*)((uintptr_t)datav + sizeof(void*)); + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + } + return dv; +} + +/* figure the serialization output size needed for tpl whose root is n*/ +static size_t tpl_ser_osz(tpl_node *n) { + tpl_node *c, *np; + size_t sz, itermax; + tpl_bin *binp; + char *strp; + tpl_pound_data *pd; + int i; + + /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */ + if (n->type != TPL_TYPE_ROOT) { + tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n"); + } + + sz = n->ser_osz; /* start with fixed overhead, already stored */ + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + sz += tpl_types[c->type].sz * c->num; + break; + case TPL_TYPE_BIN: + sz += sizeof(uint32_t); /* binary buf len */ + memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */ + sz += binp->sz; + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + sz += sizeof(uint32_t); /* string len */ + memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */ + sz += strp ? strlen(strp) : 0; + } + break; + case TPL_TYPE_ARY: + sz += sizeof(uint32_t); /* array len */ + sz += c->ser_osz; /* bubbled-up child array ser_osz */ + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + } + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + } + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + return sz; +} + + +TPL_API int tpl_dump(tpl_node *r, int mode, ...) { + va_list ap; + char *filename, *bufv; + void **addr_out,*buf, *pa_addr; + int fd,rc=0; + size_t sz,*sz_out, pa_sz; + struct stat sbuf; + + if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */ + tpl_hook.oops("error: tpl_dump called for a loaded tpl\n"); + return -1; + } + + sz = tpl_ser_osz(r); /* compute the size needed to serialize */ + + va_start(ap,mode); + if (mode & TPL_FILE) { + filename = va_arg(ap,char*); + fd = tpl_mmap_output_file(filename, sz, &buf); + if (fd == -1) rc = -1; + else { + rc = tpl_dump_to_mem(r,buf,sz); + if (msync(buf,sz,MS_SYNC) == -1) { + tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno)); + } + if (munmap(buf, sz) == -1) { + tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno)); + } + close(fd); + } + } else if (mode & TPL_FD) { + fd = va_arg(ap, int); + if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); + tpl_dump_to_mem(r,buf,sz); + bufv = buf; + do { + rc = write(fd,bufv,sz); + if (rc > 0) { + sz -= rc; + bufv += rc; + } else if (rc == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno)); + free(buf); + /* attempt to rewind partial write to a regular file */ + if (fstat(fd,&sbuf) == 0 && S_ISREG(sbuf.st_mode)) { + if (ftruncate(fd,sbuf.st_size - (bufv-(char*)buf)) == -1) { + tpl_hook.oops("can't rewind: %s\n", strerror(errno)); + } + } + return -1; + } + } while (sz > 0); + free(buf); + rc = 0; + } else if (mode & TPL_MEM) { + if (mode & TPL_PREALLOCD) { /* caller allocated */ + pa_addr = (void*)va_arg(ap, void*); + pa_sz = va_arg(ap, size_t); + if (pa_sz < sz) { + tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz); + return -1; + } + rc=tpl_dump_to_mem(r,pa_addr,sz); + } else { /* we allocate */ + addr_out = (void**)va_arg(ap, void*); + sz_out = va_arg(ap, size_t*); + if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); + *sz_out = sz; + *addr_out = buf; + rc=tpl_dump_to_mem(r,buf,sz); + } + } else if (mode & TPL_GETSIZE) { + sz_out = va_arg(ap, size_t*); + *sz_out = sz; + } else { + tpl_hook.oops("unsupported tpl_dump mode %d\n", mode); + rc=-1; + } + va_end(ap); + return rc; +} + +/* This function expects the caller to have set up a memory buffer of + * adequate size to hold the serialized tpl. The sz parameter must be + * the result of tpl_ser_osz(r). + */ +static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) { + uint32_t slen, sz32; + int *fxlens, num_fxlens, i; + void *dv; + char *fmt,flags; + tpl_node *c, *np; + tpl_pound_data *pd; + size_t itermax; + + fmt = tpl_fmt(r); + flags = 0; + if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN; + if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS; + sz32 = sz; + + dv = addr; + dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */ + dv = tpl_cpv(dv,&flags,1); /* copy flags byte */ + dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */ + dv = tpl_cpv(dv,fmt,strlen(fmt)+1); /* copy format with NUL-term */ + fxlens = tpl_fxlens(r,&num_fxlens); + dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */ + + /* serialize the tpl content, iterating over direct children of root */ + c = r->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num); + break; + case TPL_TYPE_BIN: + slen = (*(tpl_bin**)(c->data))->sz; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */ + dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */ + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + char *str = ((char**)c->data)[i]; + slen = str ? strlen(str)+1 : 0; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */ + if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/ + } + break; + case TPL_TYPE_ARY: + dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv); + break; + case TPL_TYPE_POUND: + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + + /* in start or midst of loop. advance data pointers. */ + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + } + /* do next iteration */ + c = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + } + + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c = c->next; + } + + return 0; +} + +static int tpl_cpu_bigendian() { + unsigned i = 1; + char *c; + c = (char*)&i; + return (c[0] == 1 ? 0 : 1); +} + + +/* + * algorithm for sanity-checking a tpl image: + * scan the tpl whilst not exceeding the buffer size (bufsz) , + * formulating a calculated (expected) size of the tpl based + * on walking its data. When calcsize has been calculated it + * should exactly match the buffer size (bufsz) and the internal + * recorded size (intlsz) + */ +static int tpl_sanity(tpl_node *r, int excess_ok) { + uint32_t intlsz; + int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen; + void *d, *dv; + char intlflags, *fmt, c, *mapfmt; + size_t bufsz, serlen; + + d = ((tpl_root_data*)(r->data))->mmap.text; + bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz; + + dv = d; + if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */ + if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */ + if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; + dv = (void*)((uintptr_t)dv + 3); + memcpy(&intlflags,dv,sizeof(char)); /* extract flags */ + if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS; + /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from + empty strings from non-empty strings; TPL1.2 only handled the latter two. + So we need to be mindful of which string format we're reading from. */ + if (!(intlflags & TPL_FL_NULLSTRINGS)) { + ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT; + } + dv = (void*)((uintptr_t)dv + 1); + memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t)); + if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */ + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + + /* dv points to the start of the format string. Look for nul w/in buf sz */ + fmt = (char*)dv; + while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) { + if ( (c = *(char*)dv) != '\0') { + if (strchr(tpl_fmt_chars,c) == NULL) + return ERR_FMT_INVALID; /* invalid char in format string */ + if ( (c = *(char*)dv) == '#') octothorpes++; + dv = (void*)((uintptr_t)dv + 1); + } + else found_nul = 1; + } + if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */ + dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */ + + /* compare the map format to the format of this tpl image */ + mapfmt = tpl_fmt(r); + rc = strcmp(mapfmt,fmt); + if (rc != 0) return ERR_FMT_MISMATCH; + + /* compare octothorpe lengths in image to the mapped values */ + if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4; + fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */ + while(num_fxlens--) { + memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t)); + if (flen != *fxlens) return ERR_FLEN_MISMATCH; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + fxlens++; + } + + /* dv now points to beginning of data */ + rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */ + if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */ + serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */ + if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3; + if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */ + return 0; +} + +static void *tpl_find_data_start(void *d) { + int octothorpes=0; + d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */ + d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */ + while(*(char*)d != '\0') { + if (*(char*)d == '#') octothorpes++; + d = (void*)((uintptr_t)d + 1); + } + d = (void*)((uintptr_t)d + 1); /* skip NUL */ + d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */ + return d; +} + +static int tpl_needs_endian_swap(void *d) { + char *c; + int cpu_is_bigendian; + c = (char*)d; + cpu_is_bigendian = tpl_cpu_bigendian(); + return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1; +} + +static size_t tpl_size_for(char c) { + int i; + for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) { + if (tpl_types[i].c == c) return tpl_types[i].sz; + } + return 0; +} + +TPL_API char* tpl_peek(int mode, ...) { + va_list ap; + int xendian=0,found_nul=0,old_string_format=0; + char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s; + void *addr=NULL, *dv, *datapeek_p=NULL; + size_t sz=0, fmt_len, first_atom, num_fxlens=0; + uint32_t datapeek_ssz, datapeek_csz, datapeek_flen; + tpl_mmap_rec mr = {0,NULL,0}; + char *fmt,*fmt_cpy=NULL,c; + uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv; + + va_start(ap,mode); + if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) { + tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n"); + goto fail; + } + if (mode & TPL_FILE) filename = va_arg(ap,char *); + else if (mode & TPL_MEM) { + addr = va_arg(ap,void *); + sz = va_arg(ap,size_t); + } else { + tpl_hook.oops("unsupported tpl_peek mode %d\n", mode); + goto fail; + } + if (mode & TPL_DATAPEEK) { + datapeek_f = va_arg(ap, char*); + } + if (mode & TPL_FXLENS) { + num_fxlens_out = va_arg(ap,uint32_t *); + fxlens = va_arg(ap,uint32_t **); + *num_fxlens_out = 0; + *fxlens = NULL; + } + + if (mode & TPL_FILE) { + if (tpl_mmap_file(filename, &mr) != 0) { + tpl_hook.oops("tpl_peek failed for file %s\n", filename); + goto fail; + } + addr = mr.text; + sz = mr.text_sz; + } + + dv = addr; + if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */ + if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */ + if (tpl_needs_endian_swap(dv)) xendian=1; + if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1; + dv = (void*)((uintptr_t)dv + 4); + memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ + if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t)); + if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */ + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + + /* dv points to the start of the format string. Look for nul w/in buf sz */ + fmt = (char*)dv; + while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) { + if ( (c = *(char*)dv) == '\0') { + found_nul = 1; + } else if (c == '#') { + num_fxlens++; + } + dv = (void*)((uintptr_t)dv + 1); + } + if (!found_nul) goto fail; /* runaway format string */ + fmt_len = (char*)dv - fmt; /* include space for \0 */ + fmt_cpy = tpl_hook.malloc(fmt_len); + if (fmt_cpy == NULL) { + fatal_oom(); + } + memcpy(fmt_cpy, fmt, fmt_len); + + /* retrieve the octothorpic lengths if requested */ + if (num_fxlens > 0) { + if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) { + goto fail; + } + } + if ((mode & TPL_FXLENS) && (num_fxlens > 0)) { + *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t)); + if (*fxlens == NULL) tpl_hook.fatal("out of memory"); + *num_fxlens_out = num_fxlens; + fxlensv = *fxlens; + while(num_fxlens--) { + memcpy(fxlensv,dv,sizeof(uint32_t)); + if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + fxlensv++; + } + } + /* if caller requested, peek into the specified data elements */ + if (mode & TPL_DATAPEEK) { + + first_atom = strspn(fmt, "S()"); /* skip any leading S() */ + + datapeek_flen = strlen(datapeek_f); + if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) { + tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + + if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) { + tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + + /* advance to data start, then copy out requested elements */ + dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t))); + for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) { + datapeek_p = va_arg(ap, void*); + if (*datapeek_c == 's') { /* special handling for strings */ + if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */ + if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t)); + if (old_string_format) datapeek_ssz++; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */ + if (datapeek_ssz == 0) datapeek_s = NULL; + else { + if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + datapeek_s = tpl_hook.malloc(datapeek_ssz); + if (datapeek_s == NULL) fatal_oom(); + memcpy(datapeek_s, dv, datapeek_ssz-1); + datapeek_s[datapeek_ssz-1] = '\0'; + dv = (void*)((uintptr_t)dv + datapeek_ssz-1); + } + *(char**)datapeek_p = datapeek_s; + } else { + datapeek_csz = tpl_size_for(*datapeek_c); + if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + memcpy(datapeek_p, dv, datapeek_csz); + if (xendian) tpl_byteswap(datapeek_p, datapeek_csz); + dv = (void*)((uintptr_t)dv + datapeek_csz); + } + } + } + +fail: + va_end(ap); + if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr ); + return fmt_cpy; +} + +/* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */ +/* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */ +/* tpl_jot(TPL_FD, fd, "si", &s, &i); */ +TPL_API int tpl_jot(int mode, ...) { + va_list ap; + char *filename, *fmt; + size_t *sz; + int fd, rc=0; + void **buf; + tpl_node *tn; + + va_start(ap,mode); + if (mode & TPL_FILE) { + filename = va_arg(ap,char*); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt, ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn, 0); + rc = tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + } else if (mode & TPL_MEM) { + buf = va_arg(ap,void*); + sz = va_arg(ap,size_t*); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt,ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn,0); + rc = tpl_dump(tn, TPL_MEM, buf, sz); + tpl_free(tn); + } else if (mode & TPL_FD) { + fd = va_arg(ap,int); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt,ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn,0); + rc = tpl_dump(tn, TPL_FD, fd); + tpl_free(tn); + } else { + tpl_hook.fatal("invalid tpl_jot mode\n"); + } + +fail: + va_end(ap); + return rc; +} + +TPL_API int tpl_load(tpl_node *r, int mode, ...) { + va_list ap; + int rc=0,fd=0; + char *filename=NULL; + void *addr; + size_t sz; + + va_start(ap,mode); + if (mode & TPL_FILE) filename = va_arg(ap,char *); + else if (mode & TPL_MEM) { + addr = va_arg(ap,void *); + sz = va_arg(ap,size_t); + } else if (mode & TPL_FD) { + fd = va_arg(ap,int); + } else { + tpl_hook.oops("unsupported tpl_load mode %d\n", mode); + return -1; + } + va_end(ap); + + if (r->type != TPL_TYPE_ROOT) { + tpl_hook.oops("error: tpl_load to non-root node\n"); + return -1; + } + if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) { + /* already packed or loaded, so reset it as if newly mapped */ + tpl_free_keep_map(r); + } + if (mode & TPL_FILE) { + if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) { + tpl_hook.oops("tpl_load failed for file %s\n", filename); + return -1; + } + if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { + if (rc == ERR_FMT_MISMATCH) { + tpl_hook.oops("%s: format signature mismatch\n", filename); + } else if (rc == ERR_FLEN_MISMATCH) { + tpl_hook.oops("%s: array lengths mismatch\n", filename); + } else { + tpl_hook.oops("%s: not a valid tpl file\n", filename); + } + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap ); + return -1; + } + ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY); + } else if (mode & TPL_MEM) { + ((tpl_root_data*)(r->data))->mmap.text = addr; + ((tpl_root_data*)(r->data))->mmap.text_sz = sz; + if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { + if (rc == ERR_FMT_MISMATCH) { + tpl_hook.oops("format signature mismatch\n"); + } else { + tpl_hook.oops("not a valid tpl file\n"); + } + return -1; + } + ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY); + if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE; + } else if (mode & TPL_FD) { + /* if fd read succeeds, resulting mem img is used for load */ + if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) { + return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz); + } else return -1; + } else { + tpl_hook.oops("invalid tpl_load mode %d\n", mode); + return -1; + } + /* this applies to TPL_MEM or TPL_FILE */ + if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text)) + ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; + tpl_unpackA0(r); /* prepare root A nodes for use */ + return 0; +} + +TPL_API int tpl_Alen(tpl_node *r, int i) { + tpl_node *n; + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_unpack\n", i); + return -1; + } + if (n->type != TPL_TYPE_ARY) return -1; + return ((tpl_atyp*)(n->data))->num; +} + +static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) { + tpl_backbone *bb,*bbnxt; + tpl_node *c; + void *dv; + tpl_bin *binp; + tpl_atyp *atypp; + char *strp; + size_t itermax; + tpl_pound_data *pd; + int i; + + bb = atyp->bb; + while (bb) { + bbnxt = bb->next; + dv = bb->data; + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num); + break; + case TPL_TYPE_BIN: + memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */ + if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */ + tpl_hook.free(binp); /* free tpl_bin */ + dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*)); + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */ + if (strp) tpl_hook.free(strp); /* free string */ + dv = (void*)((uintptr_t)dv + sizeof(char*)); + } + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + case TPL_TYPE_ARY: + memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */ + tpl_free_atyp(c,atypp); /* free atyp */ + dv = (void*)((uintptr_t)dv + sizeof(void*)); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + tpl_hook.free(bb); + bb = bbnxt; + } + tpl_hook.free(atyp); +} + +/* determine (by walking) byte length of serialized r/A node at address dv + * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency) + */ +static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) { + uint32_t slen; + int num=0,fidx; + tpl_node *c; + size_t len=0, alen, buf_past, itermax; + tpl_pound_data *pd; + + buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text + + ((tpl_root_data*)(r->data))->mmap.text_sz); + + if (n->type == TPL_TYPE_ROOT) num = 1; + else if (n->type == TPL_TYPE_ARY) { + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&num,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&num, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + len += sizeof(uint32_t); + } else tpl_hook.fatal("internal error in tpl_serlen\n"); + + while (num-- > 0) { + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ + if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1; + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + len += tpl_types[c->type].sz; + } + break; + case TPL_TYPE_BIN: + len += sizeof(uint32_t); + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + len += slen; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if ((uintptr_t)dv + slen > buf_past) return -1; + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ + len += sizeof(uint32_t); + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)) + slen = (slen>1) ? (slen-1) : 0; + len += slen; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if ((uintptr_t)dv + slen > buf_past) return -1; + dv = (void*)((uintptr_t)dv + slen); + } + break; + case TPL_TYPE_ARY: + if ( tpl_serlen(r,c,dv, &alen) == -1) return -1; + dv = (void*)((uintptr_t)dv + alen); + len += alen; + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + } + *serlen = len; + return 0; +} + +static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) { + void *text; + int fd,perms; + +#ifndef _WIN32 + perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */ + fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms); +#else + perms = _S_IWRITE; + fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms); +#endif + + if ( fd == -1 ) { + tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); + return -1; + } + + text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (text == MAP_FAILED) { + tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); + close(fd); + return -1; + } + if (ftruncate(fd,sz) == -1) { + tpl_hook.oops("ftruncate failed: %s\n", strerror(errno)); + munmap( text, sz ); + close(fd); + return -1; + } + *text_out = text; + return fd; +} + +static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) { + struct stat stat_buf; + + if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) { + tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); + return -1; + } + + if ( fstat(mr->fd, &stat_buf) == -1) { + close(mr->fd); + tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno)); + return -1; + } + + mr->text_sz = (size_t)stat_buf.st_size; + mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0); + if (mr->text == MAP_FAILED) { + close(mr->fd); + tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); + return -1; + } + + return 0; +} + +TPL_API int tpl_pack(tpl_node *r, int i) { + tpl_node *n, *child, *np; + void *datav=NULL; + size_t sz, itermax; + uint32_t slen; + char *str; + tpl_bin *bin; + tpl_pound_data *pd; + int fidx; + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_pack\n", i); + return -1; + } + + if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { + /* convert to an writeable tpl, initially empty */ + tpl_free_keep_map(r); + } + + ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY; + + if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n); + child = n->children; + while(child) { + switch(child->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + /* no need to use fidx iteration here; we can copy multiple values in one memcpy */ + memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num); + if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num); + if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num; + break; + case TPL_TYPE_BIN: + /* copy the buffer to be packed */ + slen = ((tpl_bin*)child->addr)->sz; + if (slen >0) { + str = tpl_hook.malloc(slen); + if (!str) fatal_oom(); + memcpy(str,((tpl_bin*)child->addr)->addr,slen); + } else str = NULL; + /* and make a tpl_bin to point to it */ + bin = tpl_hook.malloc(sizeof(tpl_bin)); + if (!bin) fatal_oom(); + bin->addr = str; + bin->sz = slen; + /* now pack its pointer, first deep freeing any pre-existing bin */ + if (*(tpl_bin**)(child->data) != NULL) { + if ((*(tpl_bin**)(child->data))->sz != 0) { + tpl_hook.free( (*(tpl_bin**)(child->data))->addr ); + } + tpl_hook.free(*(tpl_bin**)(child->data)); + } + memcpy(child->data,&bin,sizeof(tpl_bin*)); + if (datav) { + datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*)); + *(tpl_bin**)(child->data) = NULL; + } + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* binary buf len word */ + n->ser_osz += bin->sz; /* binary buf */ + } + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < child->num; fidx++) { + /* copy the string to be packed. slen includes \0. this + block also works if the string pointer is NULL. */ + char *caddr = ((char**)child->addr)[fidx]; + char **cdata = &((char**)child->data)[fidx]; + slen = caddr ? (strlen(caddr) + 1) : 0; + if (slen) { + str = tpl_hook.malloc(slen); + if (!str) fatal_oom(); + memcpy(str,caddr,slen); /* include \0 */ + } else { + str = NULL; + } + /* now pack its pointer, first freeing any pre-existing string */ + if (*cdata != NULL) { + tpl_hook.free(*cdata); + } + memcpy(cdata,&str,sizeof(char*)); + if (datav) { + datav = tpl_cpv(datav, &str, sizeof(char*)); + *cdata = NULL; + } + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* string len word */ + if (slen>1) n->ser_osz += slen-1;/* string (without nul) */ + } + } + break; + case TPL_TYPE_ARY: + /* copy the child's tpl_atype* and reset it to empty */ + if (datav) { + sz = ((tpl_atyp*)(child->data))->sz; + datav = tpl_cpv(datav, &child->data, sizeof(void*)); + child->data = tpl_hook.malloc(sizeof(tpl_atyp)); + if (!child->data) fatal_oom(); + ((tpl_atyp*)(child->data))->num = 0; + ((tpl_atyp*)(child->data))->sz = sz; + ((tpl_atyp*)(child->data))->bb = NULL; + ((tpl_atyp*)(child->data))->bbtail = NULL; + } + /* parent is array? then bubble up child array's ser_osz */ + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* array len word */ + n->ser_osz += child->ser_osz; /* child array ser_osz */ + child->ser_osz = 0; /* reset child array ser_osz */ + } + break; + + case TPL_TYPE_POUND: + /* we need to iterate n times over preceding nodes in S(...). + * we may be in the midst of an iteration each time or starting. */ + pd = (tpl_pound_data*)child->data; + itermax = child->num; + + /* itermax is total num of iterations needed */ + /* pd->iternum is current iteration index */ + /* pd->inter_elt_len is element-to-element len of contiguous structs */ + /* pd->iter_start_node is where we jump to at each iteration. */ + + if (++(pd->iternum) < itermax) { + + /* in start or midst of loop. advance addr/data pointers. */ + for(np=pd->iter_start_node; np != child; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + np->addr = (char*)(np->addr) + pd->inter_elt_len; + } + /* do next iteration */ + child = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != child; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); + } + + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + child=child->next; + } + return 0; +} + +TPL_API int tpl_unpack(tpl_node *r, int i) { + tpl_node *n, *c, *np; + uint32_t slen; + int rc=1, fidx; + char *str; + void *dv=NULL, *caddr; + size_t A_bytes, itermax; + tpl_pound_data *pd; + void *img; + size_t sz; + + + /* handle unusual case of tpl_pack,tpl_unpack without an + * intervening tpl_dump. do a dump/load implicitly. */ + if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) { + if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1; + if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) { + tpl_hook.free(img); + return -1; + }; + } + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_unpack\n", i); + return -1; + } + + /* either root node or an A node */ + if (n->type == TPL_TYPE_ROOT) { + dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text ); + } else if (n->type == TPL_TYPE_ARY) { + if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */ + else rc = ((tpl_atyp*)(n->data))->num--; + dv = ((tpl_atyp*)(n->data))->cur; + if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n"); + } + + c = n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + /* unpack elements of cross-endian octothorpic array individually */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) { + for(fidx=0; fidx < c->num; fidx++) { + caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz)); + memcpy(caddr,dv,tpl_types[c->type].sz); + tpl_byteswap(caddr, tpl_types[c->type].sz); + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + } + } else { + /* bulk unpack ok if not cross-endian */ + memcpy(c->addr, dv, tpl_types[c->type].sz * c->num); + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num); + } + break; + case TPL_TYPE_BIN: + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (slen > 0) { + str = (char*)tpl_hook.malloc(slen); + if (!str) fatal_oom(); + } else str=NULL; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen>0) memcpy(str,dv,slen); + memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*)); + memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < c->num; fidx++) { + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) + slen += 1; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen) { /* slen includes \0 */ + str = (char*)tpl_hook.malloc(slen); + if (!str) fatal_oom(); + if (slen>1) memcpy(str,dv,slen-1); + str[slen-1] = '\0'; /* nul terminate */ + dv = (void*)((uintptr_t)dv + slen-1); + } else str=NULL; + memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*)); + } + break; + case TPL_TYPE_POUND: + /* iterate over preceding nodes */ + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + /* in start or midst of loop. advance addr/data pointers. */ + for(np=pd->iter_start_node; np != c; np = np->next) { + np->addr = (char*)(np->addr) + pd->inter_elt_len; + } + /* do next iteration */ + c = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); + } + + } + break; + case TPL_TYPE_ARY: + if (tpl_serlen(r,c,dv, &A_bytes) == -1) + tpl_hook.fatal("internal error in unpack\n"); + memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); + ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + A_bytes); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + c = c->next; + } + if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */ + return rc; +} + +/* Specialized function that unpacks only the root's A nodes, after tpl_load */ +static int tpl_unpackA0(tpl_node *r) { + tpl_node *n, *c; + uint32_t slen; + int rc=1,fidx,i; + void *dv; + size_t A_bytes, itermax; + tpl_pound_data *pd; + + n = r; + dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text); + + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + for(fidx=0;fidx < c->num; fidx++) { + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + } + break; + case TPL_TYPE_BIN: + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(i=0; inum; i++) { + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) + slen += 1; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen>1) dv = (void*)((uintptr_t)dv + slen-1); + } + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + case TPL_TYPE_ARY: + if ( tpl_serlen(r,c,dv, &A_bytes) == -1) + tpl_hook.fatal("internal error in unpackA0\n"); + memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); + ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + A_bytes); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + return rc; +} + +/* In-place byte order swapping of a word of length "len" bytes */ +static void tpl_byteswap(void *word, int len) { + int i; + char c, *w; + w = (char*)word; + for(i=0; i0) ? rc : 0; + } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8)); + + if (rc<0) { + tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno)); + return -1; + } else if (rc == 0) { + /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */ + return 0; + } else if (i != 8) { + tpl_hook.oops("internal error\n"); + return -1; + } + + if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') { + memcpy(&tpllen,&preamble[4],4); + if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4); + } else { + tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n"); + return -1; + } + + /* malloc space for remainder of tpl image (overall length tpllen) + * and read it in + */ + if (tpl_hook.gather_max > 0 && + tpllen > tpl_hook.gather_max) { + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; + } + *sz = tpllen; + if ( (*img = tpl_hook.malloc(tpllen)) == NULL) { + fatal_oom(); + } + + memcpy(*img,preamble,8); /* copy preamble to output buffer */ + i=8; + do { + rc = read(fd,&((*(char**)img)[i]),tpllen-i); + i += (rc>0) ? rc : 0; + } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && iimg); + tpl_hook.free(*gs); + *gs = NULL; + } + return -1; /* error, caller should close fd */ + } + } else if (rc == 0) { + if (*gs) { + tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n"); + tpl_hook.free((*gs)->img); + tpl_hook.free(*gs); + *gs = NULL; + } + return 0; /* EOF, caller should close fd */ + } else { + /* concatenate any partial tpl from last read with new buffer */ + if (*gs) { + catlen = (*gs)->len + rc; + if (tpl_hook.gather_max > 0 && + catlen > tpl_hook.gather_max) { + tpl_hook.free( (*gs)->img ); + tpl_hook.free( (*gs) ); + *gs = NULL; + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; /* error, caller should close fd */ + } + if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { + fatal_oom(); + } + memcpy(img + (*gs)->len, buf, rc); + tpl_hook.free(*gs); + *gs = NULL; + } else { + img = buf; + catlen = rc; + } + /* isolate any full tpl(s) in img and invoke cb for each */ + tpl = img; + keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + while (keep_looping) { + if (strncmp("tpl", tpl, 3) != 0) { + tpl_hook.oops("tpl prefix invalid\n"); + if (img != buf) tpl_hook.free(img); + tpl_hook.free(*gs); + *gs = NULL; + return -3; /* error, caller should close fd */ + } + memcpy(&tpllen,&tpl[4],4); + if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); + if (tpl+tpllen <= img+catlen) { + cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ + tpl += tpllen; /* point to next tpl image */ + if (cbrc < 0) keep_looping = 0; + else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + } else keep_looping=0; + } + /* check if app callback requested closure of tpl source */ + if (cbrc < 0) { + tpl_hook.oops("tpl_fd_gather aborted by app callback\n"); + if (img != buf) tpl_hook.free(img); + if (*gs) tpl_hook.free(*gs); + *gs = NULL; + return -4; + } + /* store any leftover, partial tpl fragment for next read */ + if (tpl == img && img != buf) { + /* consumed nothing from img!=buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + (*gs)->img = tpl; + (*gs)->len = catlen; + } else if (tpl < img+catlen) { + /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { + fatal_oom(); + } + (*gs)->len = img+catlen - tpl; + memcpy( (*gs)->img, tpl, img+catlen - tpl); + /* free partially consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } else { /* tpl(s) fully consumed */ + /* free consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } + } + } +} + +/* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */ +static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) { + char *img, *tpl; + int keep_looping, cbrc=0; + size_t catlen; + uint32_t tpllen; + + /* concatenate any partial tpl from last read with new buffer */ + if (*gs) { + catlen = (*gs)->len + len; + if (tpl_hook.gather_max > 0 && + catlen > tpl_hook.gather_max) { + tpl_hook.free( (*gs)->img ); + tpl_hook.free( (*gs) ); + *gs = NULL; + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; /* error, caller should stop accepting input from source*/ + } + if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { + fatal_oom(); + } + memcpy(img + (*gs)->len, buf, len); + tpl_hook.free(*gs); + *gs = NULL; + } else { + img = buf; + catlen = len; + } + /* isolate any full tpl(s) in img and invoke cb for each */ + tpl = img; + keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + while (keep_looping) { + if (strncmp("tpl", tpl, 3) != 0) { + tpl_hook.oops("tpl prefix invalid\n"); + if (img != buf) tpl_hook.free(img); + tpl_hook.free(*gs); + *gs = NULL; + return -3; /* error, caller should stop accepting input from source*/ + } + memcpy(&tpllen,&tpl[4],4); + if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); + if (tpl+tpllen <= img+catlen) { + cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ + tpl += tpllen; /* point to next tpl image */ + if (cbrc < 0) keep_looping = 0; + else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + } else keep_looping=0; + } + /* check if app callback requested closure of tpl source */ + if (cbrc < 0) { + tpl_hook.oops("tpl_mem_gather aborted by app callback\n"); + if (img != buf) tpl_hook.free(img); + if (*gs) tpl_hook.free(*gs); + *gs = NULL; + return -4; + } + /* store any leftover, partial tpl fragment for next read */ + if (tpl == img && img != buf) { + /* consumed nothing from img!=buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + (*gs)->img = tpl; + (*gs)->len = catlen; + } else if (tpl < img+catlen) { + /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { + fatal_oom(); + } + (*gs)->len = img+catlen - tpl; + memcpy( (*gs)->img, tpl, img+catlen - tpl); + /* free partially consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } else { /* tpl(s) fully consumed */ + /* free consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } + return 1; +} diff --git a/src/tpl.h b/src/tpl.h new file mode 100644 index 0000000..2a2006c --- /dev/null +++ b/src/tpl.h @@ -0,0 +1,137 @@ +/* +Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TPL_H +#define TPL_H + +#include /* size_t */ + +#include /* va_list */ + +#ifdef __INTEL_COMPILER +#include +#endif /* Intel Compiler efficient memcpy etc */ + +#ifdef _MSC_VER +typedef unsigned int uint32_t; +#else +#include /* uint32_t */ +#endif + +#if defined __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#ifdef TPL_EXPORTS +#define TPL_API __declspec(dllexport) +#else /* */ +#ifdef TPL_NOLIB +#define TPL_API +#else +#define TPL_API __declspec(dllimport) +#endif /* TPL_NOLIB */ +#endif /* TPL_EXPORTS*/ +#else +#define TPL_API +#endif + +/* bit flags (external) */ +#define TPL_FILE (1 << 0) +#define TPL_MEM (1 << 1) +#define TPL_PREALLOCD (1 << 2) +#define TPL_EXCESS_OK (1 << 3) +#define TPL_FD (1 << 4) +#define TPL_UFREE (1 << 5) +#define TPL_DATAPEEK (1 << 6) +#define TPL_FXLENS (1 << 7) +#define TPL_GETSIZE (1 << 8) +/* do not add flags here without renumbering the internal flags! */ + +/* flags for tpl_gather mode */ +#define TPL_GATHER_BLOCKING 1 +#define TPL_GATHER_NONBLOCKING 2 +#define TPL_GATHER_MEM 3 + +/* Hooks for error logging, memory allocation functions and fatal */ +typedef int (tpl_print_fcn)(const char *fmt, ...); +typedef void *(tpl_malloc_fcn)(size_t sz); +typedef void *(tpl_realloc_fcn)(void *ptr, size_t sz); +typedef void (tpl_free_fcn)(void *ptr); +typedef void (tpl_fatal_fcn)(const char *fmt, ...); + +typedef struct tpl_hook_t { + tpl_print_fcn *oops; + tpl_malloc_fcn *malloc; + tpl_realloc_fcn *realloc; + tpl_free_fcn *free; + tpl_fatal_fcn *fatal; + size_t gather_max; +} tpl_hook_t; + +typedef struct tpl_node { + int type; + void *addr; + void *data; /* r:tpl_root_data*. A:tpl_atyp*. ow:szof type */ + int num; /* length of type if its a C array */ + size_t ser_osz; /* serialization output size for subtree */ + struct tpl_node *children; /* my children; linked-list */ + struct tpl_node *next,*prev; /* my siblings (next child of my parent) */ + struct tpl_node *parent; /* my parent */ +} tpl_node; + +/* used when un/packing 'B' type (binary buffers) */ +typedef struct tpl_bin { + void *addr; + uint32_t sz; +} tpl_bin; + +/* for async/piecemeal reading of tpl images */ +typedef struct tpl_gather_t { + char *img; + int len; +} tpl_gather_t; + +/* Callback used when tpl_gather has read a full tpl image */ +typedef int (tpl_gather_cb)(void *img, size_t sz, void *data); + +/* Prototypes */ +TPL_API tpl_node *tpl_map(char *fmt,...); /* define tpl using format */ +TPL_API void tpl_free(tpl_node *r); /* free a tpl map */ +TPL_API int tpl_pack(tpl_node *r, int i); /* pack the n'th packable */ +TPL_API int tpl_unpack(tpl_node *r, int i); /* unpack the n'th packable */ +TPL_API int tpl_dump(tpl_node *r, int mode, ...); /* serialize to mem/file */ +TPL_API int tpl_load(tpl_node *r, int mode, ...); /* set mem/file to unpack */ +TPL_API int tpl_Alen(tpl_node *r, int i); /* array len of packable i */ +TPL_API char* tpl_peek(int mode, ...); /* sneak peek at format string */ +TPL_API int tpl_gather( int mode, ...); /* non-blocking image gather */ +TPL_API int tpl_jot(int mode, ...); /* quick write a simple tpl */ + +TPL_API tpl_node *tpl_map_va(char *fmt, va_list ap); + +#if defined __cplusplus + } +#endif + +#endif /* TPL_H */ + diff --git a/src/win/Makefile.am b/src/win/Makefile.am new file mode 100644 index 0000000..8282237 --- /dev/null +++ b/src/win/Makefile.am @@ -0,0 +1,4 @@ +noinst_LTLIBRARIES = libwinmmap.la +noinst_HEADERS = mman.h +libwinmmap_la_SOURCES = nonempty.c +libwinmmap_la_LIBADD = @LTLIBOBJS@ diff --git a/src/win/README b/src/win/README new file mode 100644 index 0000000..4d055c1 --- /dev/null +++ b/src/win/README @@ -0,0 +1,11 @@ +This directory contains functions that are missing on the Windows platform. In +particular, mmap, munmap, and msync are not available on Windows. These +replacements are used on both Cygwin and MinGW. (On Cygwin the built-in mmap +has no write support, and is not used). + +mmap.c +mman.h + +Special thanks to Horea Haitonic for contributing mmap.c and mman.h. + +April 2007 diff --git a/src/win/mman.h b/src/win/mman.h new file mode 100644 index 0000000..9f97ac0 --- /dev/null +++ b/src/win/mman.h @@ -0,0 +1,52 @@ +#ifndef _MMAN_H_ +#define _MMAN_H_ + +/* Protections */ +#define PROT_NONE 0x00 /* no permissions */ +#define PROT_READ 0x01 /* pages can be read */ +#define PROT_WRITE 0x02 /* pages can be written */ +#define PROT_EXEC 0x04 /* pages can be executed */ + +/* Sharing type and options */ +#define MAP_SHARED 0x0001 /* share changes */ +#define MAP_PRIVATE 0x0002 /* changes are private */ +#define MAP_COPY MAP_PRIVATE /* Obsolete */ +#define MAP_FIXED 0x0010 /* map addr must be exactly as requested */ +#define MAP_RENAME 0x0020 /* Sun: rename private pages to file */ +#define MAP_NORESERVE 0x0040 /* Sun: don't reserve needed swap area */ +#define MAP_INHERIT 0x0080 /* region is retained after exec */ +#define MAP_NOEXTEND 0x0100 /* for MAP_FILE, don't change file size */ +#define MAP_HASSEMAPHORE 0x0200 /* region may contain semaphores */ +#define MAP_STACK 0x0400 /* region grows down, like a stack */ + +/* Error returned from mmap() */ +#define MAP_FAILED ((void *)-1) + +/* Flags to msync */ +#define MS_ASYNC 0x01 /* perform asynchronous writes */ +#define MS_SYNC 0x02 /* perform synchronous writes */ +#define MS_INVALIDATE 0x04 /* invalidate cached data */ + +/* File modes for 'open' not defined in MinGW32 (not used by mmap) */ +#ifndef S_IWGRP +#define S_IWGRP 0 +#define S_IRGRP 0 +#define S_IROTH 0 +#endif + +/** + * Map a file to a memory region + */ +void *mmap(void *addr, unsigned int len, int prot, int flags, int fd, unsigned int offset); + +/** + * Unmap a memory region + */ +int munmap(void *addr, int len); + +/** + * Synchronize a mapped region + */ +int msync(char *addr, int len, int flags); + +#endif /* _MMAN_H_ */ diff --git a/src/win/mmap.c b/src/win/mmap.c new file mode 100644 index 0000000..268cb5a --- /dev/null +++ b/src/win/mmap.c @@ -0,0 +1,171 @@ +#include +#include +#ifdef _WIN32 +#include +#endif +#include +#include "mman.h" + +static const char id[]="$Id: tpl.c 107 2007-04-20 17:11:29Z thanson $"; + +/** + * @brief Map a file to a memory region + * + * This function emulates the POSIX mmap() using CreateFileMapping() and + * MapViewOfFile() + * + * @param addr the suggested start address (if != 0) + * @param len length of the region + * @param prot region accesibility, bitwise OR of PROT_READ, PROT_WRITE, PROT_EXEC + * @param flags mapping type and options (ignored) + * @param fd object to be mapped into memory + * @param offset offset into mapped object + * @return pointer to the memory region, or NULL in case of error + */ +void *mmap(void *addr, unsigned int len, int prot, int flags, int fd, unsigned int offset) +{ + DWORD wprot; + DWORD waccess; + HANDLE h; + void *region; + + /* Translate read/write/exec flags into WIN32 constants */ + switch (prot) { + case PROT_READ: + wprot = PAGE_READONLY; + break; + case PROT_EXEC: + wprot = PAGE_EXECUTE_READ; + break; + case PROT_READ | PROT_EXEC: + wprot = PAGE_EXECUTE_READ; + break; + case PROT_WRITE: + wprot = PAGE_READWRITE; + break; + case PROT_READ | PROT_WRITE: + wprot = PAGE_READWRITE; + break; + case PROT_READ | PROT_WRITE | PROT_EXEC: + wprot = PAGE_EXECUTE_READWRITE; + break; + case PROT_WRITE | PROT_EXEC: + wprot = PAGE_EXECUTE_READWRITE; + break; + } + + /* Obtaing handle to map region */ + h = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, wprot, 0, len, 0); + if (h == NULL) { + DWORD error = GetLastError(); + + /* Try and translate some error codes */ + switch (error) { + case ERROR_ACCESS_DENIED: + case ERROR_INVALID_ACCESS: + errno = EACCES; + break; + case ERROR_OUTOFMEMORY: + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + default: + errno = EINVAL; + break; + } + return MAP_FAILED; + } + + + /* Translate sharing options into WIN32 constants */ + switch (wprot) { + case PAGE_READONLY: + waccess = FILE_MAP_READ; + break; + case PAGE_READWRITE: + waccess = FILE_MAP_WRITE; + break; + } + + /* Map file and return pointer */ + region = MapViewOfFile(h, waccess, 0, 0, 0); + if (region == NULL) { + DWORD error = GetLastError(); + + /* Try and translate some error codes */ + switch (error) { + case ERROR_ACCESS_DENIED: + case ERROR_INVALID_ACCESS: + errno = EACCES; + break; + case ERROR_INVALID_HANDLE: + errno = EBADF; + break; + default: + errno = EINVAL; + break; + } + CloseHandle(h); + return MAP_FAILED; + } + CloseHandle(h); /* ok to call UnmapViewOfFile after this */ + + /* All fine */ + return region; +} + + +/** + * @brief Unmap a memory region + * + * This is a wrapper around UnmapViewOfFile in the win32 API + * + * @param addr start address + * @param len length of the region + * @return 0 for success, -1 for error + */ +int munmap(void *addr, int len) +{ + if (UnmapViewOfFile(addr)) { + return 0; + } + else { + errno = EINVAL; + return -1; + } +} + + +/** + * Synchronize a mapped region + * + * This is a wrapper around FlushViewOfFile + * + * @param addr start address + * @param len number of bytes to flush + * @param flags sync options -- currently ignored + * @return 0 for success, -1 for error + */ +int msync(char *addr, int len, int flags) +{ + if (FlushViewOfFile(addr, len) == 0) { + DWORD error = GetLastError(); + + /* Try and translate some error codes */ + switch (error) { + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_WRITE_FAULT: + errno = EIO; + break; + default: + errno = EINVAL; + break; + } + return -1; + } + + /* Success */ + return 0; +} diff --git a/src/win/nonempty.c b/src/win/nonempty.c new file mode 100644 index 0000000..5c02f16 --- /dev/null +++ b/src/win/nonempty.c @@ -0,0 +1,6 @@ +/* This function exists solely to prevent libwinmmap.la from being empty. Empty + * libraries cause problems on some platforms, e.g. Mac OS X. */ + +int tpl_nonempty(int i) { + return i+1; +} diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..6ebd127 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,88 @@ +# Makefile for tpl built-in test suite +# +# This Makefile has three useful targets: +# +# all (default): +# Build and run all self-tests by compiling tpl into each test. +# +# Note: On Cygwin/MinGW, compiling the tpl source directly into the tests is +# not supported (as they use 'replacement' functions which get included only +# in libtpl), so on these platforms the 'alt' target is the default. +# +# alt: +# Build and run all the self-tests by linking them with libtpl, which must +# have been created beforehand (by running 'configure; make' in the +# top-level directory); a reminder will be printed if you have not done so. +# +# Note, libtool will create wrappers around each test to accomodate the +# pre-installed state of ../src/libtpl.la. In a real program, you'd link +# against an installed libtpl.la, and these wrappers would not be used. +# +# clean: +# Clean up all the compiled bits. +# +PROGS = test1 test2 test3 test4 test5 test6 test7 test8 \ + test9 test10 test11 test12 test13 test14 test15 test16 \ + test17 test18 test19 test20 test21 test22 test23 test24 \ + test25 test26 test27 test28 test29 test30 test31 test32 \ + test33 test34 test35 test36 test37 test38 test39 test40 \ + test41 test42 test43 test44 test45 test46 test47 test48 \ + test49 test50 test51 test52 test53 test54 test55 test56 \ + test57 test58 test59 test60 test61 test62 test63 test64 \ + test65 test66 test67 test68 test69 test70 test71 test72 \ + test73 test74 test75 test76 test77 test78 test79 test80 \ + test81 test82 test83 test84 test85 test86 test87 test88 \ + test89 test90 test91 test92 test93 test94 test95 test96 \ + test97 test98 test99 test100 test101 test102 test103 test104 \ + test105 test106 test107 test108 test109 test110 test111 test112 \ + test113 test114 test115 test116 test117 test118 test119 test120 \ + test121 test122 test123 test124 + +TPLSRC = ../src +CFLAGS = -I$(TPLSRC) -g +CFLAGS += -pedantic +CFLAGS += -Wall +#CFLAGS += -m32 +#CFLAGS += -m64 +CFLAGS += -O3 +#For testing without C99 feature support +#CFLAGS += -std=c89 + +# Prefer 64-bit compilation on Mac OS X (not necessary, just faster) +ifneq ($(strip $(shell $(CC) -v 2>&1 |egrep "i[0-9]+-apple-darwin")),) + CFLAGS += -m64 +endif + +# detect Cygwin or MinGW +ifneq ($(strip $(shell $(CC) -v 2>&1 |egrep "cygwin|mingw")),) + TESTS=./do_tests.cygwin + # divert to the alt target; use tpl as a lib for Cygwin/Mingw + TARGET=alt +else + TESTS=./do_tests + TARGET=$(PROGS) run_tests +endif + +all: $(TARGET) + +tpl.o : $(TPLSRC)/tpl.c $(TPLSRC)/tpl.h + $(CC) -c $(CFLAGS) $(TPLSRC)/tpl.c + +$(PROGS) : tpl.o + $(CC) $(CFLAGS) -o $@ $(@).c tpl.o + +run_tests: + perl $(TESTS) + +# This target can be used to compile the tests as dependent on +# the tpl library (rather than compiling in the tpl source). +alt: mkalttests run_tests + +mkalttests: + @$(MAKE) -f Makefile.alt PROGS="$(PROGS)" + +.PHONY: clean + +clean: + rm -f $(PROGS) tpl.o test*.out test*.err test*.exe + rm -rf $(PROGS) test*.dSYM diff --git a/tests/Makefile.alt b/tests/Makefile.alt new file mode 100644 index 0000000..054b678 --- /dev/null +++ b/tests/Makefile.alt @@ -0,0 +1,31 @@ +# This Makefile.alt is subordinate to Makefile (usage: make alt) +# Its distinction is that it builds the test programs by linking +# them with libtpl rather than by compiling tpl.c into the tests. +SRC = ../src +LIBTOOL = ../libtool +LTLIB = $(SRC)/libtpl.la +CFLAGS = -I$(SRC) -g + +# We have an alternate basic test for MinGW +ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "mingw")),) + TARGET=mingw +else + TARGET=$(PROGS) +endif + +all: $(TARGET) + + +$(PROGS) : $(LTLIB) + $(CC) -c $(CFLAGS) $(@).c + $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $(@).o $(LTLIB) + +$(LTLIB) : + @echo "you must first run 'configure; make' in the top-level directory" + @exit 1 + +mingw : + @echo "" + @echo "MinGW has its own test suite. Please run make -f Makefile.mingw" + @echo "" + @exit 1 diff --git a/tests/Makefile.mingw b/tests/Makefile.mingw new file mode 100644 index 0000000..dbf7906 --- /dev/null +++ b/tests/Makefile.mingw @@ -0,0 +1,25 @@ +# This makes a small Mingw test program. The standard test suite +# is incompatible with MinGW because it uses UNIX facilities such +# as pipe/fork and the /tmp directory, not features of Windows. +SRC = ../src +LIBTOOL = ../libtool +LTLIB = $(SRC)/libtpl.la +CFLAGS = -I$(SRC) -g + +PROG = mgwtest + +all: $(PROG) + +$(PROG) : $(LTLIB) + $(CC) -c $(CFLAGS) $(@).c + $(LIBTOOL) --mode=link --tag=CC $(CC) -o $@ $(@).o $(LTLIB) -mwindows + @echo "you can now run windows application: $@" + +$(LTLIB) : + @echo "you must first run 'configure; make' in the top-level directory" + @exit 1 + +.PHONY: clean + +clean: + rm $(PROG) $(PROG).exe $(PROG).o $(PROG).tpl diff --git a/tests/README b/tests/README new file mode 100644 index 0000000..eef1f17 --- /dev/null +++ b/tests/README @@ -0,0 +1,125 @@ +Run "make" in this directory to build the tests and run them. + +test1: serialize int into memory, unserialize +test2: serialize int into file, unserialize +test3: serialize A(i) into file, unserialize +test4: serialize A(i) into memory, unserialize +test5: serialize A(A(i)) into memory, unserialize +test6: serialize string into memory, unserialize +test7: serialize A(s) into file, unserialize +test8: serialize cA(c) into file, unserialize +test9: unpack big-endian tpl data file of A(i) +test10: unpack little-endian tpl data file of A(i) +test11: try to load a corrupt tpl image w/invalid chars +test12: try to load a corrupt tpl image w/runaway format string +test13: try to load a corrupt tpl image w/internal A length -1 +test14: try to load a corrupt tpl image w/internal A length +1 +test15: try to load a corrupt tpl image w/invalid magic +test16: try to load a corrupt tpl image w/invalid len +test17: try to load a good tpl but whose format mismatches map +test18: try to map a tpl with malformed format- unbalanced parens: missing ) +test19: try to map a tpl with malformed format- unbalanced parens: extra ) +test20: try to map a tpl with malformed format- empty A() +test21: serialize A(ii) into file, unserialize +test22: serialize A(u) into file, unserialize +test23: serialize A(u) into file, read tpl from fd using TPL_FD +test24: read A(u) tpl file with extra trailing bytes, ok in TPL_FD mode +test25: same A(u) tpl file with extra trailing bytes, not ok in TPL_FILE mode +test26: test non-blocking tpl_gather using async read of 3 tpls across 2 pipes +test27: test tpl_dump() of A(u) to file using TPL_FD, unserialize +test28: parent writes A(u) tpl to child through pipe, both use TPL_FD mode +test29: parent writes consecutive A(u) tpl then A(c) tpl to child through pipe +test30: test pack B (binary buffer) and unpack +test31: test pack B (binary buffer) of 0-length and unpack +test32: test pack A(B) and unpack +test33: test pack f (double) and unpack +test34: test pack A(f) and unpack +test35: pack A(is) +test36: unpack A(is) +test37: pack A(A(i)) [example from man page] +test38: unpack A(A(i)) [example from man page] +test39: try to load a tpl with an unsupported bit flag set +test40: pack char array - userguide example +test41: unpack char arrray - userguide example +test42: test non-aligned pointers in backbone (under Solaris dbx, check -all) +test43: test non-aligned pointers in backbone (under Solaris dbx, check -all) +test44: test non-aligned pointers in backbone (under Solaris dbx, check -all) +test46: test correct-size of backbone "double" datum (Solaris dbx, check -all) +test47: store A(i) to file - userguide example +test48: read A(i) from file - userguide example +test49: write A(s) - userguide example +test50: read A(s) - userguide example +test51: test tpl_mem_gather (_0: 1 tpl; _1: 2 tpls; _2/_3/_4: 1 tpl in 3 parts) +test52: A(A(i)): pack an int; pack parent; pack int; don't pack parent; ser_osz +test53: A(A(i)): pack an int; pack parent; pack parent; 0-length 2nd parent el. +test54: test callback negative return value for tpl_mem_gather +test55: test callback negative return value for tpl_fd_gather +test56: test static string using c# format pack/unpack in mem +test57: test pack static string using c# to file +test58: test unpack static string using c# from file +test59: test alignment using cc#cc# pack/unpack in mem +test60: test pack-then-load (implicit intervening free, using tpl_free_keep_map) +test61: test load-then-load (implicit intervening free, using tpl_free_keep_map) +test62: test load-then-pack (implicit intervening free, using tpl_free_keep_map) +test63: test pack-then-unpack (implicit dump/load) then pack-then-unpack again +test64: pack level 0 types, change and re-pack level 0 types, test implicit free +test65: pack int[] using format character # +test66: pack two separate int[] using format character # +test67: test expected failure if format strings agree but array lengths mismatch +test68: test octothorpe support by packing,unpacking two fixed lengths arrays +test69: test octothorpic array support A(i#i#) +test70: test S(...) structure pack and unpack +test71: test cS(...) pack/unpack when preceded by non-structure byte +test72: test wildcard structure unpack +test73: test wildcard structure unpack +test74: test wildcard structure unpack +test75: test sc# (string and byte array) +test76: test S(sc#) (structure of last) +test77: test S(sc#) (structure of last) with wildcard unpack +test78: pack A(i)c +test79: unpack A(i)c +test80: pack and unpack A(S(ci#)) +test81: pack and unpack A(S(ci#)) +test82: pack cA(i#)S(cf#)A(ci#) +test83: unpack cA(i#)S(cf#)A(ci#) +test84: repeat test83 with both big and little endian input files +test85: tpl_peek at file +test86: tpl_peek at in-memory tpl +test87: test tpl_gather(TPL_GATHER_FD_BLOCKING) +test88: test packing S(ic#f) +test89: test unpacking S(ic#f) as S(*) +test90: pack and unpack I +test91: pack and unpack U +test92: pack and unpack A(cIcU) +test93: pack and unpack NULL string +test94: pack and unpack A(s) with some null +test95: pack and unpack null string, empty string, non-empty string +test96: pack and unpack A(null string, empty string, non-empty string) +test97: pack and unapck 16-bit int/uint (j,v) +test98: pack and unapck 16-bit int/uint A(j,v) +test99: data peek at c in complex format +test100: data peek at i inside S(ic) +test101: data peek at c inside S(ic) [expected failure test] +test102: data peek at c in simple format c +test103: data peek at iscsi in S(iscsiu) +test104: data peek at iscsi in S(iscsiu) with NULL string pointer +test105: tpl_jot then unpack by normal then by tpl_peek +test106: test IS(Iiuijc#)#iiii +test107: test S(ic#)# +test108: test IS(Iiuijc#)#iiii +test109: test S(cijc)# where next structure elt alignment based on i +test110: test ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii +test111: test S(icfv#)# +test112: test S(ic#fv#)# +test113: test icS(ic#fv#)#ci +test114: test multi-dimension i## +test115: test S(s)#, S(si)#, S(c#si)# +test116: test cs#i +test117: test cA(s#)i +test118: test tpl_peek(TPL_FXLENS) with cA(i#)S(cf#)A(ci#) +test119: test tpl_dump(tn,TPL_GETSIZE,&sz); +test120: test TPL_PREALLOCD and TPL_EXCESS_OK flags +test121: test s## +test122: test S(ic#f$(ci)) +test123: setjmp/longjmp based fatal error handler +test124: test A(S(c#)s) as per bug report from Eric Rose diff --git a/tests/dbx.sh b/tests/dbx.sh new file mode 100644 index 0000000..c9fd876 --- /dev/null +++ b/tests/dbx.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# This script works with Solaris Studio 11 +# version of dbx which supports "check -all" and +# "check -access" run modes, at least on Sparc. +# These detect access or alignment violations or +# leftover unfreed memory. TDH 29Dec06 + +DBX=/opt/SUNWspro/bin/dbx +OUT=/tmp/dbx.out.$$ + +echo "Writing $OUT..." + +for f in test? test?? +do + echo $f + ${DBX} $f 1>>${OUT} 2>&1 < $test.out 2> $test.err`; + `diff $test.out $test.ans`; + print "$test failed\n" if $?; + $num_failed++ if $?; + unlink "$test.err" if -z "$test.err"; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; +exit $num_failed; diff --git a/tests/do_tests.cygwin b/tests/do_tests.cygwin new file mode 100755 index 0000000..a9b72e0 --- /dev/null +++ b/tests/do_tests.cygwin @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "test*[0-9].exe") { + push @tests, "$_" if -e substr($_, 0, - 4).".ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out`; + my $ansfile = substr($test, 0, - 4).".ans"; + `diff $test.out $ansfile`; + print "$test failed\n" if $?; + $num_failed++ if $?; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; diff --git a/tests/int64_align.c b/tests/int64_align.c new file mode 100644 index 0000000..a1a7bfc --- /dev/null +++ b/tests/int64_align.c @@ -0,0 +1,19 @@ +#include +#include + +/* try compiling this with -m32 vs -m64 + * + * with mac os x and gcc, + * on -m32 the int64_t gets aligned at +4 + * on -m64 the int64_t gets aligned at +8 + */ + +static const struct s_t { + int i; + int64_t j; +} s; + +int main() { + if ((long)&s.j % 8 != 0) printf("non-aligned int64\n"); + else printf("aligned int64\n"); +} diff --git a/tests/malign.c b/tests/malign.c new file mode 100644 index 0000000..c3a3228 --- /dev/null +++ b/tests/malign.c @@ -0,0 +1,20 @@ +#include + +/* try compiling this with and without aligned doubles + * + * cc -malign-double -o malign malign.c + * cc -mno-align-double -o malign malign.c + * + * on x86, double is not normally aligned (unless -malign-double is used). + * but on Sparc, or x86-64, double is aligned. + */ + +static const struct s_t { + char a; + double d; +} s; + +int main() { + if ((long)&s.d % 8 != 0) printf("-mno-align-double\n"); + else printf("-malign-double\n"); +} diff --git a/tests/mgwtest.c b/tests/mgwtest.c new file mode 100644 index 0000000..47528fc --- /dev/null +++ b/tests/mgwtest.c @@ -0,0 +1,56 @@ +#include +#include "tpl.h" + +int WINAPI WinMain (HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PSTR szCmdLine, + int iCmdShow) +{ + char *status; + int rc=0,i,j; + tpl_node *tn; + void *img; + size_t sz; + + tn = tpl_map("A(i)", &i); + for(i=0; i<10; i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_MEM,&img, &sz); + tpl_free(tn); + + j=0; + tn = tpl_map("A(i)", &i); + tpl_load(tn,TPL_MEM,img,sz); + while(tpl_unpack(tn,1) > 0) { + if (i != j++) { + rc = -1; + break; + } + } + tpl_free(tn); + + MessageBox (NULL, (rc==0)?"Test1 passed":"Test1 failed", + "MinGW Tpl Test", MB_OK); + + /* Test 2 */ + tn = tpl_map("A(i)", &i); + for(i=0; i<10; i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"mgwtest.tpl"); + tpl_free(tn); + + j=0; + tn = tpl_map("A(i)", &i); + tpl_load(tn,TPL_FILE,"mgwtest.tpl"); + while(tpl_unpack(tn,1) > 0) { + if (i != j++) { + rc = -1; + break; + } + } + tpl_free(tn); + + MessageBox (NULL, (rc==0)?"Test2 passed":"Test2 failed", + "MinGW Tpl Test", MB_OK); + + return (0); +} + diff --git a/tests/other/Makefile b/tests/other/Makefile new file mode 100644 index 0000000..04cbb2d --- /dev/null +++ b/tests/other/Makefile @@ -0,0 +1,21 @@ +SRCDIR=../../src +CPPFLAGS = -I$(SRCDIR) + +PROGS = other1 +all: $(PROGS) run_tests + +tpl.c: + cp $(SRCDIR)/tpl.c . + +other1: other1.o tpl.o + g++ -o other1 other1.o tpl.o + + +.PHONY: clean run_tests + +clean: + rm -f *.o $(PROGS) *.out + rm -f tpl.c + +run_tests: + perl ./do_tests diff --git a/tests/other/README b/tests/other/README new file mode 100644 index 0000000..d3fd757 --- /dev/null +++ b/tests/other/README @@ -0,0 +1,2 @@ +Assorted other tests that are not part of the standard test suite. +other1: a C++ program using "this" as a structure pointer diff --git a/tests/other/do_tests b/tests/other/do_tests new file mode 100755 index 0000000..0b62640 --- /dev/null +++ b/tests/other/do_tests @@ -0,0 +1,22 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "other*[0-9]") { + push @tests, $_ if -e "$_.ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out 2> $test.err`; + `diff $test.out $test.ans`; + print "$test failed\n" if $?; + $num_failed++ if $?; + unlink "$test.err" if -z "$test.err"; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; +exit $num_failed; diff --git a/tests/other/other1.ans b/tests/other/other1.ans new file mode 100644 index 0000000..f32671a --- /dev/null +++ b/tests/other/other1.ans @@ -0,0 +1,3 @@ +buffer: tpl +i: 9999 +c: this is a test string. diff --git a/tests/other/other1.cpp b/tests/other/other1.cpp new file mode 100644 index 0000000..ce7f7ba --- /dev/null +++ b/tests/other/other1.cpp @@ -0,0 +1,40 @@ +#include +#include +#include "tpl.h" +main() { +void *buffer; +size_t bsize; + +struct ci { + int i; + char c[30]; + + void pack( void **buffer, size_t *size ) + { + tpl_node *tn = tpl_map("S(ic#)", this, 30); /* pass structure address */ + tpl_pack(tn, 0); + tpl_dump(tn, TPL_MEM, buffer, size); + tpl_free(tn); + } + + void unpack( void *buffer, size_t size ) + { + tpl_node *tn = tpl_map("S(ic#)", this, 30); + tpl_load(tn, TPL_MEM, buffer, size); + tpl_unpack( tn, 0 ); + tpl_free(tn); + } +}; + +struct ci s = {9999, "this is a test string."}; + +s.pack(&buffer, &bsize); +printf("buffer: %s\n", (char *)buffer); + +struct ci b = { -1, "" }; + +b.unpack(buffer, bsize); + +printf("i: %d\n", b.i); +printf("c: %s\n", b.c); +} diff --git a/tests/sizes b/tests/sizes new file mode 100755 index 0000000..1c16449 --- /dev/null +++ b/tests/sizes @@ -0,0 +1,23 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Config; + +#print "$_: ", ($Config{$_} or ""), "\n" for keys %Config; +#exit; + +my @types = qw(char short int long longlong double); +for (@types) { + my $bytes = $Config{"${_}size"}; + my $bits = $bytes * 8; + printf "size of %-10s: %5d bytes (%d bits)\n", $_, $bytes, $bits;; +} + +print qq/ + These sizes reflect the platform and compiler options with + which Perl was built on this system. The sizes may change + if different compiler options are used. +/; + diff --git a/tests/test1-mingw.c b/tests/test1-mingw.c new file mode 100644 index 0000000..1a1f20b --- /dev/null +++ b/tests/test1-mingw.c @@ -0,0 +1,12 @@ +#include +#include "tpl.h" + +int WINAPI WinMain (HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PSTR szCmdLine, + int iCmdShow) +{ + MessageBox (NULL, "Hello", "Hello Demo", MB_OK); + return (0); +} + diff --git a/tests/test1.ans b/tests/test1.ans new file mode 100644 index 0000000..858b8f6 --- /dev/null +++ b/tests/test1.ans @@ -0,0 +1 @@ +j is 1 diff --git a/tests/test1.c b/tests/test1.c new file mode 100644 index 0000000..2e23fe4 --- /dev/null +++ b/tests/test1.c @@ -0,0 +1,24 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j=-1; + void *addr; + size_t sz; + + tn = tpl_map("i",&i); + i=1; + tpl_pack(tn,0); + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + tn = tpl_map("i",&j); + tpl_load(tn,TPL_MEM,addr,sz); + tpl_unpack(tn,0); + printf("j is %d\n", j); + tpl_free(tn); + free(addr); + return(0); +} diff --git a/tests/test10.ans b/tests/test10.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test10.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test10.c b/tests/test10.c new file mode 100644 index 0000000..5e2bb03 --- /dev/null +++ b/tests/test10.c @@ -0,0 +1,13 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test10.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test10.tpl b/tests/test10.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test10.tpl differ diff --git a/tests/test100.ans b/tests/test100.ans new file mode 100644 index 0000000..402ddae --- /dev/null +++ b/tests/test100.ans @@ -0,0 +1,2 @@ +fmt: S(ic) +p: 1 diff --git a/tests/test100.c b/tests/test100.c new file mode 100644 index 0000000..ae34b46 --- /dev/null +++ b/tests/test100.c @@ -0,0 +1,27 @@ +#include "tpl.h" +#include + +const char *filename = "/tmp/test100.tpl"; +int main() { + tpl_node *tn; + struct { + int i; + char c; + } s; + int p; + char *fmt; + + tn = tpl_map("S(ic)", &s); + s.i = 1; s.c = '^'; + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "i", &p); + if (fmt) { + printf("fmt: %s\n", fmt); + printf("p: %d\n", p); + } + + return 0; +} diff --git a/tests/test101.ans b/tests/test101.ans new file mode 100644 index 0000000..68bece6 --- /dev/null +++ b/tests/test101.ans @@ -0,0 +1,2 @@ +TPL_DATAPEEK format mismatches tpl iamge +peek failed diff --git a/tests/test101.c b/tests/test101.c new file mode 100644 index 0000000..833657a --- /dev/null +++ b/tests/test101.c @@ -0,0 +1,30 @@ +#include "tpl.h" +#include + +extern tpl_hook_t tpl_hook; +const char *filename = "/tmp/test101.tpl"; +int main() { + tpl_node *tn; + struct { + int i; + char c; + } s; + char *fmt, q; + tpl_hook.oops = printf; + + tn = tpl_map("S(ic)", &s); + s.i = 1; s.c = '^'; + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "c", &q); + if (fmt) { + printf("fmt: %s\n", fmt); + printf("q: %c\n", q); + } else { + printf("peek failed\n"); + } + + return 0; +} diff --git a/tests/test102.ans b/tests/test102.ans new file mode 100644 index 0000000..a29caa7 --- /dev/null +++ b/tests/test102.ans @@ -0,0 +1,2 @@ +fmt: c +q: ! diff --git a/tests/test102.c b/tests/test102.c new file mode 100644 index 0000000..0b8fd8c --- /dev/null +++ b/tests/test102.c @@ -0,0 +1,22 @@ +#include "tpl.h" +#include + +const char *filename = "/tmp/test102.tpl"; +int main() { + tpl_node *tn; + char *fmt,p,q; + + tn = tpl_map("c", &p); + p = '!'; + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "c", &q); + if (fmt) { + printf("fmt: %s\n", fmt); + printf("q: %c\n", q); + } + + return 0; +} diff --git a/tests/test103.ans b/tests/test103.ans new file mode 100644 index 0000000..3763fdf --- /dev/null +++ b/tests/test103.ans @@ -0,0 +1,2 @@ +fmt: S(iscsiu) +pi: 1, ps: hello, pc: ^, pt: world, pi: 2 diff --git a/tests/test103.c b/tests/test103.c new file mode 100644 index 0000000..006b388 --- /dev/null +++ b/tests/test103.c @@ -0,0 +1,33 @@ +#include "tpl.h" +#include + +const char *filename = "/tmp/test103.tpl"; +int main() { + tpl_node *tn; + struct { + int i; + char *s; + char c; + char *t; + int j; + unsigned u; + } s; + char *fmt, *ps, pc, *pt; + int pi, pj; + + tn = tpl_map("S(iscsiu)", &s); + s.i = 1; s.s = "hello"; s.c = '^'; s.t = "world"; s.j = 2; s.u = 3; + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "iscsi",&pi,&ps,&pc,&pt,&pj); + if (fmt) { + printf("fmt: %s\n", fmt); + printf("pi: %d, ps: %s, pc: %c, pt: %s, pi: %d\n", pi,ps,pc,pt,pj); + } else { + printf("peek failed\n"); + } + + return 0; +} diff --git a/tests/test104.ans b/tests/test104.ans new file mode 100644 index 0000000..5e79c69 --- /dev/null +++ b/tests/test104.ans @@ -0,0 +1,2 @@ +fmt: S(iscsiu) +pi: 1, ps: NULL, pc: ^, pt: world, pi: 2 diff --git a/tests/test104.c b/tests/test104.c new file mode 100644 index 0000000..9c2e309 --- /dev/null +++ b/tests/test104.c @@ -0,0 +1,33 @@ +#include "tpl.h" +#include + +const char *filename = "/tmp/test104.tpl"; +int main() { + tpl_node *tn; + struct { + int i; + char *s; + char c; + char *t; + int j; + unsigned u; + } s; + char *fmt, *ps, pc, *pt; + int pi, pj; + + tn = tpl_map("S(iscsiu)", &s); + s.i = 1; s.s = NULL; s.c = '^'; s.t = "world"; s.j = 2; s.u = 3; + tpl_pack(tn, 0); + tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "iscsi",&pi,&ps,&pc,&pt,&pj); + if (fmt) { + printf("fmt: %s\n", fmt); + printf("pi: %d, ps: %s, pc: %c, pt: %s, pi: %d\n",pi,ps?ps:"NULL",pc,pt,pj); + } else { + printf("peek failed\n"); + } + + return 0; +} diff --git a/tests/test105.ans b/tests/test105.ans new file mode 100644 index 0000000..811209f --- /dev/null +++ b/tests/test105.ans @@ -0,0 +1,2 @@ +i: 1, s: hello, w: world, c: $ +i: 1, s: hello, w: world, c: $ diff --git a/tests/test105.c b/tests/test105.c new file mode 100644 index 0000000..c8e6411 --- /dev/null +++ b/tests/test105.c @@ -0,0 +1,29 @@ +#include "tpl.h" +#include + +const char *filename = "/tmp/test105.tpl"; + +int main() { + int i=1; + char *s="hello",*w="world",c='$', *fmt; + tpl_node *tn; + + tpl_jot(TPL_FILE, filename, "issc", &i, &s, &w, &c); + + i = 0; s = NULL; w = NULL; c = 0; + + /* unpack the normal way */ + tn = tpl_map("issc", &i, &s, &w, &c); + tpl_load(tn, TPL_FILE, filename); + tpl_unpack(tn,0); + tpl_free(tn); + printf("i: %d, s: %s, w: %s, c: %c\n", i, s, w, c); + + i = 0; s = NULL; w = NULL; c = 0; + + /* unpack the quick way */ + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, filename, "issc", &i, &s, &w, &c); + printf("i: %d, s: %s, w: %s, c: %c\n", i, s, w, c); + + return 0; +} diff --git a/tests/test106.ans b/tests/test106.ans new file mode 100644 index 0000000..e8aff5b --- /dev/null +++ b/tests/test106.ans @@ -0,0 +1,24 @@ +testing with TPL_FILE: +1000 + 0, 0, 0, 5000, 15, Deepak + 1, 5, 10, 6000, 18, Deepak + 2, 10, 20, 7000, 21, Deepak + 3, 15, 30, 8000, 24, Deepak + 4, 20, 40, 9000, 27, Deepak + 5, 25, 50, 10000, 30, Deepak + 6, 30, 60, 11000, 33, Deepak + 7, 35, 70, 12000, 36, Deepak + 8, 40, 80, 13000, 39, Deepak +9,23,43,16 +testing with TPL_FD: +1000 + 0, 0, 0, 5000, 15, Deepak + 1, 5, 10, 6000, 18, Deepak + 2, 10, 20, 7000, 21, Deepak + 3, 15, 30, 8000, 24, Deepak + 4, 20, 40, 9000, 27, Deepak + 5, 25, 50, 10000, 30, Deepak + 6, 30, 60, 11000, 33, Deepak + 7, 35, 70, 12000, 36, Deepak + 8, 40, 80, 13000, 39, Deepak +9,23,43,16 diff --git a/tests/test106.c b/tests/test106.c new file mode 100644 index 0000000..ea0ef45 --- /dev/null +++ b/tests/test106.c @@ -0,0 +1,126 @@ +#include "tpl.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUM_LENGTH 16 +#define MS_COUNT 9 +struct sum_buf { + int64_t offset; + int len; + uint32_t sum1; + int chain; + uint16_t flags; + char sum2[SUM_LENGTH]; +}; + +struct sum_struct { + int64_t flength; + struct sum_buf *sums; + int count; + int blength; + int remainder; + int s2length; +}; + +const char *filename = "/tmp/test106.tpl"; + +int pack(int use_fd) +{ + tpl_node *tn; + struct sum_struct ms; + int fd=-1,j; + unsigned perms; + + perms = S_IRUSR|S_IWUSR; + if (use_fd) { + if ( (fd=open( filename,O_WRONLY|O_CREAT,perms)) == -1) { + printf("failed to open %s: %s", filename, strerror(errno)); + return(-1); + } + } + + ms.flength = 1000; + ms.count = MS_COUNT; + ms.blength = 23; + ms.remainder = 43; + ms.s2length = 16; + + ms.sums = (struct sum_buf*) malloc((sizeof(struct sum_buf))*ms.count); + + for(j=0;j +#include + +typedef struct { + int i; + char c[4]; +} test_t; + +const char *filename = "/tmp/test107.tpl"; + +int main() { + test_t s[5], t[5]; + tpl_node *tn; + int i; + + s[0].i = 0; strcpy(s[0].c, "cat"); + s[1].i = 1; strcpy(s[1].c, "dog"); + s[2].i = 2; strcpy(s[2].c, "eel"); + s[3].i = 3; strcpy(s[3].c, "emu"); + s[4].i = 4; strcpy(s[4].c, "ant"); + + tn = tpl_map("S(ic#)#", &s, 4, 5); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("S(ic#)#", &t, 4, 5); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + for(i=0; i < 5; i++) { + printf("%d %s\n", s[i].i, s[i].c); + } + + return 0; +} diff --git a/tests/test108.ans b/tests/test108.ans new file mode 100644 index 0000000..ff0ab00 --- /dev/null +++ b/tests/test108.ans @@ -0,0 +1,2 @@ +structure matches original +other fields match original diff --git a/tests/test108.c b/tests/test108.c new file mode 100644 index 0000000..5a12589 --- /dev/null +++ b/tests/test108.c @@ -0,0 +1,49 @@ +#include "tpl.h" +#include +#include +#include + +typedef struct { + int64_t j; + int l1; + unsigned l2; + int i; + int16_t h; + char c[4]; +} test_t; + +const char *filename = "/tmp/test108.tpl"; + +int main() { + test_t s[5], t[5]; + tpl_node *tn; + int w=10,x=20,y=30,z=40,W,X,Y,Z; + uint64_t b=10,B; + + memset(s, 0, sizeof(s)); + memset(t, 0, sizeof(t)); + + s[0].j=0; s[0].i=0; s[0].l1= 0; s[0].l2=0; s[0].h= 0; strcpy(s[0].c, "cat"); + s[1].j=100; s[1].i=1; s[1].l1=-1; s[1].l2=10; s[1].h=1000; strcpy(s[1].c, "dog"); + s[2].j=200; s[2].i=2; s[2].l1=-2; s[2].l2=20; s[2].h=2000; strcpy(s[2].c, "eel"); + s[3].j=300; s[3].i=3; s[3].l1=-3; s[3].l2=30; s[3].h=3000; strcpy(s[3].c, "emu"); + s[4].j=400; s[4].i=4; s[4].l1=-4; s[4].l2=40; s[4].h=4000; strcpy(s[4].c, "ant"); + + tn = tpl_map("IS(Iiuijc#)#iiii", &b, s, 4, 5, &w, &x, &y, &z); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("IS(Iiuijc#)#iiii", &B, t, 4, 5, &W, &X, &Y, &Z); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + if (memcmp(t,s,sizeof(t)) == 0) printf("structure matches original\n"); + else printf("structure mismatches original\n"); + + if (b==B && w==W && x==X && y==Y && z==Z) printf("other fields match original\n"); + else printf("other fields mismatch originals\n"); + + return 0; +} diff --git a/tests/test109.ans b/tests/test109.ans new file mode 100644 index 0000000..0515356 --- /dev/null +++ b/tests/test109.ans @@ -0,0 +1,2 @@ +sizeof(s): 12 +structures match diff --git a/tests/test109.c b/tests/test109.c new file mode 100644 index 0000000..616f048 --- /dev/null +++ b/tests/test109.c @@ -0,0 +1,35 @@ +#include +#include "tpl.h" +#include + +const char *filename = "/tmp/test109.tpl"; + +typedef struct { + char c; + uint32_t i; + uint16_t j; + char d; +} spad; + +int main() { + tpl_node *tn; + spad s = {'a', 1, 2, 'b'}, t = {'?', 0, 0, '!'};; + + printf("sizeof(s): %d\n", (int)sizeof(s));; + tn = tpl_map("S(cijc)", &s); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("S(cijc)", &t); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + if (s.c==t.c && s.i==t.i && s.j==t.j && s.d==t.d) + printf("structures match\n"); + else + printf("structures mismatch\n"); + + return 0; +} diff --git a/tests/test11.ans b/tests/test11.ans new file mode 100644 index 0000000..c54ecf7 --- /dev/null +++ b/tests/test11.ans @@ -0,0 +1 @@ +test11.tpl: not a valid tpl file diff --git a/tests/test11.c b/tests/test11.c new file mode 100644 index 0000000..efe5b18 --- /dev/null +++ b/tests/test11.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test11.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test11.tpl b/tests/test11.tpl new file mode 100644 index 0000000..d3a3e3d Binary files /dev/null and b/tests/test11.tpl differ diff --git a/tests/test110.ans b/tests/test110.ans new file mode 100644 index 0000000..3d6e6e2 --- /dev/null +++ b/tests/test110.ans @@ -0,0 +1 @@ +structures match diff --git a/tests/test110.c b/tests/test110.c new file mode 100644 index 0000000..3e81214 --- /dev/null +++ b/tests/test110.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include "tpl.h" +#include + +const char *filename = "/tmp/test110.tpl"; + +int nstrcmp(const char *a, const char *b) { + if (a==NULL || b==NULL) return (a==NULL && b==NULL)?0:1; + return strcmp(a,b); +} + +int main() { + tpl_node *tn; + + char *s1,*s2,*s3,*s4; + int i5,i6,i7,i8,i9, + i10,i11,i12,i13,i14,i15,i16,i17,i18,i19,i20, + i21,i22,i23,i24,i25,i26,i27,i28,i29,i30,i31,i32,i33; + double f34,f35,f36; + int i37,i38,i39,i40; + + char *S1,*S2,*S3,*S4; + int I5,I6,I7,I8,I9, + I10,I11,I12,I13,I14,I15,I16,I17,I18,I19,I20, + I21,I22,I23,I24,I25,I26,I27,I28,I29,I30,I31,I32,I33; + double F34,F35,F36; + int I37,I38,I39,I40; + + s1=NULL;s2=NULL;s3="testing";s4="some_string"; + i5=5;i6=6;i7=7;i8=8;i9=9; + i10=10;i11=11;i12=12;i13=13;i14=14;i15=15;i16=16;i17=17;i18=18;i19=19;i20=20; + i21=21;i22=22;i23=23;i24=24;i25=25;i26=26;i27=27;i28=28;i29=29;i30=30;i31=31; + i32=32;i33=33; + f34=34.0;f35=35.0;f36=36.0; + i37=37;i38=38;i39=39;i40=40; + + + tn = tpl_map("ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii", + &s1,&s2,&s3,&s4, + &i5,&i6,&i7,&i8,&i9, + &i10,&i11,&i12,&i13,&i14,&i15,&i16,&i17,&i18,&i19,&i20, + &i21,&i22,&i23,&i24,&i25,&i26,&i27,&i28,&i29,&i30,&i31,&i32,&i33, + &f34,&f35,&f36, + &i37,&i38,&i39,&i40); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("ssssiiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiii", + &S1,&S2,&S3,&S4, + &I5,&I6,&I7,&I8,&I9, + &I10,&I11,&I12,&I13,&I14,&I15,&I16,&I17,&I18,&I19,&I20, + &I21,&I22,&I23,&I24,&I25,&I26,&I27,&I28,&I29,&I30,&I31,&I32,&I33, + &F34,&F35,&F36, + &I37,&I38,&I39,&I40); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + if ( + !nstrcmp(s1,S1) && !nstrcmp(s2,S2) && !nstrcmp(s3,S3) && !nstrcmp(s4,S4) && + i5==I5 && i6==I6 && i7==I7 && i8==I8 && i9==I9 && i10==I10 && i11==I11 && + i12==I12 && i13==I13 && i14==I14 && i15==I15 && i16==I16 && i17==I17 && + i18==I18 && i19==I19 && i20==I20 && i21==I21 && i22==I22 && i23==I23 && + i24==I24 && i25==I25 && i26==I26 && i27==I27 && i28==I28 && i29==I29 && + i30==I30 && i31==I31 && i32==I32 && i33==I33 && f34==F34 && f35==F35 && + f36==F36 && i37==I37 && i38==I38 && i39==I39 && i40==I40 + ) { + printf("structures match\n"); + free(S1); free(S2); free(S3); free(S4); + } + else + printf("structures mismatch\n"); + + return 0; +} diff --git a/tests/test111.ans b/tests/test111.ans new file mode 100644 index 0000000..3d6e6e2 --- /dev/null +++ b/tests/test111.ans @@ -0,0 +1 @@ +structures match diff --git a/tests/test111.c b/tests/test111.c new file mode 100644 index 0000000..c03082d --- /dev/null +++ b/tests/test111.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test111.tpl"; + +#define NUM 100 + +struct st { + int i; + char c; + double f; + uint16_t v[2]; +}; + + +int main() { + struct st s[NUM], d[NUM]; + tpl_node *tn; + int i; + + memset(s, 0, sizeof(s)); /* clear s */ + memset(d, 0, sizeof(d)); /* clear d */ + + /* fill s with random stuff */ + for(i=0; i < NUM; i++) { + s[i].i = i; s[i].c='a'+i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i; + } + + tn = tpl_map("S(icfv#)#", s, 2, NUM); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("S(icfv#)#", d, 2, NUM); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + /* see if the result is the same as the s */ + printf("structures %s\n", (!memcmp(d,s,sizeof(d)))? "match" : "mismatch"); + return 0; +} + diff --git a/tests/test112.ans b/tests/test112.ans new file mode 100644 index 0000000..3d6e6e2 --- /dev/null +++ b/tests/test112.ans @@ -0,0 +1 @@ +structures match diff --git a/tests/test112.c b/tests/test112.c new file mode 100644 index 0000000..4dd8402 --- /dev/null +++ b/tests/test112.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test112.tpl"; + +#define NUM 10 + +struct st { + int i; + char c[8]; + double f; + uint16_t v[2]; +}; + + +int main() { + struct st s[NUM], d[NUM]; + tpl_node *tn; + int i; + + memset(s, 0, sizeof(s)); /* clear s */ + memset(d, 0, sizeof(d)); /* clear d */ + + /* fill s with random stuff */ + for(i=0; i < NUM; i++) { + s[i].i = i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i; + strncpy(s[i].c, "abcdefg",8); + s[i].c[0] += 1; + } + + tn = tpl_map("S(ic#fv#)#", s, 8, 2, NUM); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("S(ic#fv#)#", d, 8, 2, NUM); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + /* see if the result is the same as the s */ + printf("structures %s\n", (!memcmp(d,s,sizeof(d)))? "match" : "mismatch"); + return 0; +} + diff --git a/tests/test113.ans b/tests/test113.ans new file mode 100644 index 0000000..a317aed --- /dev/null +++ b/tests/test113.ans @@ -0,0 +1,5 @@ +structures match +A matches +B matches +C matches +D matches diff --git a/tests/test113.c b/tests/test113.c new file mode 100644 index 0000000..3cf827e --- /dev/null +++ b/tests/test113.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test113.tpl"; + +#define NUM 10 + +struct st { + int i; + char c[8]; + double f; + uint16_t v[2]; +}; + + +int main() { + struct st s[NUM], t[NUM]; + tpl_node *tn; + int i; + int a=5,d=8, A, D; + char b='6',c='7', B, C; + + memset(s, 0, sizeof(s)); /* clear s */ + memset(t, 0, sizeof(t)); /* clear t */ + + /* fill s with random stuff */ + for(i=0; i < NUM; i++) { + s[i].i = i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i; + strncpy(s[i].c, "abcdefg",8); + s[i].c[0] += 1; + } + + tn = tpl_map("icS(ic#fv#)#ci", &a, &b, s, 8, 2, NUM, &c, &d); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("icS(ic#fv#)#ci", &A, &B, t, 8, 2, NUM, &C, &D); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + /* see if the result is the same as the s */ + printf("structures %s\n", (!memcmp(t,s,sizeof(d)))? "match" : "mismatch"); + printf("A %s\n", (a==A)? "matches" : "mismatches"); + printf("B %s\n", (b==B)? "matches" : "mismatches"); + printf("C %s\n", (c==C)? "matches" : "mismatches"); + printf("D %s\n", (d==D)? "matches" : "mismatches"); + return 0; +} + diff --git a/tests/test114.ans b/tests/test114.ans new file mode 100644 index 0000000..4ed9d2a --- /dev/null +++ b/tests/test114.ans @@ -0,0 +1 @@ +matrices match diff --git a/tests/test114.c b/tests/test114.c new file mode 100644 index 0000000..228f0c8 --- /dev/null +++ b/tests/test114.c @@ -0,0 +1,45 @@ +#include +#include +#include "tpl.h" + +#define XDIM 10 +#define YDIM 2 + +const char *filename = "/tmp/test114.tpl"; +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int xy[XDIM][YDIM], XY[XDIM][YDIM]; + int i,j; + + tpl_hook.oops = printf; + + for(i=0; i +#include +#include + +#include "tpl.h" + +#define COUNT 10 +#define BUF_SIZE 256 +const char *filename = "/tmp/test115.tpl"; + +typedef struct { + char* s; +} s1_t; + + +typedef struct { + char* s; + int i; +} s2_t; + + +typedef struct { + char c[BUF_SIZE]; + char* s; + int i; +} s3_t; + + +const char hw[]="hello, world!"; + +int main () +{ + tpl_node* tn; + s1_t* s1, *S1; + s2_t* s2, *S2; + s3_t* s3, *S3; + int i; + + /* case 1: */ + s1 = (s1_t*)calloc (sizeof (s1_t), COUNT); + for(i=0; i < COUNT; i++) { + s1[i].s = malloc(sizeof(hw)); + memcpy(s1[i].s, hw, sizeof(hw)); + s1[i].s[sizeof(hw)-2]='0'+i; + } + tn = tpl_map ("S(s)#", s1, COUNT); + tpl_pack (tn, 0); + tpl_dump (tn, TPL_FILE, filename); + tpl_free (tn); + for(i=0; i < COUNT; i++) free(s1[i].s); + + S1 = (s1_t*)calloc (sizeof (s1_t), COUNT); + memset(S1, 0xff, sizeof(s1_t)*COUNT); + tn = tpl_map ("S(s)#", S1, COUNT); + tpl_load (tn, TPL_FILE, filename); + tpl_unpack (tn, 0); + tpl_free (tn); + + for(i=0; i +#include "tpl.h" + +#define NUM_STRS 3 + +const char *filename = "/tmp/test116.tpl"; + +int main() { + tpl_node *tn; + int i,d=1,D=-1; + char c='a', C='0'; + char *strs[NUM_STRS] = {"alpha", "beta", "gamma"}; + char *STRS[NUM_STRS] = {"femto", "nano", "centi"}; + + tn = tpl_map("cs#i", &c, strs, NUM_STRS, &d); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("cs#i", &C, STRS, NUM_STRS, &D); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + printf("%d %c\n", D, C); + for(i=0;i +#include +#include +#include "tpl.h" + +#define NUM_STRS 3 +#define NUM_ELMT 3 +#define STR "apple" +#define SLEN 5 + +const char *filename = "/tmp/test117.tpl"; + +int main() { + tpl_node *tn; + int i,j,d=1,D=-1; + char c='a', C='0'; + char *strs[NUM_STRS]; + char *STRS[NUM_STRS]; + + tn = tpl_map("cA(s#)i", &c, strs, NUM_STRS, &d); + for(i=0; i0) { + for(i=0;i +#include +#include "tpl.h" + +#define ILEN 10 +#define KLEN 8 +#define FLEN 5 + +struct st { + char c; + double f[FLEN]; +}; + +const char *filename = "/tmp/test118.tpl"; + +int main() { + tpl_node *tn; + /* some meaningless test data */ + struct st s = {'z', {0.9, 0.8, 0.7, 0.6, 0.5 }}; + int j; + int i[ILEN] = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; + int k[KLEN] = {100, 200, 300, 400, 500, 600, 700, 800}; + char a = '&'; + char b = 'x'; + const char *fmt; + uint32_t num_fxlens, *fxlens; + + tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); + tpl_pack(tn,0); + + tpl_pack(tn,1); + for(j=0; j < ILEN; j++) i[j]--; + tpl_pack(tn,1); + for(j=0; j < ILEN; j++) i[j]--; + tpl_pack(tn,1); + + tpl_pack(tn,2); + b++; + for(j=0; j < KLEN; j++) k[j] += 50; + tpl_pack(tn,2); + b++; + for(j=0; j < KLEN; j++) k[j] += 50; + tpl_pack(tn,2); + + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + /* now peek at the fxlens */ + fmt = tpl_peek(TPL_FILE|TPL_FXLENS, filename, &num_fxlens, &fxlens); + printf("format %s\n", fmt); + printf("num_fxlens %u\n", num_fxlens); + for(j=0; j0) free(fxlens); + return(0); +} diff --git a/tests/test119.ans b/tests/test119.ans new file mode 100644 index 0000000..9a47712 --- /dev/null +++ b/tests/test119.ans @@ -0,0 +1 @@ +size is 1726 diff --git a/tests/test119.c b/tests/test119.c new file mode 100644 index 0000000..f1e65b7 --- /dev/null +++ b/tests/test119.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test119.tpl"; + +#define NUM 100 + +struct st { + int i; + char c; + double f; + uint16_t v[2]; +}; + + +int main() { + struct st s[NUM], d[NUM]; + tpl_node *tn; + int i; + uint32_t sz=0; + + memset(s, 0, sizeof(s)); /* clear s */ + memset(d, 0, sizeof(d)); /* clear d */ + + /* fill s with random stuff */ + for(i=0; i < NUM; i++) { + s[i].i = i; s[i].c='a'+i; s[i].f = 3.14159 * i; s[i].v[0] = NUM*i; s[i].v[1] = NUM+i; + } + + tn = tpl_map("S(icfv#)#", s, 2, NUM); + tpl_pack(tn,0); + tpl_dump(tn,TPL_GETSIZE,&sz); + tpl_free(tn); + + printf("size is %u\n", sz); + + return 0; +} + diff --git a/tests/test12.ans b/tests/test12.ans new file mode 100644 index 0000000..c8e2333 --- /dev/null +++ b/tests/test12.ans @@ -0,0 +1 @@ +test12.tpl: not a valid tpl file diff --git a/tests/test12.c b/tests/test12.c new file mode 100644 index 0000000..51c50bc --- /dev/null +++ b/tests/test12.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test12.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test12.tpl b/tests/test12.tpl new file mode 100644 index 0000000..de74a8b Binary files /dev/null and b/tests/test12.tpl differ diff --git a/tests/test120.ans b/tests/test120.ans new file mode 100644 index 0000000..c527a46 --- /dev/null +++ b/tests/test120.ans @@ -0,0 +1,12 @@ +testing undersized output buffer... -1 +testing sufficient output buffer... 0 +j is 0 +j is 1 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 diff --git a/tests/test120.c b/tests/test120.c new file mode 100644 index 0000000..440f18a --- /dev/null +++ b/tests/test120.c @@ -0,0 +1,24 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,rc,j; + char toosmall[10]; + char buf[60]; + + tn = tpl_map("A(i)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + rc=tpl_dump(tn,TPL_MEM|TPL_PREALLOCD,toosmall,sizeof(toosmall)); + printf("testing undersized output buffer... %d \n", rc); + rc=tpl_dump(tn,TPL_MEM|TPL_PREALLOCD,buf,sizeof(buf)); + printf("testing sufficient output buffer... %d \n", rc); + tpl_free(tn); + + tn = tpl_map("A(i)",&j); + tpl_load(tn,TPL_MEM|TPL_EXCESS_OK,buf,sizeof(buf)); + while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j); + tpl_free(tn); + return(0); +} diff --git a/tests/test121.ans b/tests/test121.ans new file mode 100644 index 0000000..6c90699 --- /dev/null +++ b/tests/test121.ans @@ -0,0 +1,6 @@ +one +two +three +eins +zwei +drei diff --git a/tests/test121.c b/tests/test121.c new file mode 100644 index 0000000..f5a9585 --- /dev/null +++ b/tests/test121.c @@ -0,0 +1,31 @@ +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test121.tpl"; +int main() { + char *labels[2][3] = { {"one", "two", "three"}, + {"eins", "zwei", "drei" } }; + char *olabels[2][3] = { {NULL,NULL,NULL }, {NULL,NULL,NULL}}; + int i,j; + + tpl_node *tn; + tn = tpl_map("s##", labels, 2, 3); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + tn = tpl_map("s##", olabels, 2, 3); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + for(i=0;i<2;i++) { + for(j=0;j<3;j++) { + printf("%s\n", olabels[i][j]); + free(olabels[i][j]); + } + } + + return 0; +} diff --git a/tests/test122.ans b/tests/test122.ans new file mode 100644 index 0000000..e4c3c1b --- /dev/null +++ b/tests/test122.ans @@ -0,0 +1 @@ +1 abc 3.140000 a 1 diff --git a/tests/test122.c b/tests/test122.c new file mode 100644 index 0000000..9a3d38c --- /dev/null +++ b/tests/test122.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include "tpl.h" + +const char *filename = "/tmp/test122.tpl"; + +typedef struct { + char c; + int i; +} inner_t; + +typedef struct { + int i; + char c[3]; + double f; + inner_t inner; +} outer; + +int main() { + tpl_node *tn; + outer ms = {1, {'a','b','c'}, 3.14, {'a',1}}; + outer os; + + tn = tpl_map( "S(ic#f$(ci))", &ms, 3); + tpl_pack( tn, 0 ); + tpl_dump( tn, TPL_FILE, filename ); + tpl_free( tn ); + + memset(&os, 0, sizeof(outer)); + tn = tpl_map( "S(ic#f$(ci))", &os, 3); + tpl_load( tn, TPL_FILE, filename ); + tpl_unpack( tn, 0 ); + tpl_free( tn ); + + printf("%d %c%c%c %f %c %d\n", os.i, os.c[0],os.c[1],os.c[2],os.f, + os.inner.c, os.inner.i); + + return(0); +} diff --git a/tests/test123.ans b/tests/test123.ans new file mode 100644 index 0000000..41e76f1 --- /dev/null +++ b/tests/test123.ans @@ -0,0 +1 @@ +caught error! diff --git a/tests/test123.c b/tests/test123.c new file mode 100644 index 0000000..7d82530 --- /dev/null +++ b/tests/test123.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include "tpl.h" + +jmp_buf env; +extern tpl_hook_t tpl_hook; + +int catch_oops(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + longjmp(env,-1); /* return to setjmp point */ + return 0; /* not reached */ +} + +int main() { + int err; + tpl_node *tn; + tpl_hook.oops = catch_oops; /* install fatal handler */ + + err = setjmp(env); /* on error, control will return here */ + if (err) { + printf("caught error!\n"); + return -1; + } + + tn = tpl_map("@"); /* generate a fatal error */ + printf("program ending, without error\n"); + return 0; +} diff --git a/tests/test124.ans b/tests/test124.ans new file mode 100644 index 0000000..121d371 --- /dev/null +++ b/tests/test124.ans @@ -0,0 +1,4 @@ +mapped +freed +abcdefghi first +jklmnopqr second diff --git a/tests/test124.c b/tests/test124.c new file mode 100644 index 0000000..02020d6 --- /dev/null +++ b/tests/test124.c @@ -0,0 +1,38 @@ +#include +#include +#include "tpl.h" +#define LEN 10 + +const char *filename = "/tmp/test124.tpl"; + +typedef struct { + char name[LEN]; +} test_t; +int main() { + test_t t; + char *s; + tpl_node *tn; + + tn = tpl_map("A(S(c#)s)", &t, LEN, &s); + printf("mapped\n"); + + memcpy(t.name,"abcdefghi\0",10); + s="first"; + tpl_pack(tn,1); + + memcpy(t.name,"jklmnopqr\0",10); + s="second"; + tpl_pack(tn,1); + + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + printf("freed\n"); + + tn = tpl_map("A(S(c#)s)", &t, LEN, &s); + tpl_load(tn,TPL_FILE,filename); + while(tpl_unpack(tn,1) > 0) { + printf("%s %s\n", t.name, s); + } + tpl_free(tn); + return 0; +} diff --git a/tests/test13.ans b/tests/test13.ans new file mode 100644 index 0000000..7ebe6e2 --- /dev/null +++ b/tests/test13.ans @@ -0,0 +1 @@ +test13.tpl: not a valid tpl file diff --git a/tests/test13.c b/tests/test13.c new file mode 100644 index 0000000..140afef --- /dev/null +++ b/tests/test13.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test13.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test13.tpl b/tests/test13.tpl new file mode 100644 index 0000000..bf10054 Binary files /dev/null and b/tests/test13.tpl differ diff --git a/tests/test14.ans b/tests/test14.ans new file mode 100644 index 0000000..e803c7d --- /dev/null +++ b/tests/test14.ans @@ -0,0 +1 @@ +test14.tpl: not a valid tpl file diff --git a/tests/test14.c b/tests/test14.c new file mode 100644 index 0000000..d22f04c --- /dev/null +++ b/tests/test14.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test14.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test14.tpl b/tests/test14.tpl new file mode 100644 index 0000000..8dfd4a5 Binary files /dev/null and b/tests/test14.tpl differ diff --git a/tests/test15.ans b/tests/test15.ans new file mode 100644 index 0000000..f0c2f49 --- /dev/null +++ b/tests/test15.ans @@ -0,0 +1 @@ +test15.tpl: not a valid tpl file diff --git a/tests/test15.c b/tests/test15.c new file mode 100644 index 0000000..89cf40a --- /dev/null +++ b/tests/test15.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test15.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test15.tpl b/tests/test15.tpl new file mode 100644 index 0000000..80a8338 Binary files /dev/null and b/tests/test15.tpl differ diff --git a/tests/test16.ans b/tests/test16.ans new file mode 100644 index 0000000..db2bed3 --- /dev/null +++ b/tests/test16.ans @@ -0,0 +1 @@ +test16.tpl: not a valid tpl file diff --git a/tests/test16.c b/tests/test16.c new file mode 100644 index 0000000..400d83f --- /dev/null +++ b/tests/test16.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test16.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test16.tpl b/tests/test16.tpl new file mode 100644 index 0000000..4ef0eaa Binary files /dev/null and b/tests/test16.tpl differ diff --git a/tests/test17.ans b/tests/test17.ans new file mode 100644 index 0000000..7519cbf --- /dev/null +++ b/tests/test17.ans @@ -0,0 +1 @@ +test17.tpl: format signature mismatch diff --git a/tests/test17.c b/tests/test17.c new file mode 100644 index 0000000..551e382 --- /dev/null +++ b/tests/test17.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("A(c)",&i); + tpl_load(tn,TPL_FILE,"test17.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test17.tpl b/tests/test17.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test17.tpl differ diff --git a/tests/test18.ans b/tests/test18.ans new file mode 100644 index 0000000..c12a153 --- /dev/null +++ b/tests/test18.ans @@ -0,0 +1,2 @@ +failed to parse A(i +tpl map failed diff --git a/tests/test18.c b/tests/test18.c new file mode 100644 index 0000000..3a8f106 --- /dev/null +++ b/tests/test18.c @@ -0,0 +1,15 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i=-1; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i",&i); + printf("tpl map %s\n", tn ? "succeeded" : "failed"); + return(0); +} diff --git a/tests/test19.ans b/tests/test19.ans new file mode 100644 index 0000000..3cf2a7d --- /dev/null +++ b/tests/test19.ans @@ -0,0 +1,2 @@ +failed to parse A(i)) +tpl map failed diff --git a/tests/test19.c b/tests/test19.c new file mode 100644 index 0000000..accac44 --- /dev/null +++ b/tests/test19.c @@ -0,0 +1,15 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i=-1; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i))",&i); + printf("tpl map %s\n", tn ? "succeeded" : "failed"); + return(0); +} diff --git a/tests/test2.ans b/tests/test2.ans new file mode 100644 index 0000000..858b8f6 --- /dev/null +++ b/tests/test2.ans @@ -0,0 +1 @@ +j is 1 diff --git a/tests/test2.c b/tests/test2.c new file mode 100644 index 0000000..18f9026 --- /dev/null +++ b/tests/test2.c @@ -0,0 +1,21 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j=-1; + + tn = tpl_map("i",&i); + i=1; + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test2.tpl"); + tpl_free(tn); + + tn = tpl_map("i",&j); + tpl_load(tn,TPL_FILE,"/tmp/test2.tpl"); + tpl_unpack(tn,0); + printf("j is %d\n", j); + tpl_free(tn); + return(0); + +} diff --git a/tests/test20.ans b/tests/test20.ans new file mode 100644 index 0000000..56af0c0 --- /dev/null +++ b/tests/test20.ans @@ -0,0 +1,2 @@ +failed to parse iA() +tpl map failed diff --git a/tests/test20.c b/tests/test20.c new file mode 100644 index 0000000..25b0ab0 --- /dev/null +++ b/tests/test20.c @@ -0,0 +1,15 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i; + + tpl_hook.oops = printf; + + tn = tpl_map("iA()",&i); + printf("tpl map %s\n", tn ? "succeeded" : "failed"); + return(0); +} diff --git a/tests/test21.ans b/tests/test21.ans new file mode 100644 index 0000000..c79f83a --- /dev/null +++ b/tests/test21.ans @@ -0,0 +1,5 @@ +i,j are 0,1 +i,j are 2,3 +i,j are 4,5 +i,j are 6,7 +i,j are 8,9 diff --git a/tests/test21.c b/tests/test21.c new file mode 100644 index 0000000..67a5b57 --- /dev/null +++ b/tests/test21.c @@ -0,0 +1,18 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j=-1; + + tn = tpl_map("A(ii)",&i,&j); + for(i=0,j=1;i<10;i+=2,j+=2) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test21.tpl"); + tpl_free(tn); + + tn = tpl_map("A(ii)",&i,&j); + tpl_load(tn,TPL_FILE,"/tmp/test21.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i,j are %d,%d\n", i,j); + tpl_free(tn); + return(0); +} diff --git a/tests/test22.ans b/tests/test22.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test22.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test22.c b/tests/test22.c new file mode 100644 index 0000000..a69703b --- /dev/null +++ b/tests/test22.c @@ -0,0 +1,18 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + unsigned i; + + tn = tpl_map("A(u)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test22.tpl"); + tpl_free(tn); + + tn = tpl_map("A(u)",&i); + tpl_load(tn,TPL_FILE,"/tmp/test22.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test23.ans b/tests/test23.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test23.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test23.c b/tests/test23.c new file mode 100644 index 0000000..52193df --- /dev/null +++ b/tests/test23.c @@ -0,0 +1,29 @@ +#include +#include "tpl.h" +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i; + char *file = "/tmp/test23.tpl"; + int fd; + + tn = tpl_map("A(u)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE, file); + tpl_free(tn); + + if ( (fd=open( file,O_RDONLY)) == -1) { + printf("failed to open %s: %s", file, strerror(errno)); + } + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test24.ans b/tests/test24.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test24.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test24.c b/tests/test24.c new file mode 100644 index 0000000..6cc5797 --- /dev/null +++ b/tests/test24.c @@ -0,0 +1,26 @@ +#include +#include +#include "tpl.h" +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i; + char *file = "test24.tpl"; + int fd; + + if ( (fd=open( file,O_RDONLY)) == -1) { + printf("failed to open %s: %s", file, strerror(errno)); + exit(-1); + } + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test24.tpl b/tests/test24.tpl new file mode 100644 index 0000000..1b03586 Binary files /dev/null and b/tests/test24.tpl differ diff --git a/tests/test25.ans b/tests/test25.ans new file mode 100644 index 0000000..9383cfb --- /dev/null +++ b/tests/test25.ans @@ -0,0 +1 @@ +test25.tpl: not a valid tpl file diff --git a/tests/test25.c b/tests/test25.c new file mode 100644 index 0000000..7c5ffb0 --- /dev/null +++ b/tests/test25.c @@ -0,0 +1,27 @@ +#include +#include +#include "tpl.h" +#include +#include +#include +#include +#include + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + unsigned i; + char *file = "test25.tpl"; + + tpl_hook.oops = printf; + + tn = tpl_map("A(u)",&i); + if (tpl_load(tn, TPL_FILE, file) < 0 ) { + tpl_free(tn); + exit(-1); + } + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test25.tpl b/tests/test25.tpl new file mode 100644 index 0000000..1b03586 Binary files /dev/null and b/tests/test25.tpl differ diff --git a/tests/test26.ans b/tests/test26.ans new file mode 100644 index 0000000..bf8028a --- /dev/null +++ b/tests/test26.ans @@ -0,0 +1,2 @@ +3 tpls gathered. +999045 is their sum. diff --git a/tests/test26.c b/tests/test26.c new file mode 100644 index 0000000..dc3dc4f --- /dev/null +++ b/tests/test26.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include "tpl.h" + +#define DEBUG 0 + +int num_tpls = 0, sum_tpls = 0; + +int tpl_cb(void *tpl, size_t tpllen, void*data) { + int i; + tpl_node *tn; + + if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen); + tn = tpl_map("A(i)", &i); + tpl_load(tn, TPL_MEM, tpl, tpllen); + num_tpls++; + while (tpl_unpack(tn,1) > 0) sum_tpls += i; + tpl_free(tn); + return 0; + +} + +int main() { + FILE *f1,*f2; + int fdflags,fd,fd1,fd2; + int selrc, maxfd; + tpl_gather_t *gs1=NULL,*gs2=NULL,**gs; + + struct timeval tv; + fd_set rset; + + f1 = popen("cat test26_0.tpl;sleep 1; cat test26_1.tpl", "r"); + fd1 = fileno(f1); + fdflags = fcntl(fd1, F_GETFL, 0); + fcntl( fd1, F_SETFL, fdflags | O_NONBLOCK); + + f2 = popen("cat test26_2.tpl;sleep 1; cat test26_3.tpl", "r"); + fd2 = fileno(f2); + fdflags = fcntl(fd2, F_GETFL, 0); + fcntl( fd2, F_SETFL, fdflags | O_NONBLOCK); + + while (1) { + FD_ZERO( &rset ); + if (fd1 >= 0) FD_SET( fd1, &rset ); + if (fd2 >= 0) FD_SET( fd2, &rset ); + + if (fd1 == -1 && fd2 == -1) { + printf("%d tpls gathered.\n",num_tpls); + printf("%d is their sum.\n",sum_tpls); + return(0); + } + + maxfd=0; + if (fd1>maxfd) maxfd = fd1; + if (fd2>maxfd) maxfd = fd2; + + tv.tv_sec = 5; + tv.tv_usec = 0; + + selrc = select(maxfd+1, &rset, NULL, NULL, &tv ); + if (selrc == -1) { + perror("select()"); + } else if (selrc) { + for(fd=0;fd0\n"); + } + } + } + } else { + if (DEBUG) printf("timeout\n"); + } + } + return(0); +} diff --git a/tests/test26_0.tpl b/tests/test26_0.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test26_0.tpl differ diff --git a/tests/test26_1.tpl b/tests/test26_1.tpl new file mode 100644 index 0000000..85ae076 Binary files /dev/null and b/tests/test26_1.tpl differ diff --git a/tests/test26_2.tpl b/tests/test26_2.tpl new file mode 100644 index 0000000..73e62d3 Binary files /dev/null and b/tests/test26_2.tpl differ diff --git a/tests/test26_3.tpl b/tests/test26_3.tpl new file mode 100644 index 0000000..bb209fc Binary files /dev/null and b/tests/test26_3.tpl differ diff --git a/tests/test27.ans b/tests/test27.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test27.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test27.c b/tests/test27.c new file mode 100644 index 0000000..671fa03 --- /dev/null +++ b/tests/test27.c @@ -0,0 +1,35 @@ +#include +#include +#include "tpl.h" +#include +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i,perms; + char *file = "/tmp/test27.tpl"; + int fd; + + perms = S_IRUSR|S_IWUSR; + if ( (fd=open( file,O_RDWR|O_CREAT|O_TRUNC,perms)) == -1) { + printf("failed to open %s: %s", file, strerror(errno)); + exit(-1); + } + + tn = tpl_map("A(u)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd); + tpl_free(tn); + + lseek(fd,0,SEEK_SET); /* re-position fd to start of file */ + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test28.ans b/tests/test28.ans new file mode 100644 index 0000000..494c321 --- /dev/null +++ b/tests/test28.ans @@ -0,0 +1 @@ +sum is 49995000 diff --git a/tests/test28.c b/tests/test28.c new file mode 100644 index 0000000..ae17789 --- /dev/null +++ b/tests/test28.c @@ -0,0 +1,38 @@ +#include +#include "tpl.h" +#include +#include +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i, sum=0; + int fd[2], pid; + + pipe(fd); + if ( (pid = fork()) == 0) { /* child */ + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd[0]); + while (tpl_unpack(tn,1) > 0) sum += i; + tpl_free(tn); + printf("sum is %d\n", sum); + + } else if (pid > 0) { /* parent */ + + tn = tpl_map("A(u)",&i); + for(i=0;i<10000;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + waitpid(pid,NULL,0); + + } else if (pid == -1) { + perror("fork error"); + } + return(0); +} diff --git a/tests/test29.ans b/tests/test29.ans new file mode 100644 index 0000000..db001dc --- /dev/null +++ b/tests/test29.ans @@ -0,0 +1,2 @@ +sum is 49995000 +abcdefghijklmnopqrstuvwxyz diff --git a/tests/test29.c b/tests/test29.c new file mode 100644 index 0000000..5ed6228 --- /dev/null +++ b/tests/test29.c @@ -0,0 +1,50 @@ +#include +#include "tpl.h" +#include +#include +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i, sum=0; + int fd[2], pid; + char c; + + pipe(fd); + if ( (pid = fork()) == 0) { /* child */ + + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd[0]); + while (tpl_unpack(tn,1) > 0) sum += i; + tpl_free(tn); + printf("sum is %d\n", sum); + + tn = tpl_map("A(c)",&c); + tpl_load(tn, TPL_FD, fd[0]); + while (tpl_unpack(tn,1) > 0) printf("%c",c); + tpl_free(tn); + printf("\n"); + + } else if (pid > 0) { /* parent */ + + tn = tpl_map("A(u)",&i); + for(i=0;i<10000;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + tn = tpl_map("A(c)",&c); + for(c='a';c<='z';c++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + waitpid(pid,NULL,0); + + } else if (pid == -1) { + perror("fork error"); + } + return(0); +} diff --git a/tests/test3.ans b/tests/test3.ans new file mode 100644 index 0000000..c35e4ea --- /dev/null +++ b/tests/test3.ans @@ -0,0 +1,10 @@ +j is 0 +j is 1 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 diff --git a/tests/test3.c b/tests/test3.c new file mode 100644 index 0000000..57f92cc --- /dev/null +++ b/tests/test3.c @@ -0,0 +1,18 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j=-1; + + tn = tpl_map("A(i)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test3.tpl"); + tpl_free(tn); + + tn = tpl_map("A(i)",&j); + tpl_load(tn,TPL_FILE,"/tmp/test3.tpl"); + while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j); + tpl_free(tn); + return(0); +} diff --git a/tests/test30.ans b/tests/test30.ans new file mode 100644 index 0000000..1115af8 --- /dev/null +++ b/tests/test30.ans @@ -0,0 +1,2 @@ +buffer length: 4 +good diff --git a/tests/test30.c b/tests/test30.c new file mode 100644 index 0000000..f54f0bb --- /dev/null +++ b/tests/test30.c @@ -0,0 +1,40 @@ +#include "tpl.h" +#include +#include +#include + +int main(int argc, char*argv[]) { + tpl_bin bin; + tpl_node *tn; + int i; + char *file = "/tmp/test30.tpl"; + char str[10]; + + strcpy(str,"good egg"); + bin.addr = str; + bin.sz = 4; /* just going to pack 'good' (no NUL) */ + + tn = tpl_map("B", &bin); + tpl_pack(tn,0); + memset(str,0,10); /* just to test that buf was copied */ + tpl_dump(tn,TPL_FILE,file); + tpl_free(tn); + + bin.addr = NULL; + bin.sz = 0; + + tn = tpl_map("B", &bin); + tpl_load(tn,TPL_FILE,file); + tpl_unpack(tn,0); + tpl_free(tn); + + /* print the buffer char-by-char ; its not a nul-termd string */ + printf("buffer length: %u\n", bin.sz); + for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]); + printf("\n"); + + if (bin.sz > 0) + free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */ + return(0); +} + diff --git a/tests/test31.ans b/tests/test31.ans new file mode 100644 index 0000000..ab23e54 --- /dev/null +++ b/tests/test31.ans @@ -0,0 +1,2 @@ +buffer length: 0 + diff --git a/tests/test31.c b/tests/test31.c new file mode 100644 index 0000000..ae3c908 --- /dev/null +++ b/tests/test31.c @@ -0,0 +1,38 @@ +#include "tpl.h" +#include +#include + +int main(int argc, char*argv[]) { + tpl_bin bin; + tpl_node *tn; + int i; + char *file = "/tmp/test31.tpl"; + + bin.addr = NULL; + bin.sz = 0; + + tn = tpl_map("B", &bin); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,file); + tpl_free(tn); + + /* load these two fields with bogus values to test that tpl_unpack + * sets them back to NULL, and 0 respectively. */ + bin.addr = file; + bin.sz = 4; + + tn = tpl_map("B", &bin); + tpl_load(tn,TPL_FILE,file); + tpl_unpack(tn,0); + tpl_free(tn); + + /* print the buffer char-by-char ; its not a nul-termd string */ + printf("buffer length: %u\n", bin.sz); + for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]); + printf("\n"); + + if (bin.sz > 0) + free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */ + return(0); +} + diff --git a/tests/test32.ans b/tests/test32.ans new file mode 100644 index 0000000..bda4a86 --- /dev/null +++ b/tests/test32.ans @@ -0,0 +1,10 @@ +buffer length: 4 +good +buffer length: 4 +ood +buffer length: 4 +od e +buffer length: 4 +d eg +buffer length: 4 + egg diff --git a/tests/test32.c b/tests/test32.c new file mode 100644 index 0000000..4d61461 --- /dev/null +++ b/tests/test32.c @@ -0,0 +1,44 @@ +#include "tpl.h" +#include +#include +#include + +int main(int argc, char*argv[]) { + tpl_bin bin; + tpl_node *tn; + int i; + char *file = "/tmp/test32.tpl"; + char str[10]; + + strcpy(str,"good egg"); + bin.addr = str; + bin.sz = 4; /* just going to pack 'good' (no NUL) */ + + tn = tpl_map("A(B)", &bin); + for(i=0; i < 5; i++) { + tpl_pack(tn,1); + bin.addr = (char*)(bin.addr) + 1; + } + memset(str,0,10); /* just to test that buf was copied */ + tpl_dump(tn,TPL_FILE,file); + tpl_free(tn); + + bin.addr = NULL; + bin.sz = 0; + + tn = tpl_map("A(B)", &bin); + tpl_load(tn,TPL_FILE,file); + while (tpl_unpack(tn,1) > 0) { + /* print the buffer char-by-char ; its not a nul-termd string */ + printf("buffer length: %u\n", bin.sz); + for(i=0; i < bin.sz; i++) printf("%c", ((char*)bin.addr)[i]); + printf("\n"); + + if (bin.sz > 0) + free(bin.addr); /* malloc'd for us by tpl_unpack, we must free */ + } + tpl_free(tn); + return(0); + +} + diff --git a/tests/test33.ans b/tests/test33.ans new file mode 100644 index 0000000..8805eb3 --- /dev/null +++ b/tests/test33.ans @@ -0,0 +1,2 @@ +sizeof(double) is 8 +y is 1.000000 diff --git a/tests/test33.c b/tests/test33.c new file mode 100644 index 0000000..6018b2a --- /dev/null +++ b/tests/test33.c @@ -0,0 +1,23 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + double x,y; + + printf("sizeof(double) is %d\n", (int)sizeof(double)); + + tn = tpl_map("f",&x); + x=1.0; + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test33.tpl"); + tpl_free(tn); + + tn = tpl_map("f",&y); + tpl_load(tn,TPL_FILE,"/tmp/test33.tpl"); + tpl_unpack(tn,0); + printf("y is %.6f\n", y); + tpl_free(tn); + + return(0); +} diff --git a/tests/test34.ans b/tests/test34.ans new file mode 100644 index 0000000..6b28190 --- /dev/null +++ b/tests/test34.ans @@ -0,0 +1,15 @@ +sizeof(double) is 8 +y is 1.000000 +y is 1.666667 +y is 2.333333 +y is 3.000000 +y is 3.666667 +y is 4.333333 +y is 5.000000 +y is 5.666667 +y is 6.333333 +y is 7.000000 +y is 7.666667 +y is 8.333333 +y is 9.000000 +y is 9.666667 diff --git a/tests/test34.c b/tests/test34.c new file mode 100644 index 0000000..a4dd8bf --- /dev/null +++ b/tests/test34.c @@ -0,0 +1,21 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + double x,y; + + printf("sizeof(double) is %d\n", (int)sizeof(double)); + + tn = tpl_map("A(f)",&x); + for( x=1.0; x < 10.0; x += 2/3.0) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test34.tpl"); + tpl_free(tn); + + tn = tpl_map("A(f)",&y); + tpl_load(tn,TPL_FILE,"/tmp/test34.tpl"); + while (tpl_unpack(tn,1) > 0) printf("y is %.6f\n", y); + tpl_free(tn); + + return(0); +} diff --git a/tests/test35.ans b/tests/test35.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test35.c b/tests/test35.c new file mode 100644 index 0000000..65980e7 --- /dev/null +++ b/tests/test35.c @@ -0,0 +1,16 @@ +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + int id; + char *name, *names[] = { "joe", "bob", "cary" }; + + tn = tpl_map("A(is)", &id, &name); + + for(id=0,name=names[id]; id < 3; name=names[++id]) + tpl_pack(tn,1); + + tpl_dump(tn, TPL_FILE, "/tmp/test35.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test36.ans b/tests/test36.ans new file mode 100644 index 0000000..0b21bfe --- /dev/null +++ b/tests/test36.ans @@ -0,0 +1,3 @@ +id 0, user joe +id 1, user bob +id 2, user cary diff --git a/tests/test36.c b/tests/test36.c new file mode 100644 index 0000000..5e4867b --- /dev/null +++ b/tests/test36.c @@ -0,0 +1,22 @@ +#include +#include +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + int id; + char *name; + + tn = tpl_map("A(is)", &id, &name); + tpl_load(tn, TPL_FILE, "/tmp/test35.tpl"); + + while ( tpl_unpack(tn,1) > 0 ) { + printf("id %d, user %s\n", id, name); + free(name); + } + + tpl_free(tn); + return(0); +} + + diff --git a/tests/test37.ans b/tests/test37.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test37.c b/tests/test37.c new file mode 100644 index 0000000..6e619a6 --- /dev/null +++ b/tests/test37.c @@ -0,0 +1,20 @@ +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j; + + tn = tpl_map("A(A(i))",&j); + + for(i=2;i<4;i++) { + + for(j=i; j < 10*i; j *= i) { + tpl_pack(tn,2); + } + tpl_pack(tn,1); + } + + tpl_dump(tn, TPL_FILE, "/tmp/test37.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test38.ans b/tests/test38.ans new file mode 100644 index 0000000..157e8cf --- /dev/null +++ b/tests/test38.ans @@ -0,0 +1,9 @@ +unpacking index 1: + unpacking index 2: j is 2 + unpacking index 2: j is 4 + unpacking index 2: j is 8 + unpacking index 2: j is 16 +unpacking index 1: + unpacking index 2: j is 3 + unpacking index 2: j is 9 + unpacking index 2: j is 27 diff --git a/tests/test38.c b/tests/test38.c new file mode 100644 index 0000000..fd8fc75 --- /dev/null +++ b/tests/test38.c @@ -0,0 +1,19 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int j; + + tn = tpl_map("A(A(i))",&j); + tpl_load(tn,TPL_FILE, "/tmp/test37.tpl"); + + while (tpl_unpack(tn,1) > 0) { + printf("unpacking index 1:\n"); + while (tpl_unpack(tn,2) > 0) { + printf(" unpacking index 2: j is %d\n", j); + } + } + tpl_free(tn); + return(0); +} diff --git a/tests/test39.ans b/tests/test39.ans new file mode 100644 index 0000000..b2035d8 --- /dev/null +++ b/tests/test39.ans @@ -0,0 +1,2 @@ +test39.tpl: not a valid tpl file +load failed (rc=-1) diff --git a/tests/test39.c b/tests/test39.c new file mode 100644 index 0000000..cb892dc --- /dev/null +++ b/tests/test39.c @@ -0,0 +1,17 @@ +#include +#include "tpl.h" + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i, rc; + + tpl_hook.oops = printf; + + tn = tpl_map("A(i)",&i); + rc = tpl_load(tn,TPL_FILE,"test39.tpl"); + printf("load %s (rc=%d)\n", (rc >= 0 ? "ok" : "failed"), rc); + tpl_free(tn); + return(0); +} diff --git a/tests/test39.tpl b/tests/test39.tpl new file mode 100644 index 0000000..37d246c Binary files /dev/null and b/tests/test39.tpl differ diff --git a/tests/test4.ans b/tests/test4.ans new file mode 100644 index 0000000..c35e4ea --- /dev/null +++ b/tests/test4.ans @@ -0,0 +1,10 @@ +j is 0 +j is 1 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 diff --git a/tests/test4.c b/tests/test4.c new file mode 100644 index 0000000..9972d13 --- /dev/null +++ b/tests/test4.c @@ -0,0 +1,22 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i,j=-1; + void *addr; + int sz; + + tn = tpl_map("A(i)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + tn = tpl_map("A(i)",&j); + tpl_load(tn,TPL_MEM,addr,sz); + while (tpl_unpack(tn,1) > 0) printf("j is %d\n", j); + tpl_free(tn); + free(addr); + return(0); +} diff --git a/tests/test40.ans b/tests/test40.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test40.c b/tests/test40.c new file mode 100644 index 0000000..4c0992b --- /dev/null +++ b/tests/test40.c @@ -0,0 +1,18 @@ +#include "tpl.h" + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + for(c='a'; c<'c'; c++) tpl_pack(tn,2); + tpl_pack(tn, 1); + + for(c='1'; c<'4'; c++) tpl_pack(tn,2); + tpl_pack(tn, 1); + + tpl_dump(tn, TPL_FILE, "/tmp/test40.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test41.ans b/tests/test41.ans new file mode 100644 index 0000000..5298671 --- /dev/null +++ b/tests/test41.ans @@ -0,0 +1,2 @@ +a b +1 2 3 diff --git a/tests/test41.c b/tests/test41.c new file mode 100644 index 0000000..c5093f0 --- /dev/null +++ b/tests/test41.c @@ -0,0 +1,18 @@ +#include "tpl.h" +#include + +int main() { + char c; + tpl_node *tn; + + tn = tpl_map("A(A(c))", &c); + + tpl_load(tn, TPL_FILE, "/tmp/test40.tpl"); + while (tpl_unpack(tn,1) > 0) { + while (tpl_unpack(tn,2) > 0) printf("%c ",c); + printf("\n"); + } + tpl_free(tn); + return(0); +} + diff --git a/tests/test42.ans b/tests/test42.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test42.c b/tests/test42.c new file mode 100644 index 0000000..0f330e8 --- /dev/null +++ b/tests/test42.c @@ -0,0 +1,16 @@ +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + char id; + char *name, *names[] = { "joe", "bob", "cary" }; + + tn = tpl_map("A(cs)", &id, &name); + + for(id=0,name=names[(int)id]; id < 3; name=names[(int)++id]) + tpl_pack(tn,1); + + tpl_dump(tn, TPL_FILE, "/tmp/test42.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test43.ans b/tests/test43.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test43.c b/tests/test43.c new file mode 100644 index 0000000..ad0162e --- /dev/null +++ b/tests/test43.c @@ -0,0 +1,20 @@ +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + char id; + tpl_bin bin; + + char *junk = "0123456789"; + bin.sz = 10; + bin.addr = junk; + + tn = tpl_map("A(cB)", &id, &bin); + + for(id=0; id < 3; ++id) + tpl_pack(tn,1); /* pack same bin buffer, doesn't matter */ + + tpl_dump(tn, TPL_FILE, "/tmp/test43.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test44.ans b/tests/test44.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test44.c b/tests/test44.c new file mode 100644 index 0000000..cc25744 --- /dev/null +++ b/tests/test44.c @@ -0,0 +1,23 @@ +#include "tpl.h" + +int main(int argc, char *argv[]) { + tpl_node *tn; + char id,j; + tpl_bin bin; + + char *junk = "0123456789"; + bin.sz = 10; + bin.addr = junk; + + tn = tpl_map("A(cA(B))", &id, &bin); + + for(id=0; id < 3; ++id) { + for(j=0;j<2;j++) + tpl_pack(tn,2); /* pack same bin buffer, doesn't matter */ + tpl_pack(tn,1); + } + + tpl_dump(tn, TPL_FILE, "/tmp/test44.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test45.ans b/tests/test45.ans new file mode 100644 index 0000000..462cb89 --- /dev/null +++ b/tests/test45.ans @@ -0,0 +1,2 @@ +x is 0.500000 +y is 0.500000 diff --git a/tests/test45.c b/tests/test45.c new file mode 100644 index 0000000..ca0fc59 --- /dev/null +++ b/tests/test45.c @@ -0,0 +1,21 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + double x=0.5,y=0.0; + + tn = tpl_map("f",&x); + tpl_pack(tn,0); + printf("x is %f\n", x); + tpl_dump(tn,TPL_FILE,"/tmp/test45.tpl"); + tpl_free(tn); + + tn = tpl_map("f",&y); + tpl_load(tn,TPL_FILE,"/tmp/test45.tpl"); + tpl_unpack(tn,0); + printf("y is %f\n", y); + tpl_free(tn); + return(0); + +} diff --git a/tests/test46.ans b/tests/test46.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test46.c b/tests/test46.c new file mode 100644 index 0000000..e497754 --- /dev/null +++ b/tests/test46.c @@ -0,0 +1,14 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + double x; + + tn = tpl_map("A(f)",&x); + for(x=0.0;x<1.0;x+=0.2) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test46.tpl"); + tpl_free(tn); + + return(0); +} diff --git a/tests/test47.ans b/tests/test47.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test47.c b/tests/test47.c new file mode 100644 index 0000000..62cc619 --- /dev/null +++ b/tests/test47.c @@ -0,0 +1,14 @@ +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map( "A(i)", &i ); + for( i=0; i<10; i++ ) { + tpl_pack( tn, 1 ); + } + tpl_dump( tn, TPL_FILE, "/tmp/test47.tpl" ); + tpl_free( tn ); + return(0); +} diff --git a/tests/test48.ans b/tests/test48.ans new file mode 100644 index 0000000..a4a2f8e --- /dev/null +++ b/tests/test48.ans @@ -0,0 +1 @@ +0 1 2 3 4 5 6 7 8 9 \ No newline at end of file diff --git a/tests/test48.c b/tests/test48.c new file mode 100644 index 0000000..3cf52f7 --- /dev/null +++ b/tests/test48.c @@ -0,0 +1,15 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map( "A(i)", &i ); + tpl_load( tn, TPL_FILE, "/tmp/test47.tpl" ); + while (tpl_unpack( tn, 1 ) > 0) { + printf("%d ", i); + } + tpl_free( tn ); + return(0); +} diff --git a/tests/test49.ans b/tests/test49.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test49.c b/tests/test49.c new file mode 100644 index 0000000..9e7f8f2 --- /dev/null +++ b/tests/test49.c @@ -0,0 +1,18 @@ + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s; + + tn = tpl_map( "A(s)", &s ); + + s = "bob"; + tpl_pack(tn, 1); + + s = "betty"; + tpl_pack(tn, 1); + + tpl_dump(tn, TPL_FILE, "/tmp/test49.tpl"); + tpl_free(tn); + return(0); + } diff --git a/tests/test5.ans b/tests/test5.ans new file mode 100644 index 0000000..54164a9 --- /dev/null +++ b/tests/test5.ans @@ -0,0 +1,100 @@ +j is 0 +j is 1 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 1 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 2 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 3 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 4 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 5 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 14 +j is 6 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 14 +j is 15 +j is 7 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 14 +j is 15 +j is 16 +j is 8 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 14 +j is 15 +j is 16 +j is 17 +j is 9 +j is 10 +j is 11 +j is 12 +j is 13 +j is 14 +j is 15 +j is 16 +j is 17 +j is 18 diff --git a/tests/test5.c b/tests/test5.c new file mode 100644 index 0000000..1594fb7 --- /dev/null +++ b/tests/test5.c @@ -0,0 +1,27 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int o,i,j=-1; + void *addr; + int sz; + + tn = tpl_map("A(A(i))",&i); + for(o=0;o<10;o++) { + for(i=o; i < o+10; i++) tpl_pack(tn,2); + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + tn = tpl_map("A(A(i))",&j); + tpl_load(tn,TPL_MEM,addr,sz); + while (tpl_unpack(tn,1) > 0) { + while (tpl_unpack(tn,2) > 0) printf("j is %d\n", j); + } + tpl_free(tn); + free(addr); + return(0); +} diff --git a/tests/test50.ans b/tests/test50.ans new file mode 100644 index 0000000..556f45c --- /dev/null +++ b/tests/test50.ans @@ -0,0 +1,2 @@ +bob +betty diff --git a/tests/test50.c b/tests/test50.c new file mode 100644 index 0000000..d7f9d68 --- /dev/null +++ b/tests/test50.c @@ -0,0 +1,19 @@ + #include + #include + #include "tpl.h" + + int main() { + tpl_node *tn; + char *s; + + tn = tpl_map( "A(s)", &s ); + tpl_load( tn, TPL_FILE, "/tmp/test49.tpl" ); + + while (tpl_unpack( tn, 1 ) > 0) { + printf("%s\n", s); + free(s); /* important! */ + } + + tpl_free(tn); + return(0); + } diff --git a/tests/test51.ans b/tests/test51.ans new file mode 100644 index 0000000..bb58756 --- /dev/null +++ b/tests/test51.ans @@ -0,0 +1 @@ +num_tpls: 4, sum: 180 diff --git a/tests/test51.c b/tests/test51.c new file mode 100644 index 0000000..49834d7 --- /dev/null +++ b/tests/test51.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tpl.h" + +#define DEBUG 0 +#define FILE_BUFLEN 500 + +int num_tpls = 0, sum_tpls = 0; + +int tpl_cb(void *tpl, size_t tpllen, void*data) { + int i; + tpl_node *tn; + + if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen); + tn = tpl_map("A(i)", &i); + tpl_load(tn, TPL_MEM, tpl, tpllen); + num_tpls++; + while (tpl_unpack(tn,1) > 0) sum_tpls += i; + tpl_free(tn); + return 0; + +} + +int main(int argc, char *argv[]) { + char *files[] = {"test51_0.tpl", "test51_1.tpl", "test51_2.tpl", "test51_3.tpl","test51_4.tpl", NULL}; + char **f; + char buf[FILE_BUFLEN]; + int rc,fd; + tpl_gather_t *gs=NULL; + + for (f = files; *f; f++) { + if (DEBUG) printf("file is %s\n", *f); + if ( ( fd = open(*f, O_RDONLY) ) == -1) { + printf("error - can't open %s: %s\n", *f, strerror(errno)); + exit(-1); + } + rc = read(fd,&buf,FILE_BUFLEN); /* read whole file (no points for style) */ + if (rc == -1) { + printf("error - can't read %s: %s\n", *f, strerror(errno)); + exit(-1); + } + if (tpl_gather(TPL_GATHER_MEM,buf, rc, &gs, tpl_cb, NULL) <= 0) { + printf("tpl_gather_mem returned <= 0, exiting\n"); + exit(-1); + } + close(fd); + } + printf("num_tpls: %d, sum: %d\n", num_tpls, sum_tpls); + return(0); +} diff --git a/tests/test51_0.tpl b/tests/test51_0.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test51_0.tpl differ diff --git a/tests/test51_1.tpl b/tests/test51_1.tpl new file mode 100644 index 0000000..b056e6b Binary files /dev/null and b/tests/test51_1.tpl differ diff --git a/tests/test51_2.tpl b/tests/test51_2.tpl new file mode 100644 index 0000000..328940b Binary files /dev/null and b/tests/test51_2.tpl differ diff --git a/tests/test51_3.tpl b/tests/test51_3.tpl new file mode 100644 index 0000000..bb74463 Binary files /dev/null and b/tests/test51_3.tpl differ diff --git a/tests/test51_4.tpl b/tests/test51_4.tpl new file mode 100644 index 0000000..0219500 Binary files /dev/null and b/tests/test51_4.tpl differ diff --git a/tests/test52.ans b/tests/test52.ans new file mode 100644 index 0000000..cc17582 --- /dev/null +++ b/tests/test52.ans @@ -0,0 +1,2 @@ +---------- +--> j is 1 diff --git a/tests/test52.c b/tests/test52.c new file mode 100644 index 0000000..364ac3a --- /dev/null +++ b/tests/test52.c @@ -0,0 +1,30 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int j; + + tn = tpl_map("A(A(i))",&j); + + j=1; + tpl_pack(tn,2); + tpl_pack(tn,1); + j=2; + tpl_pack(tn,2); + /* omit packing parent */ + + tpl_dump(tn, TPL_FILE, "/tmp/test52.tpl"); + tpl_free(tn); + + tn = tpl_map("A(A(i))",&j); + tpl_load(tn, TPL_FILE, "/tmp/test52.tpl"); + while(tpl_unpack(tn,1) > 0) { + printf("----------\n"); + while(tpl_unpack(tn,2) > 0) { + printf("--> j is %d\n", j); + } + } + tpl_free(tn); + return(0); +} diff --git a/tests/test53.ans b/tests/test53.ans new file mode 100644 index 0000000..eb90ac5 --- /dev/null +++ b/tests/test53.ans @@ -0,0 +1,3 @@ +---------- +--> j is 1 +---------- diff --git a/tests/test53.c b/tests/test53.c new file mode 100644 index 0000000..311556f --- /dev/null +++ b/tests/test53.c @@ -0,0 +1,30 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int j; + + tn = tpl_map("A(A(i))",&j); + + j=1; + tpl_pack(tn,2); + tpl_pack(tn,1); + /* j=2; */ + /* tpl_pack(tn,2); */ + tpl_pack(tn,1); /* pack zero-length nested array */ + + tpl_dump(tn, TPL_FILE, "/tmp/test53.tpl"); + tpl_free(tn); + + tn = tpl_map("A(A(i))",&j); + tpl_load(tn, TPL_FILE, "/tmp/test53.tpl"); + while(tpl_unpack(tn,1) > 0) { + printf("----------\n"); + while(tpl_unpack(tn,2) > 0) { + printf("--> j is %d\n", j); + } + } + tpl_free(tn); + return(0); +} diff --git a/tests/test54.ans b/tests/test54.ans new file mode 100644 index 0000000..5125c4f --- /dev/null +++ b/tests/test54.ans @@ -0,0 +1,2 @@ +tpl_mem_gather aborted by app callback +tpl_gather_mem returned <= 0, exiting diff --git a/tests/test54.c b/tests/test54.c new file mode 100644 index 0000000..0eeccea --- /dev/null +++ b/tests/test54.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tpl.h" + +#define DEBUG 0 +#define FILE_BUFLEN 500 + +extern tpl_hook_t tpl_hook; +int num_tpls = 0, sum_tpls = 0; + +int tpl_cb(void *tpl, size_t tpllen, void*data) { + int i; + tpl_node *tn; + + tpl_hook.oops = printf; + + if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen); + tn = tpl_map("A(i)", &i); + tpl_load(tn, TPL_MEM, tpl, tpllen); + num_tpls++; + while (tpl_unpack(tn,1) > 0) sum_tpls += i; + tpl_free(tn); + /* this next line is a hack to test the callback's ability + * to abort further tpl processing by returning < 0 */ + if (num_tpls == 3) return -1; + return 0; + +} + +int main(int argc, char *argv[]) { + char *files[] = {"test54_0.tpl", "test54_1.tpl", "test54_2.tpl", "test54_3.tpl","test54_4.tpl", NULL}; + char **f; + char buf[FILE_BUFLEN]; + int rc,fd; + tpl_gather_t *gs=NULL; + + for (f = files; *f; f++) { + if (DEBUG) printf("file is %s\n", *f); + if ( ( fd = open(*f, O_RDONLY) ) == -1) { + printf("error - can't open %s: %s\n", *f, strerror(errno)); + exit(-1); + } + rc = read(fd,&buf,FILE_BUFLEN); /* read whole file (no points for style) */ + if (rc == -1) { + printf("error - can't read %s: %s\n", *f, strerror(errno)); + exit(-1); + } + if (tpl_gather(TPL_GATHER_MEM,buf, rc, &gs, tpl_cb, NULL) <= 0) { + printf("tpl_gather_mem returned <= 0, exiting\n"); + exit(-1); + } + close(fd); + } + printf("num_tpls: %d, sum: %d\n", num_tpls, sum_tpls); + return(0); +} diff --git a/tests/test54_0.tpl b/tests/test54_0.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test54_0.tpl differ diff --git a/tests/test54_1.tpl b/tests/test54_1.tpl new file mode 100644 index 0000000..b056e6b Binary files /dev/null and b/tests/test54_1.tpl differ diff --git a/tests/test54_2.tpl b/tests/test54_2.tpl new file mode 100644 index 0000000..328940b Binary files /dev/null and b/tests/test54_2.tpl differ diff --git a/tests/test54_3.tpl b/tests/test54_3.tpl new file mode 100644 index 0000000..bb74463 Binary files /dev/null and b/tests/test54_3.tpl differ diff --git a/tests/test54_4.tpl b/tests/test54_4.tpl new file mode 100644 index 0000000..0219500 Binary files /dev/null and b/tests/test54_4.tpl differ diff --git a/tests/test55.ans b/tests/test55.ans new file mode 100644 index 0000000..f840753 --- /dev/null +++ b/tests/test55.ans @@ -0,0 +1,3 @@ +tpl_fd_gather aborted by app callback +2 tpls gathered. +499545 is their sum. diff --git a/tests/test55.c b/tests/test55.c new file mode 100644 index 0000000..94bdde7 --- /dev/null +++ b/tests/test55.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include "tpl.h" + +#define DEBUG 0 + +extern tpl_hook_t tpl_hook; +int num_tpls = 0, sum_tpls = 0; + +int tpl_cb(void *tpl, size_t tpllen, void*data) { + int i; + tpl_node *tn; + + tpl_hook.oops = printf; + + if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen); + tn = tpl_map("A(i)", &i); + tpl_load(tn, TPL_MEM, tpl, tpllen); + num_tpls++; + while (tpl_unpack(tn,1) > 0) sum_tpls += i; + tpl_free(tn); + /* this next line is a hack to test the callback's ability + * to abort further tpl processing by returning < 0 */ + if (num_tpls == 1) return -1; + return 0; + +} + +int main() { + FILE *f1,*f2; + int fdflags,fd,fd1,fd2; + int selrc, maxfd; + tpl_gather_t *gs1=NULL,*gs2=NULL,**gs; + struct timeval tv; + fd_set rset; + + + f1 = popen("cat test26_0.tpl;sleep 1; cat test26_1.tpl", "r"); + fd1 = fileno(f1); + fdflags = fcntl(fd1, F_GETFL, 0); + fcntl( fd1, F_SETFL, fdflags | O_NONBLOCK); + + f2 = popen("cat test26_2.tpl;sleep 1; cat test26_3.tpl", "r"); + fd2 = fileno(f2); + fdflags = fcntl(fd2, F_GETFL, 0); + fcntl( fd2, F_SETFL, fdflags | O_NONBLOCK); + + while (1) { + FD_ZERO( &rset ); + if (fd1 >= 0) FD_SET( fd1, &rset ); + if (fd2 >= 0) FD_SET( fd2, &rset ); + + if (fd1 == -1 && fd2 == -1) { + printf("%d tpls gathered.\n",num_tpls); + printf("%d is their sum.\n",sum_tpls); + return(0); + } + + maxfd=0; + if (fd1>maxfd) maxfd = fd1; + if (fd2>maxfd) maxfd = fd2; + + tv.tv_sec = 5; + tv.tv_usec = 0; + + selrc = select(maxfd+1, &rset, NULL, NULL, &tv ); + if (selrc == -1) { + perror("select()"); + } else if (selrc) { + for(fd=0;fd0\n"); + } + } + } + } else { + if (DEBUG) printf("timeout\n"); + } + } + return(0); +} diff --git a/tests/test55_0.tpl b/tests/test55_0.tpl new file mode 100644 index 0000000..a811f62 Binary files /dev/null and b/tests/test55_0.tpl differ diff --git a/tests/test55_1.tpl b/tests/test55_1.tpl new file mode 100644 index 0000000..85ae076 Binary files /dev/null and b/tests/test55_1.tpl differ diff --git a/tests/test55_2.tpl b/tests/test55_2.tpl new file mode 100644 index 0000000..73e62d3 Binary files /dev/null and b/tests/test55_2.tpl differ diff --git a/tests/test55_3.tpl b/tests/test55_3.tpl new file mode 100644 index 0000000..bb209fc Binary files /dev/null and b/tests/test55_3.tpl differ diff --git a/tests/test56.ans b/tests/test56.ans new file mode 100644 index 0000000..f0746c8 --- /dev/null +++ b/tests/test56.ans @@ -0,0 +1,4 @@ +hs2.s1 length: 5 +hs2.s1: draco +hs2.s2 length: 2 +hs2.s2: po diff --git a/tests/test56.c b/tests/test56.c new file mode 100644 index 0000000..e0de8e6 --- /dev/null +++ b/tests/test56.c @@ -0,0 +1,42 @@ +#include "tpl.h" +#include +#include +#include + +#define S1_LEN 6 +#define S2_LEN 4 + +struct has_strings { + char a; + char s1[S1_LEN]; + char s2[S2_LEN]; +}; + +int main(int argc,char*argv[]) { + tpl_node *tn; + struct has_strings hs,hs2; + void *img; + size_t sz; + + strncpy(hs.s1, "draco",S1_LEN); + strncpy(hs.s2, "po",S2_LEN); + + tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN); + tpl_pack(tn,0); + tpl_dump(tn,TPL_MEM,&img,&sz); + tpl_free(tn); + + /* unpack */ + + tn = tpl_map("c#c#", hs2.s1, S1_LEN, hs2.s2, S2_LEN); + tpl_load(tn,TPL_MEM,img,sz); + tpl_unpack(tn,0); + tpl_free(tn); + free(img); + + printf("hs2.s1 length: %d\n", (int)strlen(hs2.s1)); + printf("hs2.s1: %s\n", hs2.s1); + printf("hs2.s2 length: %d\n", (int)strlen(hs2.s2)); + printf("hs2.s2: %s\n", hs2.s2); + return(0); +} diff --git a/tests/test57.ans b/tests/test57.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test57.c b/tests/test57.c new file mode 100644 index 0000000..43bed41 --- /dev/null +++ b/tests/test57.c @@ -0,0 +1,28 @@ +#include "tpl.h" +#include +#include +#include + +#define S1_LEN 6 +#define S2_LEN 4 + +struct has_strings { + char a; + char s1[S1_LEN]; + char s2[S2_LEN]; +}; + +int main(int argc,char*argv[]) { + tpl_node *tn; + struct has_strings hs; + + strncpy(hs.s1, "draco",S1_LEN); + strncpy(hs.s2, "po",S2_LEN); + + tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test57.tpl"); + tpl_free(tn); + + return(0); +} diff --git a/tests/test58.ans b/tests/test58.ans new file mode 100644 index 0000000..f235fc6 --- /dev/null +++ b/tests/test58.ans @@ -0,0 +1,4 @@ +hs.s1 length: 5 +hs.s1: draco +hs.s2 length: 2 +hs.s2: po diff --git a/tests/test58.c b/tests/test58.c new file mode 100644 index 0000000..15f875e --- /dev/null +++ b/tests/test58.c @@ -0,0 +1,30 @@ +#include "tpl.h" +#include +#include +#include + +#define S1_LEN 6 +#define S2_LEN 4 + +struct has_strings { + char a; + char s1[S1_LEN]; + char s2[S2_LEN]; +}; + +int main(int argc,char*argv[]) { + tpl_node *tn; + struct has_strings hs; + + tn = tpl_map("c#c#", hs.s1, S1_LEN, hs.s2, S2_LEN); + tpl_load(tn,TPL_FILE,"/tmp/test57.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + + printf("hs.s1 length: %d\n", (int)strlen(hs.s1)); + printf("hs.s1: %s\n", hs.s1); + printf("hs.s2 length: %d\n", (int)strlen(hs.s2)); + printf("hs.s2: %s\n", hs.s2); + + return(0); +} diff --git a/tests/test59.ans b/tests/test59.ans new file mode 100644 index 0000000..11e5c1c --- /dev/null +++ b/tests/test59.ans @@ -0,0 +1,6 @@ +hs2.a: t +hs2.s1 length: 5 +hs2.s1: draco +hs2.b: h +hs2.s2 length: 2 +hs2.s2: po diff --git a/tests/test59.c b/tests/test59.c new file mode 100644 index 0000000..bd394f1 --- /dev/null +++ b/tests/test59.c @@ -0,0 +1,47 @@ +#include "tpl.h" +#include +#include +#include + +#define S1_LEN 6 +#define S2_LEN 4 + +struct has_strings { + char a; + char s1[S1_LEN]; + char b; + char s2[S2_LEN]; +}; + +int main(int argc,char*argv[]) { + tpl_node *tn; + struct has_strings hs,hs2; + void *img; + size_t sz; + + strncpy(hs.s1, "draco",S1_LEN); + strncpy(hs.s2, "po",S2_LEN); + hs.a = 't'; + hs.b = 'h'; + + tn = tpl_map("cc#cc#", &hs.a, hs.s1, S1_LEN, &hs.b, hs.s2, S2_LEN); + tpl_pack(tn,0); + tpl_dump(tn,TPL_MEM,&img,&sz); + tpl_free(tn); + + /* unpack */ + + tn = tpl_map("cc#cc#", &hs2.a, hs2.s1, S1_LEN, &hs2.b, hs2.s2, S2_LEN); + tpl_load(tn,TPL_MEM,img,sz); + tpl_unpack(tn,0); + tpl_free(tn); + free(img); + + printf("hs2.a: %c\n", hs2.a); + printf("hs2.s1 length: %d\n", (int)strlen(hs2.s1)); + printf("hs2.s1: %s\n", hs2.s1); + printf("hs2.b: %c\n", hs2.b); + printf("hs2.s2 length: %d\n", (int)strlen(hs2.s2)); + printf("hs2.s2: %s\n", hs2.s2); + return(0); +} diff --git a/tests/test6.ans b/tests/test6.ans new file mode 100644 index 0000000..e972e9f --- /dev/null +++ b/tests/test6.ans @@ -0,0 +1 @@ +t is hello, world! diff --git a/tests/test6.c b/tests/test6.c new file mode 100644 index 0000000..da3aec5 --- /dev/null +++ b/tests/test6.c @@ -0,0 +1,25 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + char *s,*t; + void *addr; + int sz; + + tn = tpl_map("s",&s); + s = "hello, world!"; + tpl_pack(tn,0); + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + tn = tpl_map("s",&t); + tpl_load(tn,TPL_MEM,addr,sz); + tpl_unpack(tn,0); + printf("t is %s\n", t); + free(t); + tpl_free(tn); + free(addr); + return(0); +} diff --git a/tests/test60.ans b/tests/test60.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test60.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test60.c b/tests/test60.c new file mode 100644 index 0000000..b797bf5 --- /dev/null +++ b/tests/test60.c @@ -0,0 +1,18 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test60.tpl"); + + /* test load-after-pack: implicit free via tpl_free_keep_map() */ + tpl_load(tn,TPL_FILE,"/tmp/test60.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + tpl_free(tn); + return(0); +} diff --git a/tests/test61.ans b/tests/test61.ans new file mode 100644 index 0000000..71580ae --- /dev/null +++ b/tests/test61.ans @@ -0,0 +1,20 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 +i is 10 +i is 11 +i is 12 +i is 13 +i is 14 +i is 15 +i is 16 +i is 17 +i is 18 +i is 19 diff --git a/tests/test61.c b/tests/test61.c new file mode 100644 index 0000000..efa64ff --- /dev/null +++ b/tests/test61.c @@ -0,0 +1,18 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test61_0.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + /* test load-then-load: implicit free via tpl_free_keep_map */ + tpl_load(tn, TPL_FILE,"test61_1.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + tpl_free(tn); + return(0); +} diff --git a/tests/test61_0.tpl b/tests/test61_0.tpl new file mode 100644 index 0000000..d928efa Binary files /dev/null and b/tests/test61_0.tpl differ diff --git a/tests/test61_1.tpl b/tests/test61_1.tpl new file mode 100644 index 0000000..0ee24de Binary files /dev/null and b/tests/test61_1.tpl differ diff --git a/tests/test62.ans b/tests/test62.ans new file mode 100644 index 0000000..61287c2 --- /dev/null +++ b/tests/test62.ans @@ -0,0 +1,20 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 +i is 0 +i is -1 +i is -2 +i is -3 +i is -4 +i is -5 +i is -6 +i is -7 +i is -8 +i is -9 diff --git a/tests/test62.c b/tests/test62.c new file mode 100644 index 0000000..18797ee --- /dev/null +++ b/tests/test62.c @@ -0,0 +1,20 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test62_0.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + /* test load-then-pack (then load): implicit free via tpl_free_keep_map */ + for(i=0;i>-10;i--) tpl_pack(tn,1); + tpl_dump(tn, TPL_FILE,"/tmp/test62_1.tpl"); + tpl_load(tn,TPL_FILE,"/tmp/test62_1.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + tpl_free(tn); + return(0); +} diff --git a/tests/test62_0.tpl b/tests/test62_0.tpl new file mode 100644 index 0000000..d928efa Binary files /dev/null and b/tests/test62_0.tpl differ diff --git a/tests/test63.ans b/tests/test63.ans new file mode 100644 index 0000000..61287c2 --- /dev/null +++ b/tests/test63.ans @@ -0,0 +1,20 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 +i is 0 +i is -1 +i is -2 +i is -3 +i is -4 +i is -5 +i is -6 +i is -7 +i is -8 +i is -9 diff --git a/tests/test63.c b/tests/test63.c new file mode 100644 index 0000000..b60b8fe --- /dev/null +++ b/tests/test63.c @@ -0,0 +1,22 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + for(i=0;i<10;i++) tpl_pack(tn,1); + + /* test pack-then-unpack without dump/load; implicit dump/load*/ + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + /* implicit conversion back to output tpl (discards previous data in tpl */ + for(i=0;i>-10;i--) tpl_pack(tn,1); + + /* one more implicit conversion */ + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + + tpl_free(tn); + return(0); +} diff --git a/tests/test64.ans b/tests/test64.ans new file mode 100644 index 0000000..c9162af --- /dev/null +++ b/tests/test64.ans @@ -0,0 +1,3 @@ +s is beta +sh.str is delta +bin.addr is zeta, size 5 diff --git a/tests/test64.c b/tests/test64.c new file mode 100644 index 0000000..3800f28 --- /dev/null +++ b/tests/test64.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include "tpl.h" + +struct str_holder { + char str[10]; +}; + + +/* the real purpose of this test is to be run under dbx "check -all" + * mode to ensure the level-0 data are freed when replaced (i.e. the + * level 0 nodes are packed more than once, this is an edge case but + * the required behavior is to free the previously-packed data when + * packing the new replacement data). Do the test for s,S,and B types. + */ +int main() { + tpl_node *tn; + char *s; + struct str_holder sh; + tpl_bin bin; + + /* test a replacement pack (s type) of the level 0 node */ + tn = tpl_map("s", &s); + s = "alpha"; + tpl_pack(tn,0); /* copies alpha */ + s = "beta"; + tpl_pack(tn,0); /* should free alpha, copy beta */ + tpl_dump(tn,TPL_FILE,"/tmp/test64_0.tpl"); + tpl_free(tn); + + /* print out dumped tpl */ + s = ""; + tn = tpl_map("s", &s); + tpl_load(tn,TPL_FILE,"/tmp/test64_0.tpl"); + tpl_unpack(tn,0); + printf("s is %s\n", s); + free(s); + tpl_free(tn); + + /* test replacement pack (S type) of the level 0 node */ + tn = tpl_map("c#", sh.str, 10); + strncpy(sh.str, "gamma", 10); + tpl_pack(tn,0); /* copies gamma */ + strncpy(sh.str, "delta", 10); + tpl_pack(tn,0); /* should free gamma, copy delta */ + tpl_dump(tn,TPL_FILE,"/tmp/test64_1.tpl"); + tpl_free(tn); + + /* print out dumped tpl */ + sh.str[0] = '\0'; + tn = tpl_map("c#", sh.str, 10); + tpl_load(tn,TPL_FILE,"/tmp/test64_1.tpl"); + tpl_unpack(tn,0); + printf("sh.str is %s\n", sh.str); + tpl_free(tn); + + /* test replacement pack (B type) of the level 0 node */ + tn = tpl_map("B", &bin); + bin.addr = "epsilon"; + bin.sz = strlen("epsilon")+1; + tpl_pack(tn,0); /* copies epsilon */ + bin.addr = "zeta"; + bin.sz = strlen("zeta")+1; + tpl_pack(tn,0); /* should free epsilon, copy zeta */ + tpl_dump(tn,TPL_FILE,"/tmp/test64_2.tpl"); + tpl_free(tn); + + /* print out dumped tpl */ + bin.addr = ""; + bin.sz = 1; + tn = tpl_map("B", &bin); + tpl_load(tn,TPL_FILE,"/tmp/test64_2.tpl"); + tpl_unpack(tn,0); + printf("bin.addr is %s, size %d\n", (char*)(bin.addr), bin.sz); + free(bin.addr); + tpl_free(tn); + return(0); + +} diff --git a/tests/test65.ans b/tests/test65.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test65.c b/tests/test65.c new file mode 100644 index 0000000..fba332e --- /dev/null +++ b/tests/test65.c @@ -0,0 +1,16 @@ +#include +#include +#include "tpl.h" + +#define TEST_LEN 10 + +int main() { + tpl_node *tn; + int i[TEST_LEN] = {1,2,3,4,5,6,7,8,9,10}; + + tn = tpl_map("i#", i, TEST_LEN); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test65.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test66.ans b/tests/test66.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test66.c b/tests/test66.c new file mode 100644 index 0000000..d7ca519 --- /dev/null +++ b/tests/test66.c @@ -0,0 +1,18 @@ +#include +#include +#include "tpl.h" + +#define TEST_LEN1 10 +#define TEST_LEN2 5 + +int main() { + tpl_node *tn; + int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10}; + int j[TEST_LEN2] = {5,4,3,2,1}; + + tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test66.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test67.ans b/tests/test67.ans new file mode 100644 index 0000000..3ec4355 --- /dev/null +++ b/tests/test67.ans @@ -0,0 +1,3 @@ +load succeeded +/tmp/test67.tpl: array lengths mismatch +load failed diff --git a/tests/test67.c b/tests/test67.c new file mode 100644 index 0000000..460240f --- /dev/null +++ b/tests/test67.c @@ -0,0 +1,40 @@ +#include +#include +#include "tpl.h" + +#define TEST_LEN1 10 +#define TEST_LEN2 5 + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10}; + int j[TEST_LEN2] = {5,4,3,2,1}; + + tpl_hook.oops = printf; /* errors to printf */ + + tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test67.tpl"); + tpl_free(tn); + + /* Expect success on the next line */ + tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2); + if (tpl_load(tn,TPL_FILE,"/tmp/test67.tpl") < 0) { + printf("load failed\n"); + } else { + printf("load succeeded\n"); + } + tpl_free(tn); + + /* Expect failure on the next line (TEST_LEN2 != TEST_LEN2-1) */ + tn = tpl_map("i#i#", i, TEST_LEN1, j, (TEST_LEN2 - 1)); + if (tpl_load(tn,TPL_FILE,"/tmp/test67.tpl") < 0) { + printf("load failed\n"); + } else { + printf("load succeeded\n"); + } + tpl_free(tn); + return(0); +} diff --git a/tests/test68.ans b/tests/test68.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test68.c b/tests/test68.c new file mode 100644 index 0000000..2af8e59 --- /dev/null +++ b/tests/test68.c @@ -0,0 +1,37 @@ +#include +#include +#include "tpl.h" + +#define TEST_LEN1 10 +#define TEST_LEN2 5 + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10}; + int j[TEST_LEN2] = {5,4,3,2,1}; + int i2[TEST_LEN1]; + int j2[TEST_LEN2]; + int x; + + tpl_hook.oops = printf; /* errors to printf */ + + tn = tpl_map("i#i#", i, TEST_LEN1, j, TEST_LEN2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test68.tpl"); + tpl_free(tn); + + tn = tpl_map("i#i#", i2, TEST_LEN1, j2, TEST_LEN2); + tpl_load(tn,TPL_FILE,"/tmp/test68.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + + for(x=0;x +#include +#include "tpl.h" + +#define TEST_LEN1 10 +#define TEST_LEN2 5 + +extern tpl_hook_t tpl_hook; + +int main() { + tpl_node *tn; + int i[TEST_LEN1] = {1,2,3,4,5,6,7,8,9,10}; + int j[TEST_LEN2] = {5,4,3,2,1}; + int i2[TEST_LEN1]; + int j2[TEST_LEN2]; + int x,y; + + tpl_hook.oops = printf; /* errors to printf */ + + tn = tpl_map("A(i#i#)", i, TEST_LEN1, j, TEST_LEN2); + for(y=0; y < 2; y++) { + for(x=0; x < TEST_LEN1; x++) i[x] += 1; + for(x=0; x < TEST_LEN2; x++) j[x] -= 1; + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_FILE,"/tmp/test69.tpl"); + tpl_free(tn); + + tn = tpl_map("A(i#i#)", i2, TEST_LEN1, j2, TEST_LEN2); + tpl_load(tn,TPL_FILE,"/tmp/test69.tpl"); + while (tpl_unpack(tn,1) > 0) { + for(x=0; x < TEST_LEN1; x++) printf("%d ", i2[x]); + printf("\n"); + for(x=0; x < TEST_LEN2; x++) printf("%d ", j2[x]); + printf("\n"); + } + tpl_free(tn); + return(0); +} diff --git a/tests/test7.ans b/tests/test7.ans new file mode 100644 index 0000000..85957f3 --- /dev/null +++ b/tests/test7.ans @@ -0,0 +1,3 @@ +t is wonderful +t is prince of peace +t is counselor diff --git a/tests/test7.c b/tests/test7.c new file mode 100644 index 0000000..707655e --- /dev/null +++ b/tests/test7.c @@ -0,0 +1,27 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + char *strs[] = { "wonderful", "prince of peace", "counselor", NULL }; + int i; + char *s, *t; + + tn = tpl_map("A(s)",&s); + for(i=0; strs[i] != NULL; i++) { + s = strs[i]; + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_FILE,"/tmp/test7.tpl"); + tpl_free(tn); + + tn = tpl_map("A(s)",&t); + tpl_load(tn,TPL_FILE,"/tmp/test7.tpl"); + while (tpl_unpack(tn,1) > 0) { + printf("t is %s\n", t); + free(t); + } + tpl_free(tn); + return(0); +} diff --git a/tests/test70.ans b/tests/test70.ans new file mode 100644 index 0000000..5c9557e --- /dev/null +++ b/tests/test70.ans @@ -0,0 +1,7 @@ +a +1 +hi +o +matilda +y +3.140000 diff --git a/tests/test70.c b/tests/test70.c new file mode 100644 index 0000000..1433ed4 --- /dev/null +++ b/tests/test70.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "tpl.h" + +struct ms_t { + char c; + int i; + char s[2]; + char o; + char *x; + char y; + double d; +}; + +int main() { + tpl_node *tn; + struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 }; + struct ms_t ms2; + + tn = tpl_map("S(cic#cscf)", &ms, 2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test70.tpl"); + tpl_free(tn); + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("S(cic#cscf)", &ms2, 2); + tpl_load(tn,TPL_FILE,"/tmp/test70.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d); + free(ms2.x); + return(0); +} diff --git a/tests/test71.ans b/tests/test71.ans new file mode 100644 index 0000000..48f248c --- /dev/null +++ b/tests/test71.ans @@ -0,0 +1,7 @@ +# +1 +hi +o +matilda +y +3.140000 diff --git a/tests/test71.c b/tests/test71.c new file mode 100644 index 0000000..d58527a --- /dev/null +++ b/tests/test71.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "tpl.h" + +struct ms_t { + int i; + char s[2]; + char o; + char *x; + char y; + double d; +}; + +int main() { + tpl_node *tn; + struct ms_t ms = { /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 }; + struct ms_t ms2; + char b = '#', b2; + + tn = tpl_map("cS(ic#cscf)", &b, &ms, 2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test71.tpl"); + tpl_free(tn); + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("cS(ic#cscf)", &b2, &ms2, 2); + tpl_load(tn,TPL_FILE,"/tmp/test71.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", b2, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d); + free(ms2.x); + return(0); +} diff --git a/tests/test72.ans b/tests/test72.ans new file mode 100644 index 0000000..5c9557e --- /dev/null +++ b/tests/test72.ans @@ -0,0 +1,7 @@ +a +1 +hi +o +matilda +y +3.140000 diff --git a/tests/test72.c b/tests/test72.c new file mode 100644 index 0000000..afc9a26 --- /dev/null +++ b/tests/test72.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "tpl.h" + +struct ms_t { + char c; + int i; + char s[2]; + char o; + char *x; + char y; + double d; +}; + +int main() { + tpl_node *tn; + struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 }; + struct ms_t ms2; + + tn = tpl_map("S(cic#cscf)", &ms, 2); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test72.tpl"); + tpl_free(tn); + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("S(cic#cscf)", &ms2, 2); + tpl_load(tn,TPL_FILE,"/tmp/test72.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d); + free(ms2.x); + return(0); +} diff --git a/tests/test73.ans b/tests/test73.ans new file mode 100644 index 0000000..6b1a4e6 --- /dev/null +++ b/tests/test73.ans @@ -0,0 +1,7 @@ +a +1 +hi +o +matilda +tdh +3.140000 diff --git a/tests/test73.c b/tests/test73.c new file mode 100644 index 0000000..b44dafc --- /dev/null +++ b/tests/test73.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "tpl.h" + +struct ms_t { + char c; + int i; + char s[2]; + char o; + char *x; + char y[3]; + double d; +}; + +int main() { + tpl_node *tn; + struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ {'t','d','h'}, /*.d =*/ 3.14 }; + struct ms_t ms2; + + tn = tpl_map("S(cic#csc#f)", &ms, 2, 3); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test73.tpl"); + tpl_free(tn); + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("S(cic#csc#f)", &ms2, 2, 3); + tpl_load(tn,TPL_FILE,"/tmp/test73.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + printf("%c\n%d\n%c%c\n%c\n%s\n%c%c%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y[0],ms2.y[1],ms2.y[2], ms2.d); + free(ms2.x); + return(0); +} diff --git a/tests/test74.ans b/tests/test74.ans new file mode 100644 index 0000000..30913cf --- /dev/null +++ b/tests/test74.ans @@ -0,0 +1,301 @@ +1234 +0.00 +1.50 +3.00 +4.50 +6.00 +7.50 +9.00 +10.50 +12.00 +13.50 +15.00 +16.50 +18.00 +19.50 +21.00 +22.50 +24.00 +25.50 +27.00 +28.50 +30.00 +31.50 +33.00 +34.50 +36.00 +37.50 +39.00 +40.50 +42.00 +43.50 +45.00 +46.50 +48.00 +49.50 +51.00 +52.50 +54.00 +55.50 +57.00 +58.50 +60.00 +61.50 +63.00 +64.50 +66.00 +67.50 +69.00 +70.50 +72.00 +73.50 +75.00 +76.50 +78.00 +79.50 +81.00 +82.50 +84.00 +85.50 +87.00 +88.50 +90.00 +91.50 +93.00 +94.50 +96.00 +97.50 +99.00 +100.50 +102.00 +103.50 +105.00 +106.50 +108.00 +109.50 +111.00 +112.50 +114.00 +115.50 +117.00 +118.50 +120.00 +121.50 +123.00 +124.50 +126.00 +127.50 +129.00 +130.50 +132.00 +133.50 +135.00 +136.50 +138.00 +139.50 +141.00 +142.50 +144.00 +145.50 +147.00 +148.50 +150.00 +151.50 +153.00 +154.50 +156.00 +157.50 +159.00 +160.50 +162.00 +163.50 +165.00 +166.50 +168.00 +169.50 +171.00 +172.50 +174.00 +175.50 +177.00 +178.50 +180.00 +181.50 +183.00 +184.50 +186.00 +187.50 +189.00 +190.50 +192.00 +193.50 +195.00 +196.50 +198.00 +199.50 +201.00 +202.50 +204.00 +205.50 +207.00 +208.50 +210.00 +211.50 +213.00 +214.50 +216.00 +217.50 +219.00 +220.50 +222.00 +223.50 +225.00 +226.50 +228.00 +229.50 +231.00 +232.50 +234.00 +235.50 +237.00 +238.50 +240.00 +241.50 +243.00 +244.50 +246.00 +247.50 +249.00 +250.50 +252.00 +253.50 +255.00 +256.50 +258.00 +259.50 +261.00 +262.50 +264.00 +265.50 +267.00 +268.50 +270.00 +271.50 +273.00 +274.50 +276.00 +277.50 +279.00 +280.50 +282.00 +283.50 +285.00 +286.50 +288.00 +289.50 +291.00 +292.50 +294.00 +295.50 +297.00 +298.50 +0.00 +0.50 +1.00 +1.50 +2.00 +2.50 +3.00 +3.50 +4.00 +4.50 +5.00 +5.50 +6.00 +6.50 +7.00 +7.50 +8.00 +8.50 +9.00 +9.50 +10.00 +10.50 +11.00 +11.50 +12.00 +12.50 +13.00 +13.50 +14.00 +14.50 +15.00 +15.50 +16.00 +16.50 +17.00 +17.50 +18.00 +18.50 +19.00 +19.50 +20.00 +20.50 +21.00 +21.50 +22.00 +22.50 +23.00 +23.50 +24.00 +24.50 +25.00 +25.50 +26.00 +26.50 +27.00 +27.50 +28.00 +28.50 +29.00 +29.50 +30.00 +30.50 +31.00 +31.50 +32.00 +32.50 +33.00 +33.50 +34.00 +34.50 +35.00 +35.50 +36.00 +36.50 +37.00 +37.50 +38.00 +38.50 +39.00 +39.50 +40.00 +40.50 +41.00 +41.50 +42.00 +42.50 +43.00 +43.50 +44.00 +44.50 +45.00 +45.50 +46.00 +46.50 +47.00 +47.50 +48.00 +48.50 +49.00 +49.50 diff --git a/tests/test74.c b/tests/test74.c new file mode 100644 index 0000000..e81f84b --- /dev/null +++ b/tests/test74.c @@ -0,0 +1,39 @@ +#include +#include +#include "tpl.h" + +#define F1_LEN 200 +#define F2_LEN 100 + +struct ms_t { + int i; + double f1[F1_LEN]; + double f2[F2_LEN]; +}; + +int main() { + tpl_node *tn; + struct ms_t ms, ms2; + int i; + + ms.i = 1234; + for(i=0; i < F1_LEN; i++) ms.f1[i] = i * 1.5; + for(i=0; i < F2_LEN; i++) ms.f2[i] = i * 0.5; + + tn = tpl_map("S(if#f#)", &ms, F1_LEN, F2_LEN); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,"/tmp/test74.tpl"); + tpl_free(tn); + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("S(if#f#)", &ms2, F1_LEN, F2_LEN); + tpl_load(tn,TPL_FILE,"/tmp/test74.tpl"); + tpl_unpack(tn,0); + tpl_free(tn); + + printf("%d\n", ms2.i); + for(i=0; i < F1_LEN; i++) printf("%.2f\n", ms2.f1[i]); + for(i=0; i < F2_LEN; i++) printf("%.2f\n", ms2.f2[i]); + + return(0); +} diff --git a/tests/test75.ans b/tests/test75.ans new file mode 100644 index 0000000..43fce23 --- /dev/null +++ b/tests/test75.ans @@ -0,0 +1,2 @@ +string +byte array diff --git a/tests/test75.c b/tests/test75.c new file mode 100644 index 0000000..76b4fd3 --- /dev/null +++ b/tests/test75.c @@ -0,0 +1,38 @@ +#include +#include +#include "tpl.h" + +#define S2_LEN 10 + +struct example { + char *s1; /* s1 is a pointer */ + char s2[S2_LEN]; /* s2 is a byte array */ +}; + +int main() { + tpl_node *tn; + int i; + struct example dst, src = { + /* .s1 = */ "string", + /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'} + }; + + tn = tpl_map( "sc#", &src.s1, &src.s2, S2_LEN); + tpl_pack( tn, 0 ); + tpl_dump( tn, TPL_FILE, "/tmp/test75.tpl" ); + tpl_free( tn ); + + /* unpack it now into another struct */ + + tn = tpl_map( "sc#", &dst.s1, &dst.s2, S2_LEN); + tpl_load( tn, TPL_FILE, "/tmp/test75.tpl" ); + tpl_unpack( tn, 0 ); + tpl_free( tn ); + + printf("%s\n", dst.s1); + for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]); + printf("\n"); + + free(dst.s1); /* tpl allocated it for us; we must free it */ + return(0); +} diff --git a/tests/test76.ans b/tests/test76.ans new file mode 100644 index 0000000..43fce23 --- /dev/null +++ b/tests/test76.ans @@ -0,0 +1,2 @@ +string +byte array diff --git a/tests/test76.c b/tests/test76.c new file mode 100644 index 0000000..cb57d22 --- /dev/null +++ b/tests/test76.c @@ -0,0 +1,38 @@ +#include +#include +#include "tpl.h" + +#define S2_LEN 10 + +struct example { + char *s1; /* s1 is a pointer */ + char s2[S2_LEN]; /* s2 is a byte array */ +}; + +int main() { + tpl_node *tn; + int i; + struct example dst, src = { + /* .s1 = */ "string", + /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'} + }; + + tn = tpl_map( "S(sc#)", &src, S2_LEN); /* NOTE S(...) */ + tpl_pack( tn, 0 ); + tpl_dump( tn, TPL_FILE, "/tmp/test76.tpl" ); + tpl_free( tn ); + + /* unpack it now into another struct */ + + tn = tpl_map( "S(sc#)", &dst, S2_LEN); /* NOTE S(...) */ + tpl_load( tn, TPL_FILE, "/tmp/test76.tpl" ); + tpl_unpack( tn, 0 ); + tpl_free( tn ); + + printf("%s\n", dst.s1); + for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]); + printf("\n"); + + free(dst.s1); /* tpl allocated it for us; we must free it */ + return(0); +} diff --git a/tests/test77.ans b/tests/test77.ans new file mode 100644 index 0000000..43fce23 --- /dev/null +++ b/tests/test77.ans @@ -0,0 +1,2 @@ +string +byte array diff --git a/tests/test77.c b/tests/test77.c new file mode 100644 index 0000000..58b41b5 --- /dev/null +++ b/tests/test77.c @@ -0,0 +1,38 @@ +#include +#include +#include "tpl.h" + +#define S2_LEN 10 + +struct example { + char *s1; /* s1 is a pointer */ + char s2[S2_LEN]; /* s2 is a byte array */ +}; + +int main() { + tpl_node *tn; + int i; + struct example dst, src = { + /* .s1 = */ "string", + /* .s2 = */ {'b','y','t','e',' ','a','r','r','a','y'} + }; + + tn = tpl_map( "S(sc#)", &src, S2_LEN); /* NOTE S(...) */ + tpl_pack( tn, 0 ); + tpl_dump( tn, TPL_FILE, "/tmp/test77.tpl" ); + tpl_free( tn ); + + /* unpack it now into another struct */ + + tn = tpl_map( "S(sc#)", &dst, S2_LEN); + tpl_load( tn, TPL_FILE, "/tmp/test77.tpl" ); + tpl_unpack( tn, 0 ); + tpl_free( tn ); + + printf("%s\n", dst.s1); + for(i=0; i < S2_LEN; i++) printf("%c", dst.s2[i]); + printf("\n"); + + free(dst.s1); /* tpl allocated it for us; we must free it */ + return(0); +} diff --git a/tests/test78.ans b/tests/test78.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test78.c b/tests/test78.c new file mode 100644 index 0000000..cbe896f --- /dev/null +++ b/tests/test78.c @@ -0,0 +1,23 @@ +#include "tpl.h" + +int main() { + int i; + char c; + tpl_node *tn; + + tn = tpl_map("A(i)c", &i, &c); + + /* pack index number 0 (char c) */ + c = 'a'; + tpl_pack(tn, 0); + + /* pack A(i) (that is, index number 1) a few times */ + i = 3; + tpl_pack(tn, 1); + i = 4; + tpl_pack(tn, 1); + + tpl_dump(tn, TPL_FILE, "/tmp/test78.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test79.ans b/tests/test79.ans new file mode 100644 index 0000000..33ae185 --- /dev/null +++ b/tests/test79.ans @@ -0,0 +1,3 @@ +got a +got 3 +got 4 diff --git a/tests/test79.c b/tests/test79.c new file mode 100644 index 0000000..15cbaa2 --- /dev/null +++ b/tests/test79.c @@ -0,0 +1,22 @@ +#include +#include "tpl.h" + +int main() { + int i; + char c; + tpl_node *tn; + + tn = tpl_map("A(i)c", &i, &c); + tpl_load(tn, TPL_FILE, "/tmp/test78.tpl"); + + /* unpack index number 0 (char c) */ + tpl_unpack(tn, 0); + printf("got %c\n", c); + + /* unpack A(i) (that is, index number 1) til we run out of elements */ + while (tpl_unpack(tn, 1) > 0) { + printf("got %d\n", i); + } + tpl_free(tn); + return(0); +} diff --git a/tests/test8.ans b/tests/test8.ans new file mode 100644 index 0000000..46603c5 --- /dev/null +++ b/tests/test8.ans @@ -0,0 +1,10 @@ +d is ff, e is 61 +d is ff, e is 62 +d is ff, e is 63 +d is ff, e is 64 +d is ff, e is 65 +d is ff, e is 66 +d is ff, e is 67 +d is ff, e is 68 +d is ff, e is 69 +d is ff, e is 6a diff --git a/tests/test8.c b/tests/test8.c new file mode 100644 index 0000000..3f8067b --- /dev/null +++ b/tests/test8.c @@ -0,0 +1,27 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + unsigned int i; + unsigned char b,c; + unsigned char d,e; + + tn = tpl_map("cA(c)",&b,&c); + b = 255; + tpl_pack(tn,0); + for (i=0; i < 10; i++) { + c = 'a' + i; + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_FILE,"/tmp/test8.tpl"); + tpl_free(tn); + + tn = tpl_map("cA(c)",&d,&e); + tpl_load(tn,TPL_FILE,"/tmp/test8.tpl"); + tpl_unpack(tn,0); + while (tpl_unpack(tn,1) > 0) + printf("d is %x, e is %x\n", (unsigned)d, (unsigned)e); + tpl_free(tn); + return(0); +} diff --git a/tests/test80.ans b/tests/test80.ans new file mode 100644 index 0000000..ee45485 --- /dev/null +++ b/tests/test80.ans @@ -0,0 +1 @@ +a 0 1 2 3 4 5 6 7 8 9 diff --git a/tests/test80.c b/tests/test80.c new file mode 100644 index 0000000..17b8b4a --- /dev/null +++ b/tests/test80.c @@ -0,0 +1,30 @@ +#include +#include "tpl.h" + +#define ILEN 10 + +struct st { + char c; + int i[ILEN]; +}; + +int main() { + struct st dst, s = {'a', {0,1,2,3,4,5,6,7,8,9}}; + tpl_node *tn; + int j; + + tn = tpl_map("A(S(ci#))", &s, ILEN); + tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,"/tmp/test80.tpl"); + tpl_free(tn); + + tn = tpl_map("A(S(ci#))", &dst, ILEN); + tpl_load(tn,TPL_FILE,"/tmp/test80.tpl"); + while (tpl_unpack(tn,1) > 0) { + printf("%c ", dst.c); + for(j=0;j +#include "tpl.h" + +#define ILEN 10 + +struct st { + char c; + int i[ILEN]; +}; + +int main() { + struct st dst, s = {'a', {0,1,2,3,4,5,6,7,8,9}}; + tpl_node *tn; + int j; + + tn = tpl_map("A(S(ci#))", &s, ILEN); + tpl_pack(tn,1); + + /* fiddle with the fields and pack another element */ + s.c++; + for(j=0;j 0) { + printf("%c ", dst.c); + for(j=0;j +#include "tpl.h" + +#define ILEN 10 +#define KLEN 8 +#define FLEN 5 + +struct st { + char c; + double f[FLEN]; +}; + +int main() { + tpl_node *tn; + /* some meaningless test data */ + struct st s = {'z', {0.9, 0.8, 0.7, 0.6, 0.5 }}; + int j; + int i[ILEN] = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; + int k[KLEN] = {100, 200, 300, 400, 500, 600, 700, 800}; + char a = '&'; + char b = 'x'; + + tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); + tpl_pack(tn,0); + + tpl_pack(tn,1); + for(j=0; j < ILEN; j++) i[j]--; + tpl_pack(tn,1); + for(j=0; j < ILEN; j++) i[j]--; + tpl_pack(tn,1); + + tpl_pack(tn,2); + b++; + for(j=0; j < KLEN; j++) k[j] += 50; + tpl_pack(tn,2); + b++; + for(j=0; j < KLEN; j++) k[j] += 50; + tpl_pack(tn,2); + + tpl_dump(tn,TPL_FILE,"/tmp/test82.tpl"); + tpl_free(tn); + return(0); +} diff --git a/tests/test83.ans b/tests/test83.ans new file mode 100644 index 0000000..39a07f8 --- /dev/null +++ b/tests/test83.ans @@ -0,0 +1,7 @@ +& z 0.90 0.80 0.70 0.60 0.50 +-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 +-2 -3 -4 -5 -6 -7 -8 -9 -10 -11 +-3 -4 -5 -6 -7 -8 -9 -10 -11 -12 +x 100 200 300 400 500 600 700 800 +y 150 250 350 450 550 650 750 850 +z 200 300 400 500 600 700 800 900 diff --git a/tests/test83.c b/tests/test83.c new file mode 100755 index 0000000..8cadf29 --- /dev/null +++ b/tests/test83.c @@ -0,0 +1,42 @@ +#include +#include "tpl.h" + +#define ILEN 10 +#define KLEN 8 +#define FLEN 5 + +struct st { + char c; + double f[FLEN]; +}; + +int main() { + tpl_node *tn; + /* some meaningless test data */ + struct st s; + int j; + int i[ILEN]; + int k[KLEN]; + char a; + char b; + + tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); + tpl_load(tn,TPL_FILE,"/tmp/test82.tpl"); + + tpl_unpack(tn,0); + printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]); + + while( tpl_unpack(tn,1) > 0) { + for(j=0;j 0) { + printf("%c ", b); + for(j=0;j +#include "tpl.h" + +#define ILEN 10 +#define KLEN 8 +#define FLEN 5 + +struct st { + char c; + double f[FLEN]; +}; + +int main() { + tpl_node *tn; + /* some meaningless test data */ + struct st s; + int j; + int i[ILEN]; + int k[KLEN]; + char a; + char b; + + tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); + tpl_load(tn,TPL_FILE,"test84_0.tpl"); + + tpl_unpack(tn,0); + printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]); + + while( tpl_unpack(tn,1) > 0) { + for(j=0;j 0) { + printf("%c ", b); + for(j=0;j 0) { + for(j=0;j 0) { + printf("%c ", b); + for(j=0;j +#include +#include "tpl.h" + +int main() { + char *fmt; + fmt = tpl_peek(TPL_FILE, "test85.tpl"); + if (fmt) { + printf("%s\n",fmt); + free(fmt); + } + return 0; +} diff --git a/tests/test85.tpl b/tests/test85.tpl new file mode 100644 index 0000000..bb934e3 Binary files /dev/null and b/tests/test85.tpl differ diff --git a/tests/test86.ans b/tests/test86.ans new file mode 100644 index 0000000..66c7f89 --- /dev/null +++ b/tests/test86.ans @@ -0,0 +1 @@ +A(A(i)) diff --git a/tests/test86.c b/tests/test86.c new file mode 100644 index 0000000..b87070e --- /dev/null +++ b/tests/test86.c @@ -0,0 +1,27 @@ +#include +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int o,i; + void *addr; + int sz; + char *fmt; + + tn = tpl_map("A(A(i))",&i); + for(o=0;o<10;o++) { + for(i=o; i < o+10; i++) tpl_pack(tn,2); + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + fmt = tpl_peek(TPL_MEM, addr, sz); + if (fmt) { + printf("%s\n",fmt); + free(fmt); + } + free(addr); + return(0); +} diff --git a/tests/test87.ans b/tests/test87.ans new file mode 100644 index 0000000..494c321 --- /dev/null +++ b/tests/test87.ans @@ -0,0 +1 @@ +sum is 49995000 diff --git a/tests/test87.c b/tests/test87.c new file mode 100644 index 0000000..b11db98 --- /dev/null +++ b/tests/test87.c @@ -0,0 +1,47 @@ +#include +#include +#include "tpl.h" +#include +#include +#include +#include +#include +#include +#include + +int main() { + tpl_node *tn; + unsigned i, sum=0; + int fd[2], pid,rc; + void *img; + size_t sz; + + pipe(fd); + if ( (pid = fork()) == 0) { /* child */ + + rc = tpl_gather(TPL_GATHER_BLOCKING,fd[0],&img,&sz); + if (rc != 1) { + printf("error: rc non-zero: %d\n", rc); + exit(-1); + } + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_MEM, img,sz); + while (tpl_unpack(tn,1) > 0) sum += i; + tpl_free(tn); + printf("sum is %d\n", sum); + + } else if (pid > 0) { /* parent */ + + tn = tpl_map("A(u)",&i); + for(i=0;i<10000;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + close(fd[1]); + waitpid(pid,NULL,0); + + } else if (pid == -1) { + perror("fork error"); + } + return(0); +} diff --git a/tests/test88.ans b/tests/test88.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test88.c b/tests/test88.c new file mode 100644 index 0000000..57e5e82 --- /dev/null +++ b/tests/test88.c @@ -0,0 +1,18 @@ +#include "tpl.h" + +struct ms_t { + int i; + char c[3]; + double f; +}; + +int main() { + tpl_node *tn; + struct ms_t ms = {1, {'a','b','c'}, 3.14}; + + tn = tpl_map( "S(ic#f)", &ms, 3); + tpl_pack( tn, 0 ); + tpl_dump( tn, TPL_FILE, "/tmp/test88.tpl" ); + tpl_free( tn ); + return(0); +} diff --git a/tests/test89.ans b/tests/test89.ans new file mode 100644 index 0000000..626f9b8 --- /dev/null +++ b/tests/test89.ans @@ -0,0 +1,3 @@ +1 +abc +3.14 diff --git a/tests/test89.c b/tests/test89.c new file mode 100644 index 0000000..f632db4 --- /dev/null +++ b/tests/test89.c @@ -0,0 +1,21 @@ +#include "tpl.h" +#include + +struct ms_t { + int i; + char c[3]; + double f; +}; + +int main() { + tpl_node *tn; + struct ms_t ms; + + tn = tpl_map( "S(ic#f)", &ms, 3); + tpl_load( tn, TPL_FILE, "/tmp/test88.tpl" ); + tpl_unpack( tn, 0 ); + tpl_free( tn ); + + printf("%d\n%c%c%c\n%.2f\n", ms.i, ms.c[0],ms.c[1],ms.c[2], ms.f); + return(0); +} diff --git a/tests/test9.ans b/tests/test9.ans new file mode 100644 index 0000000..903c08c --- /dev/null +++ b/tests/test9.ans @@ -0,0 +1,10 @@ +i is 0 +i is 1 +i is 2 +i is 3 +i is 4 +i is 5 +i is 6 +i is 7 +i is 8 +i is 9 diff --git a/tests/test9.c b/tests/test9.c new file mode 100644 index 0000000..f4a7251 --- /dev/null +++ b/tests/test9.c @@ -0,0 +1,13 @@ +#include +#include "tpl.h" + +int main() { + tpl_node *tn; + int i; + + tn = tpl_map("A(i)",&i); + tpl_load(tn,TPL_FILE,"test9.tpl"); + while (tpl_unpack(tn,1) > 0) printf("i is %d\n", i); + tpl_free(tn); + return(0); +} diff --git a/tests/test9.tpl b/tests/test9.tpl new file mode 100644 index 0000000..d928efa Binary files /dev/null and b/tests/test9.tpl differ diff --git a/tests/test90.ans b/tests/test90.ans new file mode 100644 index 0000000..4f2784a --- /dev/null +++ b/tests/test90.ans @@ -0,0 +1 @@ +cn is equal to cn2 diff --git a/tests/test90.c b/tests/test90.c new file mode 100644 index 0000000..5c61152 --- /dev/null +++ b/tests/test90.c @@ -0,0 +1,23 @@ +#include +#include + +#include + +int main ( int n , char* a [ ] ) +{ + + tpl_node* tn ; + int64_t cn = 100,cn2 ; + + tn = tpl_map ( "I" , &cn ) ; + tpl_pack ( tn , 0 ) ; + tpl_dump ( tn , TPL_FILE , "/tmp/test90.tpl" ) ; + tpl_free ( tn ) ; + + tn = tpl_map ( "I" , &cn2 ) ; + tpl_load ( tn , TPL_FILE , "/tmp/test90.tpl" ) ; + tpl_unpack ( tn , 0 ) ; + printf("cn is %sequal to cn2\n", (cn == cn2) ? "" : "not"); + tpl_free ( tn ) ; + return ( 0 ) ; +} diff --git a/tests/test91.ans b/tests/test91.ans new file mode 100644 index 0000000..4f2784a --- /dev/null +++ b/tests/test91.ans @@ -0,0 +1 @@ +cn is equal to cn2 diff --git a/tests/test91.c b/tests/test91.c new file mode 100644 index 0000000..0e9f659 --- /dev/null +++ b/tests/test91.c @@ -0,0 +1,23 @@ +#include +#include + +#include + +int main ( int n , char* a [ ] ) +{ + + tpl_node* tn ; + uint64_t cn = 100, cn2 ; + + tn = tpl_map ( "U" , &cn ) ; + tpl_pack ( tn , 0 ) ; + tpl_dump ( tn , TPL_FILE , "/tmp/test91.tpl" ) ; + tpl_free ( tn ) ; + + tn = tpl_map ( "U" , &cn2 ) ; + tpl_load ( tn , TPL_FILE , "/tmp/test91.tpl" ) ; + tpl_unpack ( tn , 0 ) ; + printf("cn is %sequal to cn2\n", (cn == cn2) ? "" : "not"); + tpl_free ( tn ) ; + return ( 0 ) ; +} diff --git a/tests/test92.ans b/tests/test92.ans new file mode 100644 index 0000000..e69de29 diff --git a/tests/test92.c b/tests/test92.c new file mode 100644 index 0000000..55d47f7 --- /dev/null +++ b/tests/test92.c @@ -0,0 +1,39 @@ +#include +#include + +#include + +int main ( int n , char* a [ ] ) +{ + + tpl_node* tn ; + char c='a',c2='b',c3,c4; + int64_t cn = -100, cn2 ; + uint64_t ucn = 200, ucn2; + + tn = tpl_map ( "A(cIcU)" , &c, &cn, &c2, &ucn ) ; + tpl_pack ( tn , 1 ) ; + c += 1; + cn -= 1; + c2 += 1; + ucn += 1; + tpl_pack ( tn , 1 ) ; + tpl_dump ( tn , TPL_FILE , "/tmp/test92.tpl" ) ; + tpl_free ( tn ) ; + + tn = tpl_map ( "A(cIcU)" , &c3, &cn2, &c4, &ucn2 ) ; + tpl_load(tn,TPL_FILE,"/tmp/test92.tpl"); + /* Hesitant to rely on portability of %lld to print int64_t. + At least on MinGW it is questionable. */ + /* + * while (tpl_unpack(tn,1) > 0) { + * printf("%c %lld %c %llu\n", c3, cn2, c4, ucn2); + * } + */ + tpl_unpack(tn,1); + if (c3 != 'a' || cn2 != -100 || c4 != 'b' || ucn2 != 200) printf("unpack error 1\n"); + tpl_unpack(tn,1); + if (c3 != 'b' || cn2 != -101 || c4 != 'c' || ucn2 != 201) printf("unpack error 2\n"); + return ( 0 ) ; + +} diff --git a/tests/test93.ans b/tests/test93.ans new file mode 100644 index 0000000..8023187 --- /dev/null +++ b/tests/test93.ans @@ -0,0 +1 @@ +s is null diff --git a/tests/test93.c b/tests/test93.c new file mode 100644 index 0000000..944cc76 --- /dev/null +++ b/tests/test93.c @@ -0,0 +1,21 @@ +#include "tpl.h" +#include + +const char *filename="/tmp/test93.tpl"; +int main() { + tpl_node *tn; + char *s = NULL; + tn = tpl_map("s", &s); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + s = (char*)0x1; /* overwritten below */ + tn = tpl_map("s", &s); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + printf("s %s null\n", (s==NULL?"is":"is NOT")); + return(0); +} + diff --git a/tests/test94.ans b/tests/test94.ans new file mode 100644 index 0000000..572008b --- /dev/null +++ b/tests/test94.ans @@ -0,0 +1,5 @@ +s is hello +s is NULL +s is hello +s is NULL +s is hello diff --git a/tests/test94.c b/tests/test94.c new file mode 100644 index 0000000..43843a2 --- /dev/null +++ b/tests/test94.c @@ -0,0 +1,26 @@ +#include "tpl.h" +#include + +const char *filename="/tmp/test94.tpl"; +int main() { + tpl_node *tn; + int i; + char *s = NULL; + tn = tpl_map("A(s)", &s); + for(i=0;i<5;i++) { + s = (i&1) ? NULL : "hello"; /* odd i are NULL string */ + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + s = (char*)0x1; /* overwritten below */ + tn = tpl_map("A(s)", &s); + tpl_load(tn,TPL_FILE,filename); + while( tpl_unpack(tn,1) > 0) { + printf("s is %s\n", (s?s:"NULL")); + } + tpl_free(tn); + return(0); +} + diff --git a/tests/test95.ans b/tests/test95.ans new file mode 100644 index 0000000..9ce4720 --- /dev/null +++ b/tests/test95.ans @@ -0,0 +1,3 @@ +s1 NULL +s2 +s3 hello diff --git a/tests/test95.c b/tests/test95.c new file mode 100644 index 0000000..d7b0f6d --- /dev/null +++ b/tests/test95.c @@ -0,0 +1,23 @@ +#include "tpl.h" +#include + +const char *filename="/tmp/test95.tpl"; +int main() { + tpl_node *tn; + char *s1 = NULL, *s2 = "", *s3 = "hello"; + tn = tpl_map("sss", &s1, &s2, &s3); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + s1 = s2 = s3 = (char*)0x1; /* overwritten below */ + tn = tpl_map("sss", &s1, &s2, &s3); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + printf("s1 %s\n", s1?s1:"NULL"); + printf("s2 %s\n", s2?s2:"NULL"); + printf("s3 %s\n", s3?s3:"NULL"); + return(0); +} + diff --git a/tests/test96.ans b/tests/test96.ans new file mode 100644 index 0000000..6f463a1 --- /dev/null +++ b/tests/test96.ans @@ -0,0 +1,15 @@ +s1 NULL +s2 +s3 hello +s1 NULL +s2 +s3 hello +s1 NULL +s2 +s3 hello +s1 NULL +s2 +s3 hello +s1 NULL +s2 +s3 hello diff --git a/tests/test96.c b/tests/test96.c new file mode 100644 index 0000000..8e45bef --- /dev/null +++ b/tests/test96.c @@ -0,0 +1,27 @@ +#include "tpl.h" +#include + +const char *filename="/tmp/test96.tpl"; +int main() { + tpl_node *tn; + int i; + char *s1 = NULL, *s2 = "", *s3 = "hello"; + tn = tpl_map("A(sss)", &s1, &s2, &s3); + for(i=0;i<5;i++) { + tpl_pack(tn,1); + } + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + s1 = s2 = s3 = (char*)0x1; /* overwritten below */ + tn = tpl_map("A(sss)", &s1, &s2, &s3); + tpl_load(tn,TPL_FILE,filename); + while( tpl_unpack(tn,1) > 0) { + printf("s1 %s\n", s1?s1:"NULL"); + printf("s2 %s\n", s2?s2:"NULL"); + printf("s3 %s\n", s3?s3:"NULL"); + } + tpl_free(tn); + return(0); +} + diff --git a/tests/test97.ans b/tests/test97.ans new file mode 100644 index 0000000..2fe2921 --- /dev/null +++ b/tests/test97.ans @@ -0,0 +1 @@ +j is -128, v is 65535 diff --git a/tests/test97.c b/tests/test97.c new file mode 100644 index 0000000..c1ed67b --- /dev/null +++ b/tests/test97.c @@ -0,0 +1,27 @@ +#include "tpl.h" +#include +#include + +const char *filename = "/tmp/test97.tpl"; + +int main() { + tpl_node *tn; + int16_t j = -128; + uint16_t v=65535; + + tn = tpl_map("jv", &j, &v); + tpl_pack(tn,0); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + j = v = 0; + + tn = tpl_map("jv", &j, &v); + tpl_load(tn,TPL_FILE,filename); + tpl_unpack(tn,0); + tpl_free(tn); + + printf("j is %d, v is %d\n", (int)j, (int)v); + return(0); + +} diff --git a/tests/test98.ans b/tests/test98.ans new file mode 100644 index 0000000..6a2a1cc --- /dev/null +++ b/tests/test98.ans @@ -0,0 +1,3 @@ +j is -128, v is 65535 +j is -129, v is 65534 +j is -130, v is 65533 diff --git a/tests/test98.c b/tests/test98.c new file mode 100644 index 0000000..1eb9541 --- /dev/null +++ b/tests/test98.c @@ -0,0 +1,28 @@ +#include "tpl.h" +#include +#include + +const char *filename = "/tmp/test98.tpl"; + +int main() { + tpl_node *tn; + int16_t j = -128; + uint16_t v=65535; + + tn = tpl_map("A(jv)", &j, &v); + tpl_pack(tn,1); j -= 1; v-= 1; + tpl_pack(tn,1); j -= 1; v-= 1; + tpl_pack(tn,1); + tpl_dump(tn,TPL_FILE,filename); + tpl_free(tn); + + j = v = 0; + + tn = tpl_map("A(jv)", &j, &v); + tpl_load(tn,TPL_FILE,filename); + while (tpl_unpack(tn,1) > 0) { + printf("j is %d, v is %d\n", (int)j, (int)v); + } + tpl_free(tn); + return(0); +} diff --git a/tests/test99.ans b/tests/test99.ans new file mode 100644 index 0000000..3267dcf --- /dev/null +++ b/tests/test99.ans @@ -0,0 +1,2 @@ +cA(i#)S(cf#)A(ci#) +& diff --git a/tests/test99.c b/tests/test99.c new file mode 100755 index 0000000..f4ac12a --- /dev/null +++ b/tests/test99.c @@ -0,0 +1,14 @@ +#include +#include +#include "tpl.h" + +int main() { + char *fmt, c; + fmt = tpl_peek(TPL_FILE|TPL_DATAPEEK, "test99.tpl", "c", &c); + if (fmt) { + printf("%s\n",fmt); + free(fmt); + printf("%c\n", c); + } + return 0; +} diff --git a/tests/test99.tpl b/tests/test99.tpl new file mode 100644 index 0000000..bb934e3 Binary files /dev/null and b/tests/test99.tpl differ diff --git a/tests/threads/Makefile b/tests/threads/Makefile new file mode 100644 index 0000000..f44ca2e --- /dev/null +++ b/tests/threads/Makefile @@ -0,0 +1,26 @@ +TPLSRC = ../../src +PROGS = test1 + +# Thread support requires compiler-specific options +# ---------------------------------------------------------------------------- +# GNU +CFLAGS = -I$(TPLSRC) -g -pthread +# Solaris (Studio 11 on Sparc Ultra3) +#CFLAGS = -I$(TPLSRC) -g -mt +# ---------------------------------------------------------------------------- + +all: $(PROGS) run_tests + +tpl.o : $(TPLSRC)/tpl.c $(TPLSRC)/tpl.h + $(CC) -c $(CFLAGS) $(TPLSRC)/tpl.c + +$(PROGS) : tpl.o + $(CC) $(CFLAGS) -o $@ $(@).c tpl.o + +run_tests: $(PROGS) + perl ../do_tests + +.PHONY: clean + +clean: + rm -f $(PROGS) tpl.o test*.out diff --git a/tests/threads/README b/tests/threads/README new file mode 100644 index 0000000..44f5e5c --- /dev/null +++ b/tests/threads/README @@ -0,0 +1,3 @@ +Tests for using tpl in threaded programs. + +thread_ipc.c a thread version of the pipe IPC test ../test28.c diff --git a/tests/threads/test1.ans b/tests/threads/test1.ans new file mode 100644 index 0000000..5b92e81 --- /dev/null +++ b/tests/threads/test1.ans @@ -0,0 +1,2 @@ +sum is 49995000 +thread result: 0 null diff --git a/tests/threads/test1.c b/tests/threads/test1.c new file mode 100644 index 0000000..388021d --- /dev/null +++ b/tests/threads/test1.c @@ -0,0 +1,48 @@ +#include +#include +#include "tpl.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int fd[2]; + +void *thread_routine( void *arg ) { + tpl_node *tn; + int i,sum=0; + + /* child */ + tn = tpl_map("A(u)",&i); + tpl_load(tn, TPL_FD, fd[0]); + while (tpl_unpack(tn,1) > 0) sum += i; + tpl_free(tn); + printf("sum is %d\n", sum); + return NULL; +} + +int main() { + tpl_node *tn; + unsigned i; + int status; + pthread_t thread_id; + void *thread_result; + + pipe(fd); + if ( status = pthread_create( &thread_id, NULL, thread_routine, NULL )) { + printf("failure: status %d\n", status); + exit(-1); + } + /* parent */ + tn = tpl_map("A(u)",&i); + for(i=0;i<10000;i++) tpl_pack(tn,1); + tpl_dump(tn,TPL_FD, fd[1] ); + tpl_free(tn); + + status = pthread_join( thread_id, &thread_result ); + printf("thread result: %d %s\n", status, thread_result ? "non-null":"null"); +}