From feec3bb95026c5485078a153d5e7e57127070997 Mon Sep 17 00:00:00 2001 From: "Troy D. Hanson" Date: Tue, 12 Mar 2013 16:38:58 -0400 Subject: [PATCH] initial import --- LICENSE | 21 + Makefile.am | 2 + README | 56 + bootstrap | 11 + configure.ac | 28 + doc/Makefile | 26 + doc/NOTES | 11 + doc/html/ChangeLog.html | 796 +++++++++++ doc/html/img/banner.png | Bin 0 -> 24129 bytes doc/html/img/banner.svg | 429 ++++++ doc/html/img/grad_azure.png | Bin 0 -> 409 bytes doc/html/img/grad_azure.svg | 105 ++ doc/html/img/rss.png | Bin 0 -> 689 bytes doc/html/img/tpl-mini.png | Bin 0 -> 3133 bytes doc/html/img/tpl-mini.svg | 242 ++++ doc/html/img/tpl.dia | Bin 0 -> 4424 bytes doc/html/img/tpl.png | Bin 0 -> 12392 bytes doc/html/img/tpl_aai.dia | Bin 0 -> 4125 bytes doc/html/index.html | 162 +++ doc/html/license.html | 61 + doc/html/perl.html | 412 ++++++ doc/html/styles.css | 160 +++ doc/html/tdh-quirks.css | 41 + doc/html/tdh.css | 402 ++++++ doc/html/toc.css | 35 + doc/html/userguide.html | 1548 ++++++++++++++++++++++ doc/pdf/userguide.pdf | Bin 0 -> 255756 bytes doc/txt/ChangeLog.txt | 70 + doc/txt/compiling.txt | 38 + doc/txt/examples.txt | 297 +++++ doc/txt/future.txt | 52 + doc/txt/perl.txt | 302 +++++ doc/txt/sflogo.txt | 5 + doc/txt/toc.txt | 85 ++ doc/txt/topnav.txt | 10 + doc/txt/userguide.txt | 1264 ++++++++++++++++++ lang/perl/Tpl.pm | 475 +++++++ lang/perl/tests/Makefile | 9 + lang/perl/tests/README | 29 + lang/perl/tests/client.pl | 31 + lang/perl/tests/do_tests | 20 + lang/perl/tests/server.pl | 56 + lang/perl/tests/test1 | 23 + lang/perl/tests/test1.ans | 10 + lang/perl/tests/test10 | 16 + lang/perl/tests/test10.ans | 53 + lang/perl/tests/test11 | 24 + lang/perl/tests/test11.ans | 2 + lang/perl/tests/test12 | 23 + lang/perl/tests/test12.ans | 16 + lang/perl/tests/test13 | 16 + lang/perl/tests/test13.ans | 71 + lang/perl/tests/test14 | 17 + lang/perl/tests/test14.ans | 1 + lang/perl/tests/test14.tpl | Bin 0 -> 14 bytes lang/perl/tests/test15 | 17 + lang/perl/tests/test15.ans | 1 + lang/perl/tests/test15.tpl | Bin 0 -> 14 bytes lang/perl/tests/test16 | 22 + lang/perl/tests/test16.ans | 2 + lang/perl/tests/test17 | 25 + lang/perl/tests/test17.ans | 1 + lang/perl/tests/test18 | 23 + lang/perl/tests/test18.ans | 3 + lang/perl/tests/test19 | 25 + lang/perl/tests/test19.ans | 7 + lang/perl/tests/test2 | 18 + lang/perl/tests/test2.ans | 10 + lang/perl/tests/test20 | 36 + lang/perl/tests/test20.ans | 1 + lang/perl/tests/test21 | 15 + lang/perl/tests/test21.ans | 1 + lang/perl/tests/test22 | 21 + lang/perl/tests/test22.ans | 1 + lang/perl/tests/test23 | 21 + lang/perl/tests/test3 | 16 + lang/perl/tests/test3.ans | 53 + lang/perl/tests/test4 | 26 + lang/perl/tests/test4.ans | 1 + lang/perl/tests/test5 | 23 + lang/perl/tests/test5.ans | 10 + lang/perl/tests/test6 | 16 + lang/perl/tests/test6.ans | 53 + lang/perl/tests/test7 | 26 + lang/perl/tests/test7.ans | 1 + lang/perl/tests/test8 | 29 + lang/perl/tests/test8.ans | 1 + lang/perl/tests/test9 | 23 + lang/perl/tests/test9.ans | 10 + lang/perl/tplfmt | 24 + lang/perl/tplxml | 306 +++++ src/Makefile.am | 7 + src/tpl.c | 2478 +++++++++++++++++++++++++++++++++++ src/tpl.h | 137 ++ src/win/Makefile.am | 4 + src/win/README | 11 + src/win/mman.h | 52 + src/win/mmap.c | 171 +++ src/win/nonempty.c | 6 + tests/Makefile | 88 ++ tests/Makefile.alt | 31 + tests/Makefile.mingw | 25 + tests/README | 125 ++ tests/dbx.sh | 24 + tests/do_tests | 22 + tests/do_tests.cygwin | 21 + tests/int64_align.c | 19 + tests/malign.c | 20 + tests/mgwtest.c | 56 + tests/other/Makefile | 21 + tests/other/README | 2 + tests/other/do_tests | 22 + tests/other/other1.ans | 3 + tests/other/other1.cpp | 40 + tests/sizes | 23 + tests/test1-mingw.c | 12 + tests/test1.ans | 1 + tests/test1.c | 24 + tests/test10.ans | 10 + tests/test10.c | 13 + tests/test10.tpl | Bin 0 -> 57 bytes tests/test100.ans | 2 + tests/test100.c | 27 + tests/test101.ans | 2 + tests/test101.c | 30 + tests/test102.ans | 2 + tests/test102.c | 22 + tests/test103.ans | 2 + tests/test103.c | 33 + tests/test104.ans | 2 + tests/test104.c | 33 + tests/test105.ans | 2 + tests/test105.c | 29 + tests/test106.ans | 24 + tests/test106.c | 126 ++ tests/test107.ans | 5 + tests/test107.c | 38 + tests/test108.ans | 2 + tests/test108.c | 49 + tests/test109.ans | 2 + tests/test109.c | 35 + tests/test11.ans | 1 + tests/test11.c | 17 + tests/test11.tpl | Bin 0 -> 57 bytes tests/test110.ans | 1 + tests/test110.c | 78 ++ tests/test111.ans | 1 + tests/test111.c | 45 + tests/test112.ans | 1 + tests/test112.c | 47 + tests/test113.ans | 5 + tests/test113.c | 53 + tests/test114.ans | 1 + tests/test114.c | 45 + tests/test115.ans | 40 + tests/test115.c | 119 ++ tests/test116.ans | 4 + tests/test116.c | 29 + tests/test117.ans | 10 + tests/test117.c | 47 + tests/test118.ans | 5 + tests/test118.c | 55 + tests/test119.ans | 1 + tests/test119.c | 41 + tests/test12.ans | 1 + tests/test12.c | 17 + tests/test12.tpl | Bin 0 -> 57 bytes tests/test120.ans | 12 + tests/test120.c | 24 + tests/test121.ans | 6 + tests/test121.c | 31 + tests/test122.ans | 1 + tests/test122.c | 40 + tests/test123.ans | 1 + tests/test123.c | 33 + tests/test124.ans | 4 + tests/test124.c | 38 + tests/test13.ans | 1 + tests/test13.c | 17 + tests/test13.tpl | Bin 0 -> 57 bytes tests/test14.ans | 1 + tests/test14.c | 17 + tests/test14.tpl | Bin 0 -> 57 bytes tests/test15.ans | 1 + tests/test15.c | 17 + tests/test15.tpl | Bin 0 -> 57 bytes tests/test16.ans | 1 + tests/test16.c | 17 + tests/test16.tpl | Bin 0 -> 57 bytes tests/test17.ans | 1 + tests/test17.c | 17 + tests/test17.tpl | Bin 0 -> 57 bytes tests/test18.ans | 2 + tests/test18.c | 15 + tests/test19.ans | 2 + tests/test19.c | 15 + tests/test2.ans | 1 + tests/test2.c | 21 + tests/test20.ans | 2 + tests/test20.c | 15 + tests/test21.ans | 5 + tests/test21.c | 18 + tests/test22.ans | 10 + tests/test22.c | 18 + tests/test23.ans | 10 + tests/test23.c | 29 + tests/test24.ans | 10 + tests/test24.c | 26 + tests/test24.tpl | Bin 0 -> 58 bytes tests/test25.ans | 1 + tests/test25.c | 27 + tests/test25.tpl | Bin 0 -> 58 bytes tests/test26.ans | 2 + tests/test26.c | 85 ++ tests/test26_0.tpl | Bin 0 -> 57 bytes tests/test26_1.tpl | Bin 0 -> 4017 bytes tests/test26_2.tpl | Bin 0 -> 2000 bytes tests/test26_3.tpl | Bin 0 -> 2017 bytes tests/test27.ans | 10 + tests/test27.c | 35 + tests/test28.ans | 1 + tests/test28.c | 38 + tests/test29.ans | 2 + tests/test29.c | 50 + tests/test3.ans | 10 + tests/test3.c | 18 + tests/test30.ans | 2 + tests/test30.c | 40 + tests/test31.ans | 2 + tests/test31.c | 38 + tests/test32.ans | 10 + tests/test32.c | 44 + tests/test33.ans | 2 + tests/test33.c | 23 + tests/test34.ans | 15 + tests/test34.c | 21 + tests/test35.ans | 0 tests/test35.c | 16 + tests/test36.ans | 3 + tests/test36.c | 22 + tests/test37.ans | 0 tests/test37.c | 20 + tests/test38.ans | 9 + tests/test38.c | 19 + tests/test39.ans | 2 + tests/test39.c | 17 + tests/test39.tpl | Bin 0 -> 57 bytes tests/test4.ans | 10 + tests/test4.c | 22 + tests/test40.ans | 0 tests/test40.c | 18 + tests/test41.ans | 2 + tests/test41.c | 18 + tests/test42.ans | 0 tests/test42.c | 16 + tests/test43.ans | 0 tests/test43.c | 20 + tests/test44.ans | 0 tests/test44.c | 23 + tests/test45.ans | 2 + tests/test45.c | 21 + tests/test46.ans | 0 tests/test46.c | 14 + tests/test47.ans | 0 tests/test47.c | 14 + tests/test48.ans | 1 + tests/test48.c | 15 + tests/test49.ans | 0 tests/test49.c | 18 + tests/test5.ans | 100 ++ tests/test5.c | 27 + tests/test50.ans | 2 + tests/test50.c | 19 + tests/test51.ans | 1 + tests/test51.c | 57 + tests/test51_0.tpl | Bin 0 -> 57 bytes tests/test51_1.tpl | Bin 0 -> 114 bytes tests/test51_2.tpl | Bin 0 -> 30 bytes tests/test51_3.tpl | Bin 0 -> 20 bytes tests/test51_4.tpl | Bin 0 -> 7 bytes tests/test52.ans | 2 + tests/test52.c | 30 + tests/test53.ans | 3 + tests/test53.c | 30 + tests/test54.ans | 2 + tests/test54.c | 63 + tests/test54_0.tpl | Bin 0 -> 57 bytes tests/test54_1.tpl | Bin 0 -> 114 bytes tests/test54_2.tpl | Bin 0 -> 30 bytes tests/test54_3.tpl | Bin 0 -> 20 bytes tests/test54_4.tpl | Bin 0 -> 7 bytes tests/test55.ans | 3 + tests/test55.c | 91 ++ tests/test55_0.tpl | Bin 0 -> 57 bytes tests/test55_1.tpl | Bin 0 -> 4017 bytes tests/test55_2.tpl | Bin 0 -> 2000 bytes tests/test55_3.tpl | Bin 0 -> 2017 bytes tests/test56.ans | 4 + tests/test56.c | 42 + tests/test57.ans | 0 tests/test57.c | 28 + tests/test58.ans | 4 + tests/test58.c | 30 + tests/test59.ans | 6 + tests/test59.c | 47 + tests/test6.ans | 1 + tests/test6.c | 25 + tests/test60.ans | 10 + tests/test60.c | 18 + tests/test61.ans | 20 + tests/test61.c | 18 + tests/test61_0.tpl | Bin 0 -> 57 bytes tests/test61_1.tpl | Bin 0 -> 57 bytes tests/test62.ans | 20 + tests/test62.c | 20 + tests/test62_0.tpl | Bin 0 -> 57 bytes tests/test63.ans | 20 + tests/test63.c | 22 + tests/test64.ans | 3 + tests/test64.c | 80 ++ tests/test65.ans | 0 tests/test65.c | 16 + tests/test66.ans | 0 tests/test66.c | 18 + tests/test67.ans | 3 + tests/test67.c | 40 + tests/test68.ans | 0 tests/test68.c | 37 + tests/test69.ans | 4 + tests/test69.c | 39 + tests/test7.ans | 3 + tests/test7.c | 27 + tests/test70.ans | 7 + tests/test70.c | 34 + tests/test71.ans | 7 + tests/test71.c | 34 + tests/test72.ans | 7 + tests/test72.c | 34 + tests/test73.ans | 7 + tests/test73.c | 34 + tests/test74.ans | 301 +++++ tests/test74.c | 39 + tests/test75.ans | 2 + tests/test75.c | 38 + tests/test76.ans | 2 + tests/test76.c | 38 + tests/test77.ans | 2 + tests/test77.c | 38 + tests/test78.ans | 0 tests/test78.c | 23 + tests/test79.ans | 3 + tests/test79.c | 22 + tests/test8.ans | 10 + tests/test8.c | 27 + tests/test80.ans | 1 + tests/test80.c | 30 + tests/test81.ans | 2 + tests/test81.c | 36 + tests/test82.ans | 0 tests/test82.c | 43 + tests/test83.ans | 7 + tests/test83.c | 42 + tests/test84.ans | 14 + tests/test84.c | 63 + tests/test84_0.tpl | Bin 0 -> 308 bytes tests/test84_1.tpl | Bin 0 -> 308 bytes tests/test85.ans | 1 + tests/test85.c | 13 + tests/test85.tpl | Bin 0 -> 308 bytes tests/test86.ans | 1 + tests/test86.c | 27 + tests/test87.ans | 1 + tests/test87.c | 47 + tests/test88.ans | 0 tests/test88.c | 18 + tests/test89.ans | 3 + tests/test89.c | 21 + tests/test9.ans | 10 + tests/test9.c | 13 + tests/test9.tpl | Bin 0 -> 57 bytes tests/test90.ans | 1 + tests/test90.c | 23 + tests/test91.ans | 1 + tests/test91.c | 23 + tests/test92.ans | 0 tests/test92.c | 39 + tests/test93.ans | 1 + tests/test93.c | 21 + tests/test94.ans | 5 + tests/test94.c | 26 + tests/test95.ans | 3 + tests/test95.c | 23 + tests/test96.ans | 15 + tests/test96.c | 27 + tests/test97.ans | 1 + tests/test97.c | 27 + tests/test98.ans | 3 + tests/test98.c | 28 + tests/test99.ans | 2 + tests/test99.c | 14 + tests/test99.tpl | Bin 0 -> 308 bytes tests/threads/Makefile | 26 + tests/threads/README | 3 + tests/threads/test1.ans | 2 + tests/threads/test1.c | 48 + 405 files changed, 16806 insertions(+) create mode 100755 LICENSE create mode 100644 Makefile.am create mode 100644 README create mode 100755 bootstrap create mode 100644 configure.ac create mode 100644 doc/Makefile create mode 100644 doc/NOTES create mode 100644 doc/html/ChangeLog.html create mode 100644 doc/html/img/banner.png create mode 100644 doc/html/img/banner.svg create mode 100644 doc/html/img/grad_azure.png create mode 100644 doc/html/img/grad_azure.svg create mode 100755 doc/html/img/rss.png create mode 100644 doc/html/img/tpl-mini.png create mode 100644 doc/html/img/tpl-mini.svg create mode 100644 doc/html/img/tpl.dia create mode 100644 doc/html/img/tpl.png create mode 100644 doc/html/img/tpl_aai.dia create mode 100644 doc/html/index.html create mode 100644 doc/html/license.html create mode 100644 doc/html/perl.html create mode 100644 doc/html/styles.css create mode 100644 doc/html/tdh-quirks.css create mode 100644 doc/html/tdh.css create mode 100644 doc/html/toc.css create mode 100644 doc/html/userguide.html create mode 100644 doc/pdf/userguide.pdf create mode 100644 doc/txt/ChangeLog.txt create mode 100644 doc/txt/compiling.txt create mode 100644 doc/txt/examples.txt create mode 100644 doc/txt/future.txt create mode 100644 doc/txt/perl.txt create mode 100644 doc/txt/sflogo.txt create mode 100644 doc/txt/toc.txt create mode 100644 doc/txt/topnav.txt create mode 100644 doc/txt/userguide.txt create mode 100644 lang/perl/Tpl.pm create mode 100644 lang/perl/tests/Makefile create mode 100644 lang/perl/tests/README create mode 100755 lang/perl/tests/client.pl create mode 100755 lang/perl/tests/do_tests create mode 100755 lang/perl/tests/server.pl create mode 100755 lang/perl/tests/test1 create mode 100644 lang/perl/tests/test1.ans create mode 100755 lang/perl/tests/test10 create mode 100644 lang/perl/tests/test10.ans create mode 100755 lang/perl/tests/test11 create mode 100644 lang/perl/tests/test11.ans create mode 100755 lang/perl/tests/test12 create mode 100644 lang/perl/tests/test12.ans create mode 100755 lang/perl/tests/test13 create mode 100644 lang/perl/tests/test13.ans create mode 100755 lang/perl/tests/test14 create mode 100644 lang/perl/tests/test14.ans create mode 100644 lang/perl/tests/test14.tpl create mode 100755 lang/perl/tests/test15 create mode 100644 lang/perl/tests/test15.ans create mode 100644 lang/perl/tests/test15.tpl create mode 100755 lang/perl/tests/test16 create mode 100644 lang/perl/tests/test16.ans create mode 100755 lang/perl/tests/test17 create mode 100644 lang/perl/tests/test17.ans create mode 100755 lang/perl/tests/test18 create mode 100644 lang/perl/tests/test18.ans create mode 100755 lang/perl/tests/test19 create mode 100644 lang/perl/tests/test19.ans create mode 100755 lang/perl/tests/test2 create mode 100644 lang/perl/tests/test2.ans create mode 100755 lang/perl/tests/test20 create mode 100644 lang/perl/tests/test20.ans create mode 100755 lang/perl/tests/test21 create mode 100644 lang/perl/tests/test21.ans create mode 100755 lang/perl/tests/test22 create mode 100644 lang/perl/tests/test22.ans create mode 100755 lang/perl/tests/test23 create mode 100755 lang/perl/tests/test3 create mode 100644 lang/perl/tests/test3.ans create mode 100755 lang/perl/tests/test4 create mode 100644 lang/perl/tests/test4.ans create mode 100755 lang/perl/tests/test5 create mode 100644 lang/perl/tests/test5.ans create mode 100755 lang/perl/tests/test6 create mode 100644 lang/perl/tests/test6.ans create mode 100755 lang/perl/tests/test7 create mode 100644 lang/perl/tests/test7.ans create mode 100755 lang/perl/tests/test8 create mode 100644 lang/perl/tests/test8.ans create mode 100755 lang/perl/tests/test9 create mode 100644 lang/perl/tests/test9.ans create mode 100755 lang/perl/tplfmt create mode 100755 lang/perl/tplxml create mode 100644 src/Makefile.am create mode 100644 src/tpl.c create mode 100644 src/tpl.h create mode 100644 src/win/Makefile.am create mode 100644 src/win/README create mode 100644 src/win/mman.h create mode 100644 src/win/mmap.c create mode 100644 src/win/nonempty.c create mode 100644 tests/Makefile create mode 100644 tests/Makefile.alt create mode 100644 tests/Makefile.mingw create mode 100644 tests/README create mode 100644 tests/dbx.sh create mode 100755 tests/do_tests create mode 100755 tests/do_tests.cygwin create mode 100644 tests/int64_align.c create mode 100644 tests/malign.c create mode 100644 tests/mgwtest.c create mode 100644 tests/other/Makefile create mode 100644 tests/other/README create mode 100755 tests/other/do_tests create mode 100644 tests/other/other1.ans create mode 100644 tests/other/other1.cpp create mode 100755 tests/sizes create mode 100644 tests/test1-mingw.c create mode 100644 tests/test1.ans create mode 100644 tests/test1.c create mode 100644 tests/test10.ans create mode 100644 tests/test10.c create mode 100644 tests/test10.tpl create mode 100644 tests/test100.ans create mode 100644 tests/test100.c create mode 100644 tests/test101.ans create mode 100644 tests/test101.c create mode 100644 tests/test102.ans create mode 100644 tests/test102.c create mode 100644 tests/test103.ans create mode 100644 tests/test103.c create mode 100644 tests/test104.ans create mode 100644 tests/test104.c create mode 100644 tests/test105.ans create mode 100644 tests/test105.c create mode 100644 tests/test106.ans create mode 100644 tests/test106.c create mode 100644 tests/test107.ans create mode 100644 tests/test107.c create mode 100644 tests/test108.ans create mode 100644 tests/test108.c create mode 100644 tests/test109.ans create mode 100644 tests/test109.c create mode 100644 tests/test11.ans create mode 100644 tests/test11.c create mode 100644 tests/test11.tpl create mode 100644 tests/test110.ans create mode 100644 tests/test110.c create mode 100644 tests/test111.ans create mode 100644 tests/test111.c create mode 100644 tests/test112.ans create mode 100644 tests/test112.c create mode 100644 tests/test113.ans create mode 100644 tests/test113.c create mode 100644 tests/test114.ans create mode 100644 tests/test114.c create mode 100644 tests/test115.ans create mode 100644 tests/test115.c create mode 100644 tests/test116.ans create mode 100644 tests/test116.c create mode 100644 tests/test117.ans create mode 100644 tests/test117.c create mode 100644 tests/test118.ans create mode 100644 tests/test118.c create mode 100644 tests/test119.ans create mode 100644 tests/test119.c create mode 100644 tests/test12.ans create mode 100644 tests/test12.c create mode 100644 tests/test12.tpl create mode 100644 tests/test120.ans create mode 100644 tests/test120.c create mode 100644 tests/test121.ans create mode 100644 tests/test121.c create mode 100644 tests/test122.ans create mode 100644 tests/test122.c create mode 100644 tests/test123.ans create mode 100644 tests/test123.c create mode 100644 tests/test124.ans create mode 100644 tests/test124.c create mode 100644 tests/test13.ans create mode 100644 tests/test13.c create mode 100644 tests/test13.tpl create mode 100644 tests/test14.ans create mode 100644 tests/test14.c create mode 100644 tests/test14.tpl create mode 100644 tests/test15.ans create mode 100644 tests/test15.c create mode 100644 tests/test15.tpl create mode 100644 tests/test16.ans create mode 100644 tests/test16.c create mode 100644 tests/test16.tpl create mode 100644 tests/test17.ans create mode 100644 tests/test17.c create mode 100644 tests/test17.tpl create mode 100644 tests/test18.ans create mode 100644 tests/test18.c create mode 100644 tests/test19.ans create mode 100644 tests/test19.c create mode 100644 tests/test2.ans create mode 100644 tests/test2.c create mode 100644 tests/test20.ans create mode 100644 tests/test20.c create mode 100644 tests/test21.ans create mode 100644 tests/test21.c create mode 100644 tests/test22.ans create mode 100644 tests/test22.c create mode 100644 tests/test23.ans create mode 100644 tests/test23.c create mode 100644 tests/test24.ans create mode 100644 tests/test24.c create mode 100644 tests/test24.tpl create mode 100644 tests/test25.ans create mode 100644 tests/test25.c create mode 100644 tests/test25.tpl create mode 100644 tests/test26.ans create mode 100644 tests/test26.c create mode 100644 tests/test26_0.tpl create mode 100644 tests/test26_1.tpl create mode 100644 tests/test26_2.tpl create mode 100644 tests/test26_3.tpl create mode 100644 tests/test27.ans create mode 100644 tests/test27.c create mode 100644 tests/test28.ans create mode 100644 tests/test28.c create mode 100644 tests/test29.ans create mode 100644 tests/test29.c create mode 100644 tests/test3.ans create mode 100644 tests/test3.c create mode 100644 tests/test30.ans create mode 100644 tests/test30.c create mode 100644 tests/test31.ans create mode 100644 tests/test31.c create mode 100644 tests/test32.ans create mode 100644 tests/test32.c create mode 100644 tests/test33.ans create mode 100644 tests/test33.c create mode 100644 tests/test34.ans create mode 100644 tests/test34.c create mode 100644 tests/test35.ans create mode 100644 tests/test35.c create mode 100644 tests/test36.ans create mode 100644 tests/test36.c create mode 100644 tests/test37.ans create mode 100644 tests/test37.c create mode 100644 tests/test38.ans create mode 100644 tests/test38.c create mode 100644 tests/test39.ans create mode 100644 tests/test39.c create mode 100644 tests/test39.tpl create mode 100644 tests/test4.ans create mode 100644 tests/test4.c create mode 100644 tests/test40.ans create mode 100644 tests/test40.c create mode 100644 tests/test41.ans create mode 100644 tests/test41.c create mode 100644 tests/test42.ans create mode 100644 tests/test42.c create mode 100644 tests/test43.ans create mode 100644 tests/test43.c create mode 100644 tests/test44.ans create mode 100644 tests/test44.c create mode 100644 tests/test45.ans create mode 100644 tests/test45.c create mode 100644 tests/test46.ans create mode 100644 tests/test46.c create mode 100644 tests/test47.ans create mode 100644 tests/test47.c create mode 100644 tests/test48.ans create mode 100644 tests/test48.c create mode 100644 tests/test49.ans create mode 100644 tests/test49.c create mode 100644 tests/test5.ans create mode 100644 tests/test5.c create mode 100644 tests/test50.ans create mode 100644 tests/test50.c create mode 100644 tests/test51.ans create mode 100644 tests/test51.c create mode 100644 tests/test51_0.tpl create mode 100644 tests/test51_1.tpl create mode 100644 tests/test51_2.tpl create mode 100644 tests/test51_3.tpl create mode 100644 tests/test51_4.tpl create mode 100644 tests/test52.ans create mode 100644 tests/test52.c create mode 100644 tests/test53.ans create mode 100644 tests/test53.c create mode 100644 tests/test54.ans create mode 100644 tests/test54.c create mode 100644 tests/test54_0.tpl create mode 100644 tests/test54_1.tpl create mode 100644 tests/test54_2.tpl create mode 100644 tests/test54_3.tpl create mode 100644 tests/test54_4.tpl create mode 100644 tests/test55.ans create mode 100644 tests/test55.c create mode 100644 tests/test55_0.tpl create mode 100644 tests/test55_1.tpl create mode 100644 tests/test55_2.tpl create mode 100644 tests/test55_3.tpl create mode 100644 tests/test56.ans create mode 100644 tests/test56.c create mode 100644 tests/test57.ans create mode 100644 tests/test57.c create mode 100644 tests/test58.ans create mode 100644 tests/test58.c create mode 100644 tests/test59.ans create mode 100644 tests/test59.c create mode 100644 tests/test6.ans create mode 100644 tests/test6.c create mode 100644 tests/test60.ans create mode 100644 tests/test60.c create mode 100644 tests/test61.ans create mode 100644 tests/test61.c create mode 100644 tests/test61_0.tpl create mode 100644 tests/test61_1.tpl create mode 100644 tests/test62.ans create mode 100644 tests/test62.c create mode 100644 tests/test62_0.tpl create mode 100644 tests/test63.ans create mode 100644 tests/test63.c create mode 100644 tests/test64.ans create mode 100644 tests/test64.c create mode 100644 tests/test65.ans create mode 100644 tests/test65.c create mode 100644 tests/test66.ans create mode 100644 tests/test66.c create mode 100644 tests/test67.ans create mode 100644 tests/test67.c create mode 100644 tests/test68.ans create mode 100644 tests/test68.c create mode 100644 tests/test69.ans create mode 100644 tests/test69.c create mode 100644 tests/test7.ans create mode 100644 tests/test7.c create mode 100644 tests/test70.ans create mode 100644 tests/test70.c create mode 100644 tests/test71.ans create mode 100644 tests/test71.c create mode 100644 tests/test72.ans create mode 100644 tests/test72.c create mode 100644 tests/test73.ans create mode 100644 tests/test73.c create mode 100644 tests/test74.ans create mode 100644 tests/test74.c create mode 100644 tests/test75.ans create mode 100644 tests/test75.c create mode 100644 tests/test76.ans create mode 100644 tests/test76.c create mode 100644 tests/test77.ans create mode 100644 tests/test77.c create mode 100644 tests/test78.ans create mode 100644 tests/test78.c create mode 100644 tests/test79.ans create mode 100644 tests/test79.c create mode 100644 tests/test8.ans create mode 100644 tests/test8.c create mode 100644 tests/test80.ans create mode 100644 tests/test80.c create mode 100644 tests/test81.ans create mode 100755 tests/test81.c create mode 100644 tests/test82.ans create mode 100755 tests/test82.c create mode 100644 tests/test83.ans create mode 100755 tests/test83.c create mode 100644 tests/test84.ans create mode 100755 tests/test84.c create mode 100644 tests/test84_0.tpl create mode 100644 tests/test84_1.tpl create mode 100644 tests/test85.ans create mode 100755 tests/test85.c create mode 100644 tests/test85.tpl create mode 100644 tests/test86.ans create mode 100644 tests/test86.c create mode 100644 tests/test87.ans create mode 100644 tests/test87.c create mode 100644 tests/test88.ans create mode 100644 tests/test88.c create mode 100644 tests/test89.ans create mode 100644 tests/test89.c create mode 100644 tests/test9.ans create mode 100644 tests/test9.c create mode 100644 tests/test9.tpl create mode 100644 tests/test90.ans create mode 100644 tests/test90.c create mode 100644 tests/test91.ans create mode 100644 tests/test91.c create mode 100644 tests/test92.ans create mode 100644 tests/test92.c create mode 100644 tests/test93.ans create mode 100644 tests/test93.c create mode 100644 tests/test94.ans create mode 100644 tests/test94.c create mode 100644 tests/test95.ans create mode 100644 tests/test95.c create mode 100644 tests/test96.ans create mode 100644 tests/test96.c create mode 100644 tests/test97.ans create mode 100644 tests/test97.c create mode 100644 tests/test98.ans create mode 100644 tests/test98.c create mode 100644 tests/test99.ans create mode 100755 tests/test99.c create mode 100644 tests/test99.tpl create mode 100644 tests/threads/Makefile create mode 100644 tests/threads/README create mode 100644 tests/threads/test1.ans create mode 100644 tests/threads/test1.c 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 0000000000000000000000000000000000000000..ba9ffe8f9641192e3811f3ec088655271372a14b GIT binary patch literal 24129 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..bf5c0cf2d4fed179b1c1cfdc6c00901f14ddba3a GIT binary patch literal 409 zcmeAS@N?(olHy`uVBq!ia0vp^tAUt{gAGWYUa<}&z?S6g?!xdN1Q+aGJ{c&&S>O>_ z%)r1c48n{Iv*t(u1=&kHeO=kFv9q%nn@P`k>BqppsOIV77!q;#?WBXNLL7NqRqMisNBN|6F{kd={ri=>a@&S0b27Rkw))+9Zu6e? zA$lnBb;|wq9H)C( zSDZfNEgGH7pnsLa`u9B<-j&5=T`4!D0>ihlDJQL(Ggt8Ra{j9iBeyS_^~NSicJH&9 z(_30+ao8qh+}Q2bZ#D7y|M^>$OI$w%Io=7FSny(!#}zM@UDG(iC!Sa&zG%g3A?~gt zyS~j~ZP<}F=a2Km9drMDJh%7D+r=5>|D!Wie|6TeojUja@1^w}W%b|RM_=b(Iq5>Y vp;X)d7Q@#DX|io_vL~_~$Oe)R@Bd&H^5s*EdXc&w7>Ep>u6{1-oD!M + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/doc/html/img/rss.png b/doc/html/img/rss.png new file mode 100755 index 0000000000000000000000000000000000000000..b3c949d2244f2c0c81d65e74719af2a1b56d06a3 GIT binary patch literal 689 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 X;XdzzW@l(h00000NkvXXu0mjfJbf0d literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d63ed77247675acbf332906f2669cb4ee737b9e7 GIT binary patch literal 4424 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 + + + + 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 0000000000000000000000000000000000000000..ddac2c87b59b9a318a3f50d91fb28f01660ad156 GIT binary patch literal 255756 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 z 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 z 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f11ee5fae5db852a9b1a5222afbc0eb2fd03095a GIT binary patch literal 14 VcmXRZ$YEq)VBpJS`2YX^KL8#61;hXV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..69249f79a729b78228e65552a7187f8538fda49b GIT binary patch literal 14 VcmXRZ$YJ1PU|`5(`1}9=e*hj91;PLT literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d3a3e3de65cc79942e8a4a2f0bef4d8b5771129b GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6sM2KM0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwej{?mA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..de74a8b2ac248cf9c059776de5301cd644fe0626 GIT binary patch literal 57 WcmXRZ$YHQ#U|?|6$kfav1pojn#T5Dg literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bf10054303fdaa63bab03d23e666f1582d91897c GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u1TsK?5r~<9m>Gy!fS47C*?^cGh&g~5qz(XxL;|}2 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8dfd4a5148faaf4dea7529835ad5fb0de4b888fd GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u1~Nc^5r~<9m>Gy!fS47C*?^cGh&h0m6NmwboC3W7 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..80a83384cc24bc3786bd3aaf847b292b8d4a8a82 GIT binary patch literal 57 ucmXRZC}FTGy!fS47C*?^cGh&h0m6NmwdC<4R) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4ef0eaad698fe07112a2734cc69bcbcf3242a21c GIT binary patch literal 57 ucmXRZ$YHQzU|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwbr2@SG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b03586065bbf754f255034ebd46f39cedd35dbf GIT binary patch literal 58 wcmXRZ$YHQ#U|?|6DAi=(0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nnc80FDd;XaE2J literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b03586065bbf754f255034ebd46f39cedd35dbf GIT binary patch literal 58 wcmXRZ$YHQ#U|?|6DAi=(0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nnc80FDd;XaE2J literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 diff --git a/tests/test26_1.tpl b/tests/test26_1.tpl new file mode 100644 index 0000000000000000000000000000000000000000..85ae076aa787b733208636b51b59a603f8bb37bb GIT binary patch literal 4017 zcmW;OQ_vWQ4hGP_wr$(CZQHhO+qP}nwrzX2-L1L3r#F-H&EzGoN!N~T12#nn2q=`Y zRjPoOK?43?fn;F*j6n%Ta6%B0P=qE7VF^cg>lxi$tXrMhOvxeJQJA6 zBqlS3sZ3)!GnmONW;2Jm%ws+aSjZw4vxKEAV>v5W$tqT}hPA9?Jsa4_CN{H$t!!gE zJJ`uCcC&}Q>|;L%ILILmbA+QD<2WZc$tg~AhO?aGJQujgB`$M?t6bwcH@L|yZgYpb z+~YnEc*r9j^Mt27<2f&Q$tzy-hQIimx4h#Y{^dO%_{b+d^M$W`<2yh2$uIunHvxe( zfeAuTf)Sh$gd`N92}4-I5uOM{BodK{LR6v=ofyO<7O{y#T;dU*1SBL8iAh3Il98Mg zq$CxoNkdxFk)8}>Bomp*LRPYoogCyO7rDtpUhrl%y1;DMMMx zQJxA^q!N{>LRG3!of_1n7PYBEUFuPv1~jA*jcGztn$esVw4@cSX+vAu(Vh-;q!XR# zLRY%cogVb07rp62U;5FX0SsgigBik5hB2HGjARs}8N*n{F`fxbWD=8^!c?X)of*tz z7PFbdT;?&K1uSF{i&?@_ma&`_tYj6dS;Jb^v7QZVWD}d&!dAAiogM6C7rWWRUiPt{ z103WKhdIJgj&Yn5oa7XzIm21bah?lYUG8z82R!5vk9opV zp7ER)yyO+HdBb1)&0F5_5C8I>4}9bkpZUU9zVV$O{Nxw^@tZ$~KM;WlLQsMcoDhU0 z6rl-2Si%vW2t*_jk%>Z7q7j`K#3UB6i9=lC5uXGkBoT>8LQ;~EoD`%a6{$%>TGEl8 z3}hq|naM&{vXPw}F`or2 zWD$#5!cvy8oE5BO6{}gpTGp|i4Qyl+o7uuvwy~WZ>|__a*~4D;v7ZARh2uUbH6Na#aBRmm^ zNF*W?g{VX$Ix&bzEMgOfxWpqq2}npH5|f0aBqKQ~NJ%PElZLdUBRv_&NG39qg{)*F zJ2}WnE^?EHyyPQ41t>@%3R8rl6r(sLC`l}a>$Rs8+g{e$qIy0EbEM_x@xy)le3s}e^7PEw-V?7(#$R;+kg{^F3J3H9PE_Snrz3gK@2RO(f4s(Q~9OF1AILRqabB42= p<2)C*$R#dwg{xfSIybn;fX**A`zJ=L?s&j1Y!`ASi~j{afwHK5|EHYBqj+-Nk(!~ zkdjoSCJkvxM|v_4Ohz)1nJi=_8`;T0PI8f(Jme)G`6)m_3Q?FM6r~u&DM3j}QJON8 zr5xp{Kt(E1nJQGJ8r7*mO=?k_I@F~e^=Uvu8qt_0G^H8MX+cX`(V8~2r5)|*Ku0>! znJ#pt8{O$aPkPatKJ=v@{TaYO1~Hf+3}qO@8NoeG#AU83dBtnq@RoPH=K~-4#Am+nmGAhTANY}<_?ch$mEZWCKlqcs_?v(DMj%KC zMQFkhmT-h80uhNsWTFt2XhbIlF^NTN;t-d3#3um>Nkn3jkd$O3Cj}`P^DMC?-QJfN#q!gtoLs`mEo(fc?5|yb! zRjN^)8q}l~wW&j0>QSEtG^7!YX+l$)(VP~vq!q1cLtEO>o(^=R6P@WoSGv)i9`vLa zz3D?=`q7^O3}g_48NyJ8F`N;MWE7(r!&t^Ko(W835|f$2RHiYV8O&rBvzfzO<}sfI zEMyUjS;A75v78mGWEHDf!&=s{o(*hd6Pww>R<^O75O%PWUF>ELd)dc+4seh|9Oei| zImU5LaFSD;<_u>!$9XPrkxN|W3Rk(tb#8EzTioUjce%%X9`KMyJmv{cdB$^I@RC=& V<_&Lo$9q2TkxzW)3txi*{{l=6^ltzF literal 0 HcmV?d00001 diff --git a/tests/test26_3.tpl b/tests/test26_3.tpl new file mode 100644 index 0000000000000000000000000000000000000000..bb209fcd20380c224dfc2e9e2faa5d0d7bb5c287 GIT binary patch literal 2017 zcmWO5LjoKK006M5ZQHhO+qP}nwr$(CZQJd7Yb%F5A%WLH0)aQYy8VCd-D8UF$2tpEy(1al@;RsIzA`*$nL?J5Ch)xV*5{uZxAujQVPXZE> zh{PlzDalAq3R04a)TALT=}1ooGLnhRWFafr$W9J&l8fBrAusvJPXP*2h{6=1D8(pF z2})9m(v+brs7?)PQj6Nup)U2PPXij#h{iObDa~k33tG~O*0iB5 z?PyO2I?{>GbfGKV=uQuM(u>~op)dXD&j1E8h`|hDD8m@e2u3oB(Trg%;~38bCNhc1 zOkpb1n9dAlGK<;FVJ`ES&jJ>*h{Y^nDa%;S3Rbd;)vRGH>sZeQHnNG$Y+)*>T;VF$xXul3a*NyC;V$>M z&jTLvh{rtPDbIM$3tsYyKlqcs_?v(Dm;d;m*Sz5^?|9D#KJtmreBmqK_|6Z0@{2$) z{|QPkf)j#}gd#Ly2unD^6M=|CA~I2kN;IMqgP6o3HgSkcJmQmpgd`#{Nk~dEl9Pgz zq#`wGNJ~1>lYxw6A~RXYN;a~SgPi0dH+jfQKJrt5f)t`KMJP%!ic^A;l%h0cC`&oY zQ-O+9qB2#eN;RregPPQ$Hg%{=J?hhdhBTrvO=wCpn$v=ow4ya_XiGcV(}9k3qBC9S zN;kUGgP!!FH+|?!Kl(F(fed0WLm0|1hBJbZjAArn7|S@uGl7XrVlq>h$~2}kgPF`? zHglNEJm#~2g)Cw*OIXS>ma~GDtYS55Sj#%rvw@9lVl!LV$~LyMgPrVRH+$I2KK65f zgB;>8M>xtcj&p*OoZ>WRILkTCbAgLo;xbpb$~CTYgPYvqHg~woJ?`^>hdkmjPk72R Kp7Vm2yy7>;S^RAP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..37d246ccf5180e82d20b4c4dcc7f6f92a25d01a8 GIT binary patch literal 57 ucmXRZ$YEt*V6b%5$kYVV3|t@r2pEBw35c12m<5PIir9dd9f&!Am=geqE&{;- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 diff --git a/tests/test51_1.tpl b/tests/test51_1.tpl new file mode 100644 index 0000000000000000000000000000000000000000..b056e6b04d65a73da77c6e3306d6f87644ff05cc GIT binary patch literal 114 ycmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6NpR5)d&Erk_C4F literal 0 HcmV?d00001 diff --git a/tests/test51_2.tpl b/tests/test51_2.tpl new file mode 100644 index 0000000000000000000000000000000000000000..328940bfea42dfee025e721362a85c607705155e GIT binary patch literal 30 ecmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>B>--2#;W literal 0 HcmV?d00001 diff --git a/tests/test51_3.tpl b/tests/test51_3.tpl new file mode 100644 index 0000000000000000000000000000000000000000..bb7446337b54794f192af444ef07396cd7d9a07a GIT binary patch literal 20 WcmZQzU|?ZjU| 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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 diff --git a/tests/test54_1.tpl b/tests/test54_1.tpl new file mode 100644 index 0000000000000000000000000000000000000000..b056e6b04d65a73da77c6e3306d6f87644ff05cc GIT binary patch literal 114 ycmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6NpR5)d&Erk_C4F literal 0 HcmV?d00001 diff --git a/tests/test54_2.tpl b/tests/test54_2.tpl new file mode 100644 index 0000000000000000000000000000000000000000..328940bfea42dfee025e721362a85c607705155e GIT binary patch literal 30 ecmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>B>--2#;W literal 0 HcmV?d00001 diff --git a/tests/test54_3.tpl b/tests/test54_3.tpl new file mode 100644 index 0000000000000000000000000000000000000000..bb7446337b54794f192af444ef07396cd7d9a07a GIT binary patch literal 20 WcmZQzU|?ZjU| +#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 0000000000000000000000000000000000000000..a811f623c7fedbaa51321b3789b96174f6ca0d44 GIT binary patch literal 57 ucmXRZ$YHQ#U|?|6$kb%u0y02=5r~<9m>Gy!fS47C*?^cGh&h0m6Nmwba00vl literal 0 HcmV?d00001 diff --git a/tests/test55_1.tpl b/tests/test55_1.tpl new file mode 100644 index 0000000000000000000000000000000000000000..85ae076aa787b733208636b51b59a603f8bb37bb GIT binary patch literal 4017 zcmW;OQ_vWQ4hGP_wr$(CZQHhO+qP}nwrzX2-L1L3r#F-H&EzGoN!N~T12#nn2q=`Y zRjPoOK?43?fn;F*j6n%Ta6%B0P=qE7VF^cg>lxi$tXrMhOvxeJQJA6 zBqlS3sZ3)!GnmONW;2Jm%ws+aSjZw4vxKEAV>v5W$tqT}hPA9?Jsa4_CN{H$t!!gE zJJ`uCcC&}Q>|;L%ILILmbA+QD<2WZc$tg~AhO?aGJQujgB`$M?t6bwcH@L|yZgYpb z+~YnEc*r9j^Mt27<2f&Q$tzy-hQIimx4h#Y{^dO%_{b+d^M$W`<2yh2$uIunHvxe( zfeAuTf)Sh$gd`N92}4-I5uOM{BodK{LR6v=ofyO<7O{y#T;dU*1SBL8iAh3Il98Mg zq$CxoNkdxFk)8}>Bomp*LRPYoogCyO7rDtpUhrl%y1;DMMMx zQJxA^q!N{>LRG3!of_1n7PYBEUFuPv1~jA*jcGztn$esVw4@cSX+vAu(Vh-;q!XR# zLRY%cogVb07rp62U;5FX0SsgigBik5hB2HGjARs}8N*n{F`fxbWD=8^!c?X)of*tz z7PFbdT;?&K1uSF{i&?@_ma&`_tYj6dS;Jb^v7QZVWD}d&!dAAiogM6C7rWWRUiPt{ z103WKhdIJgj&Yn5oa7XzIm21bah?lYUG8z82R!5vk9opV zp7ER)yyO+HdBb1)&0F5_5C8I>4}9bkpZUU9zVV$O{Nxw^@tZ$~KM;WlLQsMcoDhU0 z6rl-2Si%vW2t*_jk%>Z7q7j`K#3UB6i9=lC5uXGkBoT>8LQ;~EoD`%a6{$%>TGEl8 z3}hq|naM&{vXPw}F`or2 zWD$#5!cvy8oE5BO6{}gpTGp|i4Qyl+o7uuvwy~WZ>|__a*~4D;v7ZARh2uUbH6Na#aBRmm^ zNF*W?g{VX$Ix&bzEMgOfxWpqq2}npH5|f0aBqKQ~NJ%PElZLdUBRv_&NG39qg{)*F zJ2}WnE^?EHyyPQ41t>@%3R8rl6r(sLC`l}a>$Rs8+g{e$qIy0EbEM_x@xy)le3s}e^7PEw-V?7(#$R;+kg{^F3J3H9PE_Snrz3gK@2RO(f4s(Q~9OF1AILRqabB42= p<2)C*$R#dwg{xfSIybn;fX**A`zJ=L?s&j1Y!`ASi~j{afwHK5|EHYBqj+-Nk(!~ zkdjoSCJkvxM|v_4Ohz)1nJi=_8`;T0PI8f(Jme)G`6)m_3Q?FM6r~u&DM3j}QJON8 zr5xp{Kt(E1nJQGJ8r7*mO=?k_I@F~e^=Uvu8qt_0G^H8MX+cX`(V8~2r5)|*Ku0>! znJ#pt8{O$aPkPatKJ=v@{TaYO1~Hf+3}qO@8NoeG#AU83dBtnq@RoPH=K~-4#Am+nmGAhTANY}<_?ch$mEZWCKlqcs_?v(DMj%KC zMQFkhmT-h80uhNsWTFt2XhbIlF^NTN;t-d3#3um>Nkn3jkd$O3Cj}`P^DMC?-QJfN#q!gtoLs`mEo(fc?5|yb! zRjN^)8q}l~wW&j0>QSEtG^7!YX+l$)(VP~vq!q1cLtEO>o(^=R6P@WoSGv)i9`vLa zz3D?=`q7^O3}g_48NyJ8F`N;MWE7(r!&t^Ko(W835|f$2RHiYV8O&rBvzfzO<}sfI zEMyUjS;A75v78mGWEHDf!&=s{o(*hd6Pww>R<^O75O%PWUF>ELd)dc+4seh|9Oei| zImU5LaFSD;<_u>!$9XPrkxN|W3Rk(tb#8EzTioUjce%%X9`KMyJmv{cdB$^I@RC=& V<_&Lo$9q2TkxzW)3txi*{{l=6^ltzF literal 0 HcmV?d00001 diff --git a/tests/test55_3.tpl b/tests/test55_3.tpl new file mode 100644 index 0000000000000000000000000000000000000000..bb209fcd20380c224dfc2e9e2faa5d0d7bb5c287 GIT binary patch literal 2017 zcmWO5LjoKK006M5ZQHhO+qP}nwr$(CZQJd7Yb%F5A%WLH0)aQYy8VCd-D8UF$2tpEy(1al@;RsIzA`*$nL?J5Ch)xV*5{uZxAujQVPXZE> zh{PlzDalAq3R04a)TALT=}1ooGLnhRWFafr$W9J&l8fBrAusvJPXP*2h{6=1D8(pF z2})9m(v+brs7?)PQj6Nup)U2PPXij#h{iObDa~k33tG~O*0iB5 z?PyO2I?{>GbfGKV=uQuM(u>~op)dXD&j1E8h`|hDD8m@e2u3oB(Trg%;~38bCNhc1 zOkpb1n9dAlGK<;FVJ`ES&jJ>*h{Y^nDa%;S3Rbd;)vRGH>sZeQHnNG$Y+)*>T;VF$xXul3a*NyC;V$>M z&jTLvh{rtPDbIM$3tsYyKlqcs_?v(Dm;d;m*Sz5^?|9D#KJtmreBmqK_|6Z0@{2$) z{|QPkf)j#}gd#Ly2unD^6M=|CA~I2kN;IMqgP6o3HgSkcJmQmpgd`#{Nk~dEl9Pgz zq#`wGNJ~1>lYxw6A~RXYN;a~SgPi0dH+jfQKJrt5f)t`KMJP%!ic^A;l%h0cC`&oY zQ-O+9qB2#eN;RregPPQ$Hg%{=J?hhdhBTrvO=wCpn$v=ow4ya_XiGcV(}9k3qBC9S zN;kUGgP!!FH+|?!Kl(F(fed0WLm0|1hBJbZjAArn7|S@uGl7XrVlq>h$~2}kgPF`? zHglNEJm#~2g)Cw*OIXS>ma~GDtYS55Sj#%rvw@9lVl!LV$~LyMgPrVRH+$I2KK65f zgB;>8M>xtcj&p*OoZ>WRILkTCbAgLo;xbpb$~CTYgPYvqHg~woJ?`^>hdkmjPk72R Kp7Vm2yy7>;S^RAP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d928efa399903c22fe4ecff4dd6d8ccf129971a1 GIT binary patch literal 57 ucmXRZ$YEq)V6b%5$kYVV3|t@r2ta~NK+FupEI`Z(#B4y!4#XTl%n1O8Rsy{M literal 0 HcmV?d00001 diff --git a/tests/test61_1.tpl b/tests/test61_1.tpl new file mode 100644 index 0000000000000000000000000000000000000000..0ee24de064d4c34defe08ae32c6799160496aaff GIT binary patch literal 57 tcmXRZ$YEq)V6b%5$kYVV3|vsm4WxO1m=}oofS4bM1%Ox(h=qVy7yypK10eta literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d928efa399903c22fe4ecff4dd6d8ccf129971a1 GIT binary patch literal 57 ucmXRZ$YEq)V6b%5$kYVV3|t@r2ta~NK+FupEI`Z(#B4y!4#XTl%n1O8Rsy{M literal 0 HcmV?d00001 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-Wh}D>Z#D5_82gHAY_zw{O z2I5~p{1b?O0P%Mq{szRvsQC(1N5~wInlC_Hb@mJhys@7(69it`r-8vUdt)$oY!7rB z5InF4IkF-JNSpv-9iXo!0PzMs!g0pj04 z{0oSG0`U(Z{tm?7fS4FHUxDffnFCVurON)z84x&Y|8gb>%(8!$1_t(zjlsbF0nm96 zz+3^OQ-Jsc&{sM@JOPNmFfcGh0P!B6Clo4ylGA|r7f>P&hz|iJfZk*30^)Z-1wmE# JbORN%003Eaoz(yU literal 0 HcmV?d00001 diff --git a/tests/test85.ans b/tests/test85.ans new file mode 100644 index 0000000..7d8448a --- /dev/null +++ b/tests/test85.ans @@ -0,0 +1 @@ +cA(i#)S(cf#)A(ci#) diff --git a/tests/test85.c b/tests/test85.c new file mode 100755 index 0000000..4a0e12c --- /dev/null +++ b/tests/test85.c @@ -0,0 +1,13 @@ +#include +#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 0000000000000000000000000000000000000000..bb934e3e4cbf3aee43273e665d5a478dc46c7c0e GIT binary patch literal 308 zcmXRZ$YC&HWMD{k)W}rU4Aw|aQ`U6UNCvVQxEL51Sb>-Wh}D>Z#D5_82gHAY_zw{O z2I5~p{1b?O0P%Mq{szRvsQC(1N5~wInlC_Hb@mJhys@7(69it`r-8vUdt)$oY!7rB z5InF4IkF-JNSpv-9iXo!0Pz +#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 0000000000000000000000000000000000000000..d928efa399903c22fe4ecff4dd6d8ccf129971a1 GIT binary patch literal 57 ucmXRZ$YEq)V6b%5$kYVV3|t@r2ta~NK+FupEI`Z(#B4y!4#XTl%n1O8Rsy{M literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bb934e3e4cbf3aee43273e665d5a478dc46c7c0e GIT binary patch literal 308 zcmXRZ$YC&HWMD{k)W}rU4Aw|aQ`U6UNCvVQxEL51Sb>-Wh}D>Z#D5_82gHAY_zw{O z2I5~p{1b?O0P%Mq{szRvsQC(1N5~wInlC_Hb@mJhys@7(69it`r-8vUdt)$oY!7rB z5InF4IkF-JNSpv-9iXo!0Pz +#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"); +}