pax_global_header00006660000000000000000000000064123014401750014507gustar00rootroot0000000000000052 comment=5ac54fe7beab67cbfb6f95552c15a68204a014bd tpl-1.6.1/000077500000000000000000000000001230144017500123135ustar00rootroot00000000000000tpl-1.6.1/.gitignore000066400000000000000000000002131230144017500142770ustar00rootroot00000000000000# Object files *.o # Libraries *.lib *.a # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app tpl-1.6.1/LICENSE000077500000000000000000000021571230144017500133300ustar00rootroot00000000000000Copyright (c) 2005-2013, Troy Hanson http://troydhanson.github.com/tpl/ 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. tpl-1.6.1/Makefile.am000066400000000000000000000000641230144017500143470ustar00rootroot00000000000000SUBDIRS = src EXTRA_DIST = LICENSE tests lang doc tpl-1.6.1/README000066400000000000000000000026651230144017500132040ustar00rootroot00000000000000tpl: fast, easy serialization in C ============================================================================== Documentation for tpl is available in the doc/ directory or at: http://troydhanson.github.com/tpl 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, March 17 2013 tdh@tkhanson.net tpl-1.6.1/README.md000066400000000000000000000001161230144017500135700ustar00rootroot00000000000000 Documentation for tpl is available at: http://troydhanson.github.com/tpl/ tpl-1.6.1/bootstrap000077500000000000000000000003261230144017500142570ustar00rootroot00000000000000#!/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 tpl-1.6.1/configure.ac000066400000000000000000000013231230144017500146000ustar00rootroot00000000000000AC_PREREQ(2.59) AC_INIT([libtpl], [1.4], [tdh@tkhanson.net]) AC_CONFIG_SRCDIR(src/tpl.c) AC_CONFIG_AUX_DIR(config) AC_CONFIG_HEADERS(config/config.h) AM_INIT_AUTOMAKE([foreign]) 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 tpl-1.6.1/doc/000077500000000000000000000000001230144017500130605ustar00rootroot00000000000000tpl-1.6.1/doc/ChangeLog.html000066400000000000000000000537551230144017500156140ustar00rootroot00000000000000 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 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


tpl-1.6.1/doc/ChangeLog.txt000066400000000000000000000061741230144017500154600ustar00rootroot00000000000000tpl 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: tpl-1.6.1/doc/Makefile000066400000000000000000000004541230144017500145230ustar00rootroot00000000000000HTML=$(patsubst %.txt,%.html,$(wildcard *.txt)) all: $(HTML) # when each target of a multi-target rule has its own prereq # we use a static pattern rule. $(HTML): %.html: %.txt asciidoc -a toc2 $< TMP=/tmp/tpl-gh-pages stage: mkdir -p ${TMP} rm -if ${TMP}/* cp *.html *.css *.png ${TMP} tpl-1.6.1/doc/banner.png000066400000000000000000000571011230144017500150370ustar00rootroot00000000000000PNG  IHDRQDbKGD pHYs  tIMEA IDATxwևߞY% (r WQiW159bBtQJXD*Qr9oQ53=yfyJ}Sф8l pB: bºk*ьc!9]+uEV8.4{oOt+6o}#("g&A52uLC w{ohԱ{?~Nw[w0҆Lkh}DbDqq[4uyyW}!/['~ د.s7]|`JFK{jQG (EK\R2wO$f`wy3u|ⷯ8G`xfs6PD}%[~O~o /ǂB{R޾|y>惟B44u=۷Om+ul ??c}W)>^}=CQRB_֌#c"S[*F1/ D呍:~Wmj=w- O^3T}|Cd6|oaG~᥮^-.4tXBGn7x}PYrS`Ϝ-?\a;~'tc#Lڍہt +^>Q@/^?j:9ddd}&.Xa'`P9%HNR$tfHz4[>`. 'X_L; gxd+&JvK'wZsRlߜ;1%:J o3̫iɣC[je vE~q&AEDpd$&"@;)Infjk3npyeL0G[U+.Kbl8Esv3pe0'r%S\RP%w "hY2-$?!r%:͒<32DR2k"WQ`dTIn4&ÿ~B|; mȃhrU !p.\X`=ޕL}0qm2zG78RO#`Yv'v(.M#s$ر CTܷOA֗F p-A|4Pm,qd؅R|ۆ.z}DP"3ZC% ]u)I8@Fb"ԃ%rޱb&Ʃ l?.:.3ǫU7̸q.=.g_~}F҈J=y1N=[v{tJZ0S[qtJکRuf}>/HW82?Purd]CL~O ]\mMO ; Bw8Mn;lWcGV@J_QLӚ+H'=z[|FqQtt7˯hL|KQh߭?,1_ (#<3g0D ibh0 Џ[LyԔ~5ӏ| Ò;p{]1?41-V·AG#\dD+%ΖMQ^wkVRGiWϞMO-@C7]\*亻[w#t[]|} &7~H2kA`ޣŋc.E2/DLt1Δ~ӹ Et-OݼwмA5re]t7\F}LnImG~ȹ>ʼnwƼ 9yYjˡͰ6V zn !NhTOuZKr93J?#;{'PmgTY$ڀ] 3+w 3Xv~5bEn.J|P(4*ʿ#*(d C QHNJǶuFDr'33Bv-~L-}xϙev6]n9xyT倷;s`^j唪#1!u0\:{u_yB)֣f}ٵ=wxzT4tq5ː3hv#qz7IWJmQ#qmrKo'?~OFܚ̞(v,j$ӝ?=Mfَ}Kprۯ6ّW)]E;ʾש?l,9;7׸;&WbHSPw{ndh#D5?yB3\2 !=:E)ɼx'Az-8#CƎ%ᾯM _Z3sFEz[ߎ#w ߆ȫZDF=qOjY떲ad]ߕ{{"݂.>V>xeIRH}y"؉Şv5C{xCd$/ۘD2&evKGM4>*{>/5),L[M[7u<+ZTxT G /XP+̜^!҄]g=9MdN`^ƀ9y=Oz?arf+w6|ds#"Ĺ[[E.yWifo5`/dP2f).If S"+}7gҁR^Ŵ_\O eDtQ2uQ֘_ms!3m*ׅY/uGz'.2P Kf!Mٛ!01L^< Z 2 Zfl_{xүo2+C$d sd@衅cr|PP&4, !7BۖF vP4ld= 0u[ ;/=I6l4=!l?6:\ڝ"ݘ9Lx0fCCT&`#Z* ?Ej)^f22,F{|#6TgqГ.bh/-C /Jmh*>;gT $e{ P@g=C3KR뀉J}cn/ CɭTvх$jpKٵ{ÂGؑ]S:]c>`yxr7O&|je&Cn\,W[Nnq%M%Pk'vm7Ej/wtD4-|Eu9#dC‟IvI6 *a bm)_ ZxbܡFe8m*}mx%vKW_ Fҡ[bw|+E-tOF3ovƛA|L\"^3_yZsE7$)ܜewH^n yYY̚>T8f @GuU]bt `/Yvy9*l`vjKbv-Ǻ?9'f}Nl\@1s^I\$B,9t"s9˜.BLbGS}`NwHk|C])߈f^s- I~h5,Xp0g ţmy8jʘLG{s$Bv{F}; O~h_L<Zn>]WCvL~'U_xɑҏ3(ô'ěrn.y?P^ AwdVqsdCl(`x|3W'⡀1PdV7,7ȡ#hƁe1CLۿyX]?w&!y:_z6=+wQSߐ_~m&fՒffee?|`ol'6\7`bk!Ro-ٟ׀;Ql ?ypw繥<6/ѻoDf"~<}S w{j"cNz_lv]J\vL9w߬H-2osGL5Uױ/Eݿ9wGTM}Q\̶Cx|sT0Ԑ/Y>\N vrGpsCtq)Q|.5Y2"c]45kw?FxRq=K_#|<1)_QZK`Ϸ,7je Bu>/sr @ >&\$vg51S̸[JTUÄqMxah u= SY2SE^/1`,ͫ)m,Yd KݸcRb WtT/,L/cKՅe߈.ꕲT\}E<Rݮ_@uϻ {Q?Pڬʼbwr5|~6@zV} t>lZO+ fd*] œ]uS0U +LcetE Ϝ]h,2V#L%9}E ,ypEQbӗ[`\bΙiϛVk‚Dc֟hL^qE @أ6Uw241H˩|3Я bpߔROE 7th)]ɜ3ӊP*CU6I D5eTCHNעm`/C\>nw,d B]T>U- hrؼ0ĠM|γ Ř%2u&ovD湇̴Mer67j739 Q C`1̴95h/b o̓fñ[is wb+XdBseY8wq8`ɡ<kPu]ߎrHxuX`^uRfNƀML{;xio`[m#SvE't­xz~.FTkx_n_.~tKt?X-u'HejjeP%I(nE]@CwowGtaSQ{v-ջ׃"Rt1Cm>ξ]u+à ]* Ou0S9>BOhuqD6SڷJJ7# 7> IDAT]lq3dƄcn}%.>q뮋Ytf&ȞyÒ]9q2)c~@1 3;9|kn-Q)_sB? |%H1cnfC Nj; ٙ^~zx|L 4PyǙQCSz` s dp{@6KTHhX1~*_$qcr-y%U*VIsM6/^&;:8Og|OhQ\O^zS7D|d8!PS}+'9#93-yK)o׻o/ΠүL ZXnWwE؍ܖ~ܔ ?!ExH3x]CNYl>c9#׮@{Uލ&!Qbΐ mPu!ʦx)1]*tW%O ģ:TQء U i.֨:6.JjO:A'#u `t>_fy{z_ntBktH zpŷ;̴~a&5ۣeFchchs75@ m["ӡ&pLujCw]jK_#L`Ce܃}F^w([=ݼj-;v{j_c̲N[_NXU^ڬ"L/ae/DB$lkUtqyGN2g-"ΔTFF3]z7  #&=/F #|5y^0kUbtѳ{ÍD0QdtV8 ]#%D5Ctu{hq()HRg ж  /kr {)Ous1p>x-5>2sak㵎7U(G{B?79o(yf]W8aŁy7E{7Fr8Hݳ)ēZmUB3Ȳ; e]e̸-r7ZZQw AV#PűpwG/O/NZX|ŗ2. N9roYS7fSJERoYJo&=g%qL Twhݪhgq,-6yo;/RZ}`hPݳew^a뉡5]eLꚆ99u-ܣg-0/{uԳ@zhP w; +.0#VӰVo?Pwv " C xCsSe~;tsO(Î-av";A҇$S1ula6G!Mﲼe/U2z])ƹ uԟ+K9*~? Ɣ_^iSN{֛H}wEu m K}4t.㝮Wm߃9Ou! @-yr6 :=_mơsΐm9Uv.O`{v(5Z*5}ʉMR 15\\_>>`ؼ-5!-4AI9,;̹lp[A1i?:-):̷fL^M{l7<ׯ^֬:Sbx"s9׆ uPQ{VG?lQUjd TkڃM`"V{5,Xp1Jケ{wrv̾ ':\>\tM[3˂ Bņ7B߶;4Q][xl{HMT"`=՚dipr_~:BT>IL}:f sP3w R!n Wc[rNbk&hԠK~`wf Z:oٽoP?w\ K?#3{r6_Vt)[ߗ#Omf =2GiL7QWƮ#ԻQR<|:кNϿ˜)|T _@T{.x~6y__CZr$=3ޏgl|&:T@1vdȦnǨ0\`vUYe̹VnCۤsov Kʡk΅E$m9D-/N_pE/h5rF3tpgL|d_WsmW#gYfK<jtBBl6rj,3j7.-".Ox~SWEw=DZ0 xxouܒa<?2pv~}u?՟;@9H՝:cܗH@2Xv} p=R_1 䚵pU> zEޘV\#{;'G?|Tew͜6&y&Ra% Gl5`;>rjFsS/qձLy'uy(Q 9Y&{m[T,!ETJhE}GmԒו'MZ~:jDg鿆tJi`?b+B=og*~vݙ8ϓ/H+ PS9lR$2tE_Fpjc:-#eIo+gc1gM '$qėaS]l~9sJ99?35O2˦gC6/K+*Ѧ+{謬`K~6 $*&Մ@+MuMbGz gLD;]vZ ixVKΐ^rdv9-Tjסjzg@S/Q.,vֹ Q,. |e0&ٍA73TLpשTƜ3{|RNQ'zm$yt=M|í.y'0NF}ҐQͨ#=xȻI[HӸb/!͘\V3CYFt/g; i ]BsxA7w9]no$Zۓ~fT,f[XTPՌ% ]\nB0g'.KևrrmƓ˹N8LrdZKIޏM ̜%~ ǚ?ј'WeQP7(M՝ M Z~ȢT1wa,T sv2IGIN_n™%@{`(b♳+ @zYcF8 0I,Xs([C:QJgق#.IN/Hb ,+sfZ-ᨻ,)$oa ,Msf@t r Yz5 37ZӏX`!81.b,"B,VX[h[u#[T`(Ck]m#j8­]&38p+-Y ] E ,!_ij!93S&sҲ#Id)bq(^EҮF6d5C>=[16Ιi#O;ʮrѵ'w`g7FJL^*8ׁ?CEXD QZ!mWw$eQ(|`=A1ơ.^Q__df({һw-CS!JH[-Pc_>G7y6Άv]\}gt1TRmqj>ntTY%HUQ)8w,GWK-vRӽ9e"+!zA`;;ʢNPFأ G}r@ ~ *;XEkvR {FAkXCpy>nUWϠO}P@ӠԞun.֗m )] b-UN3=M?}UjBx;"y黾1@t@p#8ޕJxx ]<%$_-HWY/Ip.v)sinuTj O UpFk03:ŕ~}I@]݊.>S*}yh >vQũ]qgM4 م1\5Kh6#9ĭ oSq!hH ?O^@,mst!y]f #LբNPmt0Y]]'8D{L6o&&-SAv?ڕ>>0 &Wf 6 \\u2 J^`]TOTB|8Mx1Q?Кh;´p܋|Ih>.ervjG93ͬp^<z{_bq^7X/Eͼ0l1Y^S"~?e>J3SCk.v_C+5W]l3iq_kiP9|t/2SqSt _sҮ؎g4{3J5 "A`~wm_ىiLljkݮɃx2eXu`h!/P Yj54[x] 05C}b;!E*Yh/ NJG -CaHVw5kNtyEZnl&qىVQtwU:}`Y5t= fE. \<)>OJILAr:^aY>f[h6*e0/*ȭWw!X!\~]r.r0:CK4iZO8l9KfN)vWbD<dwf*H(~0B-N ~L ^;]QRJ0NCo7?I%hgi9@9ٗf!|sHsHMspI(. b ~.3n73  fIDAT!B-ٞsqY/a 8{j7&N }9]2ӦoQW<ƱLBkCZ7՟YPZx 0 AAn`MjYO"S14.w+-hɡO6Mwxqy*?βdz&jt1=@KOaC. f4pePos;Q2xRƾE`hUScҋ HkJi`Ys<v>BBqa>OG:I#yj5c8v/x"^ Xs~|[PІVGߜCœe,JDiu6xY]oFժ122]u* C3jk{s=UHk@ʆ2!w*}>2uW?/XV|ig4hTFJxb  Js#l;د4"ȹZݣǃ`),Vc37#ҭ%Y}\sآvnQ7ui 'UZ`5GCn33 ^Sׇ@߿ju] qu%6iu&jK±yʹ`bhu;z92(y7C"%N*.~Z. }ΩܦC55th. zhWuot.\P'>ȇ9/>Ϭbwi~Rp,>ɷEE!5 N 5ڂG 0~RijwÁfɐK,on|"l87[ʍC{зgkO糿 ""K]+Hٝ'XWTK]ي>Nhs/I$o(c\`hk]Lbs n  (EYwܿCٲ7IձPX%l>6pǃKRz( y y)sдFR̤JDÖ%b]sSW>q)"uNM[Òo.7Ka٪m\5b3T^O/%k5^OO{Xo BE e,,ېAG5x-Xp !c[V+{0Ἲ˗ A[~z?莻,Lb "V z s.# B2d7tQl^g,0r_\z?vbb0DXy3ͫc˩6Ahl6%88دD* f'qzA}Nۖ 3d@ ((hЪhrK]P׾H̫F\;YRQlB lY :3fr,3%pϓsϜ=[:FQTЛ cqڵxw~v5e>rQUٛr=O!!濏SU@|t Eq3=")wM.+H;Lk)X0Q ^!UHQ:3}D;JОkK7m8.6!n&(h |))Ql^ѴGnf<:AE6|']Rsr+OA>ŁOUexcppŗ5-ߐ[pciaB@O6{fVώ'qCA 'kbS-&3=Ǥ %} }n'Z^oLȸjPg/`+녇ƨv_x Ć ^`8ds> oG .R#CF'  N!E101WzWlj.<mm׻O'=n+⯛eO7f#Ze>آ?HK^; 9x>:px牵24x pbl+.<=T{#kpQ> ؈SWa^O]exA1.&w!C&hOV꾫mOI$pD[Ww8$ĸH VguL}6  QW{rGرX@fs߄|cҿ--߀L>_P& sb ݍ*NifF2 VT< PTϛ)w!AQhаE+L|Թ8ǐZ0=M.nFP:9: I%kR XG@3& UTtu:z T%Z[*n3yS`e4e Rm < <1M XE|ȯ=L[GA&\ؐ4v<Rcp(|s(p"PM㥝,:!vK^L 58vIH?37x'8[ R8+f=6Ͻ;n'ZhTlxpŽټ{j{.v.D?*AV3&ЫFw-g[$dR.&peSs(926;Je.u':#n[q{q`mnǜs)z%ٹOdxx?^6O)U{[W2s!4*cc[3d2&~{mc7s :)kkN/pʤX89b@:(TEؙfk0UcQb`ɓ玐3d4^$_*>.`ɓ te!]vnaH\#E*R/:lVF**rNX&=JY0}mn[Pųa; UL7cQ^"g]@_w3g6V>\#47=G|`7x7 Z3«'T;ϋ3 ]R>@*ܓ^Ylğ׿ }gbڋDq+Ϥ _t Mg\I #zimNb-+~]9ͬޮ.S]B$jY4N]L`YebR҈-w9o ]ã@]~(}Mn;NOi$Dx`^MKM'%2U,m$ہ::x?)jo«Ȃˀ'w)b^+@v` T(!J4'(0/Â4&i>)1ûTTQ8HIyeRuIZM43OW +#ΈFf)~҃$#Gmh萤m R[/4䍍єT.&`}{N,q=ݐxk|Vb8zpҳM]N|F |((ڴ;*O#9f?}ʈ0Shy}xrcl-;3mZ. ?}G>x7&Dcհ.޸Du9cΎ a!:G wmz@^N,4 5%D0r* !j4ʄpVb tMo)j XV+T'[t8s|_oU<hT,>H7C)9#T23U\]x) / &SJ=Nk':bR)^KB.?Q.5S;(Dě y"Κ2 9]a_Q[ؿ;[B%ytI13bW~}%uڴ>?KKx+Q?1^+lr/4MAJo2F=1י5#gXP[ D}Xj*֛j{{0`'B"41fyr~ܞ$3Pnܰv=QagK L3>w.2X cBuXb|Pi-t31RZJO sGzj}ڰғa|5L1Ѧfǝu-$d9̗OaF$΍I0Mu=l(˚dos6ltp0.Hm!e- ;8VhكeÆc87&IHe6lUYp8N./{lذq`" S;6Czy w6lذa}^@άIENDB`tpl-1.6.1/doc/banner.svg000066400000000000000000000422641230144017500150560ustar00rootroot00000000000000 image/svg+xml tpl easily store and retrieve binary data in C tpl-1.6.1/doc/compiling.html000066400000000000000000000451711230144017500157370ustar00rootroot00000000000000

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.


tpl-1.6.1/doc/compiling.txt000066400000000000000000000022251230144017500156030ustar00rootroot00000000000000Compiling 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`. tpl-1.6.1/doc/examples.html000066400000000000000000000614221230144017500155710ustar00rootroot00000000000000 tpl examples

Examples

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 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 <stdio.h>
#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 <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);
}

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 <stdio.h>
    #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 <stdio.h>
#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 <sys/time.h>

    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.


tpl-1.6.1/doc/examples.txt000066400000000000000000000155141230144017500154450ustar00rootroot00000000000000tpl examples ============ Troy D. Hanson v1.0, October 2006 Examples -------- 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`. tpl-1.6.1/doc/grad_azure.png000066400000000000000000000006311230144017500157110ustar00rootroot00000000000000PNG  IHDR ˨RbKGD pHYs  tIME36N&IDATXTA DTPkWSE oJȁKHw\~mԜhXN<ɪ w-Щ۵E#iϏY88ad߯ˍK[c/; svdQW#b˧Y image/svg+xml tpl-1.6.1/doc/index.html000066400000000000000000000103321230144017500150540ustar00rootroot00000000000000 tpl home page
GitHub 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.

tpl-1.6.1/doc/license.html000066400000000000000000000035121230144017500153710ustar00rootroot00000000000000 tpl: binary serialization for C
tpl home > BSD license

Copyright (c) 2005-2013, Troy D. Hanson  http://troydhanson.github.com/tpl/
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.

tpl-1.6.1/doc/perl.html000066400000000000000000000706151230144017500147210ustar00rootroot00000000000000 tpl Perl API

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.

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.


tpl-1.6.1/doc/perl.txt000066400000000000000000000205151230144017500145660ustar00rootroot00000000000000tpl Perl API ============ Troy D. Hanson v1.1, April 2007 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 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: tpl-1.6.1/doc/rss.png000066400000000000000000000012611230144017500143750ustar00rootroot00000000000000PNG  IHDRH-gAMA7tEXtSoftwareAdobe ImageReadyqe<CIDATxڌMHaδ1E"!HD*(PKtPnEХ:oAAHBRQ!%B]Y; ys4I@#֒Ps ZH sYLKDzzdR`TB ~;Ub# 4b^)uk)C2̝KhJlMlhim~&@eI25 K,M mGa<]ao% ޏ/q'Qʽbv[M)`q~fBYxaqd dg)BWM7H^lP@Qzz֦`H/7JeyJ%W\If m}XX\^+`_ǹ\,űX{+17j6+NDuZ(5]t6j.<&m+ARDž) ݬN GhȮ4ytPZ*k`r4M1MDC E2X$? GbIENDB`tpl-1.6.1/doc/styles.css000066400000000000000000000052061230144017500151200ustar00rootroot00000000000000#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 } .twitter-share-button { float: right; } .twitter-follow-button { padding: 5px; } #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; } tpl-1.6.1/doc/tpl-mini.png000066400000000000000000000060751230144017500153270ustar00rootroot00000000000000PNG  IHDRi#A$0bKGD pHYs  tIME  sy IDAThyt՝?Y=yߐ}%0#ZH]z(*gZxfF{:vE`֦R ф`M-Ͻ~nBJɰuj))@e>It A6;.$oJ)Y%YEIJE_< C*c^ K!@ύUMIS>DI0*RRN#*H)a *jN'?]"d\_.J=ISB_M>r'Ѻ "vLNm4kz0@ĉ[t/946Ƙ2K_>/׽~x&eLvnx<ż(~{|TUK1_=kY `p%FDxYҊEԜ[YkC#^a!K`iO։H`-27 10r5. i nX|)r{KeOUH7<zUxYjxV^ZB,IUЬ} Put"{ۈWq+sr}>J"KWgƍGNbTAF:#CJ8bE[;GGH})hLZ"".*{ҕCib2ϲdEKOH Z]E=O )1i xʜ+XML۩Emx*j=%fc.$WO۽hk MEE˟u=;+Oy rX֌aG@'NJ M1=T"HHP{*Y?.O(y+q0]%iGLI]CK&ɱt}of=h9soŖײ :F) E{hͣ拔4QD_Tְ/t_긢ӔzQrc>+SQ PKhX B0}R U_U _#`eeĭVB<ٍ]yG^;~qiE c-XLnq($&lk<^n}1¯N w;%{_8kѷN~v[U<1`fkXjĥK<ȸ-E`T;z+->` sp#nZieg+&>PL"{(XZ_˼r넒iuO&Ib尜sx;O4 aˠĠ'@̝NRsVMֈ6u*IsۆcJo0cIdv>)U1 'g yV 8hS*hH LЄp9)wâ(J.y;pHj8;ChS'ʇ47,cn U⯁McWDcͽ{ӱ]ф؎ɧ_t`,Ynڜ|/'G , XEV65!}a~CF$ ?lZC~~'D`X808HzTV;4dWD@b ț̻Bӯ;1Y|1_ag"_sh&_G~\kH@Oʵ'\,I{% [/Hht` C1dzɳD FmZ\0No(1/o}H ] ;`i3ƔǸYY Ah*]̘,*hÒ{`uz`ˢ)~wKh1@[>%&2%o],ykǷ.|E?pvEMWl9:Ϙ֮,φK E͏FBfghJIENDB`tpl-1.6.1/doc/tpl-mini.svg000066400000000000000000000275261230144017500153460ustar00rootroot00000000000000 image/svg+xml tpl tpl-1.6.1/doc/tpl.png000066400000000000000000000301501230144017500143640ustar00rootroot00000000000000PNG  IHDRWI;tY+iCCPICC ProfilexXwP;v{iJ/^T.E"K Aٰ U^EQ^ȗy/3;y䜜$'s'<<  p0%K{H^ 쬠)PDX>wC3+ܹjE  @7`0&vbH(~> &D89A|bM1G0trQMBG`7.B 4JsB #.'cLiƾOh[Q/Go9\9/7k=tͽA`76dܲXXX5Mjo ::q%>݁lmƹ̝wF@ZFXUQlxd#Vy-Z)nG[zu!~m%#;c YO6e532nޚF6.G'Vgk[-Ϲ=\{,}R}KnS@ }ԐЬjg#Ql"1t-l':&9ON1M58q&}utHNL?xZ#K8k-{8^ټ|Bɢ3q%gJˮ;RQXTpąًmr/ը\A]Z~v]v zM[:MgZ[tͺ*M8^=*kPX8_xZlOdK=!Jb^hXoy0s`՝:W4;10}Dyd]>~G?y—!ߌ~_n)M w]ص$,g]k+?W66=s7\+K6Kǜz\|PnQSm> Jg,%)4-̉6޺T']C~ +nOlLB#I|IoU%K!,6NFLPkΆ~GpJlxC?`P: N z'@#1Cj!GW("Pi4? Cˆc\1YgXf6NJ↘hL-n̗XY."X=Yhk6]*v<{ G:g Vbn~nxy? JUDE1Ubb[H[%%ofHHMIWo## /#$/$?pi{OT|TFJ ~Jjxu19˫'clA6j41a12 1v9k^cK렻Gƞ/})] m9OD$E F58.ŝM098t.!:֏?%-ƾhf&,S"s9GZVpx-|9gvVNf.V]]j->pNn&. 7w'ONuׅDDb-D QIȭ+Rۦefepy RDUE3%/R\*/TE44C;uuyFL+-l<[]<0qgҠ"})"uA>dh5#'bƉ,K&R?H;utwɞ 5<3gJJ W6V_p$yk1uW|9=[l_P#>BwD{7я|)0 4Z o!,o燇FZߕ&w#}~xx'g}_<~,{i 8l>qa»KKs?V+kk딟?6<̗~,FZLx_KhH~7|-ᯗ o2. _iQKC`%C1~j{>Ga!6V|Pļ&~#0ƐvSiFCM:P'+(%޿M'r> C!M-56$ޟ @?xх.h2Z M_m=[D:O{q˛kgEpTgB `3%^ t_zQIy9FA*ATB݋ΒzPK3Aeҫltk8_.w8 -Ӟ&@XgGX^^W%mukwkC۶X)uz²`{jܮE '=R棴˞;^b܋$%=rWCg޿}D|kC'>dWn)"%n3{x񇪺81!KWƋCoPA5%?8v|~뜗n4ݘ+kmo78mRD :2Obߊ n'Qn}>G`]VyCL}q _tҳ5 rappkɔս>U1aq!f.Jհ̏kGӽ912r`C5 ZQzШ }[?[d\s>1RΥn̑~.F|lHw_\VE>P{ *?վ{=4K懕Cz0i;bY@lftxH]8^Ak\ձ-j!kxw섿HvEE\v =ep;eQDղSBwM" l #~_y4/Ӯt픝H[:m -z6| n;:'iSnYܩ,wgy꼎4AO] gC>_qN6p/XsƄ=1N}گ}8 XO>_yco#ƒJ|2-&Ul6u >1ϐ!ssX."{&c&"~(ǭu֔„;N|dXaOXߗww#";X/]U>V KY;ҹe%=P s d`3u߀33b@8%,<@Bep]MIɁ6(p|0hq2vG*<)cF%dP͆˓ߝU:IEE( W%xGYz+Sg!mOUn._U&eB1: 8Eb 7چmҒ'_MvWH::ҩii{utI7/_۞4 LLB3"^nNT%^D.M^ iY''EDKdHzd۞9nVLYV;*8;L)sw߾Lx646dw .闺ƌVSJ;#WaV$/k}wY ZGO3L=,J@G-߼uwǴ̻⦬ EVz=Z CZyF0Gr&bw^kW+no%k.!RWn^|뺊QK&._& +A ÁO ΎBd \ Ω\U"?da#/|)f |iLܔ[J꼛:6?'I%YEW4+.k񽆸GLSuGZD*!Q$w{֋HV.ķH9"pP12> \i^'i|G^34}fr斢*6dGɺ]!pE{df\K$FDӁJ$>9 ,h+~H'UkC#i(X2H1Z[3"'i첖&Ɇ7w4"OF'gZA #J֝]ͩ?&pAt ,uςdμ8:;_ nڢn_;~Dt/_j/;B{)6s[=.Ȳ!onER, Ң׎Z*Ia%C";~-HaΔgi@k] knlʺ5Lڒ){!Y`==`8=UA`WI>9?!W偞W;QKIh x4Pc]0ė.weu߽j_)=搡y값up(Po1<Sela2f}=gݲE:qO0 &!V$ͻ#rX0O`{J$#gM er?MU;Q)fk' _NA6ldҮ@{C?~֢-]4%JMk8Md^"07Vb] :`X~ R !ER^|x`-,)GYMջ7 /wMw-B+ޒhwZMK~+tQG{vKP^)T`y{uF] }k;_)LaS^E31΍;x*҇ZxMg90wR2hc֯~.X<4\+ڼd/!"HNq֫b|R(jfCa \,x"YZɋ \P,YguW)edbx`**5Ȱ*M/ Z낥оZb .!b)n/8V{$5|QScAvT$r%0tan\ &<RZO Sc2gU铺yizi *Z7.&kt~o ns?z;- j)/bGY-[F$Z&ʛ%9NTߤKn%?§%!҃ooQpmBR}Z7 >=.h"=p`q% xfۜ j*ᣕ{a/ífk<XQGd11}RChOjwP ".")K+1e`CBIb aɩ($M poKo_[ z^ Qђa'Sa^xDQaf>->U{'U&if˒' r&DaSEfo)AN? M@wH6!mĉH@nưת}R{ b(W(DƤsEЯLO2 aϙX;VJR4&GA}H(ZoVÆdmuyBݴ}5kW̼ vGʂ /#}̛2} b  Qt&HDbQɿ;hhCᰴ6` @q({\d*ܲg 6sMYBBde)P5xL,ȂڔX)c<-`]3 *j;0]@na2;H`oeOɡV?=@80K ;)sԙ%{TƟ-1aq5ĕ~OybKf'0~[280c yoNJVgw!XESPLm{~]l 80x|q*O|^  Ea)/7"] pgƿ(ᤧ ls , L(S;A\X8&rP uOۚ\ iCe8+T!n& /ncږ٪pX! 3P6)ϦrI\9Y :7K^a,*AsSڝ5cGe@W`:'U`O\3|3K.'`%Lܕ񥂉MGG'6'4Y5@xŲo/:T?~ok8/~ N tpl User Guide

To download tpl, follow this link back to the GitHub project page.

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

You can clone tpl, or get a zipfile, from the GitHub project page.

Getting help

Please ask on Github if you need help. You can email the author at Troy D. Hanson <tdh@tkhanson.net>, but I am often behind on email by weeks or months. Sorry!

Contributing

If you add a new feature or fix something in tpl or in the extras, please make a pull request on Github. For anything other than a trivial change, include a unit test and documentation if you possibly can. (And don’t be discouraged if it takes weeks or even months for me to merge it. Sorry, my life is busy!) Thanks!

News

The author has a blog for software updates (RSS) . You can also follow @troydhanson on Twitter for updates.

Other software

Other open-source software by the author is listed at http://tkhanson.net.

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

(Note that, if configure is missing, generate it by running bootstrap.)

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

Compile-in

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.

DLL

If you like, you can build the DLL yourself using VS2008 or VS2010 (the free Express Edition is sufficient) and perhaps newer versions, though this has not been tested by the author. (As of 2013 we need to restore the solution file to make this easy, that’s currently missing).

MinGW/Cygwin

You can build it in the traditional Unix method under Cygwin/MinGW using the "configure; make; make install" approach. If the "configure" script is not present in the repository you cloned, generate it by running "bootstrap".

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 */
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.

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

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, &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.


tpl-1.6.1/doc/userguide.txt000066400000000000000000001407451230144017500156300ustar00rootroot00000000000000tpl User Guide ============== Troy D. Hanson v1.5, February 2010 To download tpl, follow this link back to the https://github.com/troydhanson/tpl[GitHub project page]. 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 <> 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 ~~~~~~~~ You can clone tpl, or get a zipfile, from the https://github.com/troydhanson/tpl[GitHub project page]. Getting help ~~~~~~~~~~~~ Please ask on Github if you need help. You can email the author at Troy D. Hanson , but I am often behind on email by weeks or months. Sorry! Contributing ~~~~~~~~~~~~ If you add a new feature or fix something in tpl or in the extras, please make a pull request on Github. For anything other than a trivial change, include a unit test and documentation if you possibly can. (And don't be discouraged if it takes weeks or even months for me to merge it. Sorry, my life is busy!) Thanks! News ~~~~ The author has a blog for http://troydhanson.wordpress.com/[software updates] image:rss.png[(RSS)]. You can also follow @troydhanson on Twitter for updates. Other software ^^^^^^^^^^^^^^ Other open-source software by the author is listed at http://tkhanson.net. 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 (Note that, if `configure` is missing, generate it by running `bootstrap`.) 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 ~~~~~~~~~~ Compile-in ^^^^^^^^^^ 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`. DLL ^^^ If you like, you can build the DLL yourself using VS2008 or VS2010 (the free Express Edition is sufficient) and perhaps newer versions, though this has not been tested by the author. (As of 2013 we need to restore the solution file to make this easy, that's currently missing). MinGW/Cygwin ^^^^^^^^^^^^ You can build it in the traditional Unix method under Cygwin/MinGW using the "configure; make; make install" approach. If the "configure" script is not present in the repository you cloned, generate it by running "bootstrap". 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: tpl-1.6.1/lang/000077500000000000000000000000001230144017500132345ustar00rootroot00000000000000tpl-1.6.1/lang/perl/000077500000000000000000000000001230144017500141765ustar00rootroot00000000000000tpl-1.6.1/lang/perl/Tpl.pm000066400000000000000000000443621230144017500153040ustar00rootroot00000000000000package Tpl; # Copyright (c) 2005-2013, Troy Hanson http://troydhanson.github.com/tpl/ # 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. 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 tpl-1.6.1/lang/perl/tests/000077500000000000000000000000001230144017500153405ustar00rootroot00000000000000tpl-1.6.1/lang/perl/tests/Makefile000066400000000000000000000001261230144017500167770ustar00rootroot00000000000000all: run_tests run_tests: perl ./do_tests .PHONY: clean clean: rm -f test*.out tpl-1.6.1/lang/perl/tests/README000066400000000000000000000025611230144017500162240ustar00rootroot00000000000000Run '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) tpl-1.6.1/lang/perl/tests/client.pl000077500000000000000000000012631230144017500171600ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/do_tests000077500000000000000000000005351230144017500171150ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/server.pl000077500000000000000000000023311230144017500172050ustar00rootroot00000000000000#!/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); tpl-1.6.1/lang/perl/tests/test1000077500000000000000000000005501230144017500163260ustar00rootroot00000000000000#!/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" } tpl-1.6.1/lang/perl/tests/test1.ans000066400000000000000000000000241230144017500170770ustar00rootroot000000000000000 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test10000077500000000000000000000004271230144017500164110ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test10.ans000066400000000000000000000015721230144017500171700ustar00rootroot00000000000000 ]> 0 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test11000077500000000000000000000005251230144017500164110ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test11.ans000066400000000000000000000000051230144017500171570ustar00rootroot00000000000000 tpl-1.6.1/lang/perl/tests/test12000077500000000000000000000005701230144017500164120ustar00rootroot00000000000000#!/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); } tpl-1.6.1/lang/perl/tests/test12.ans000066400000000000000000000002211230144017500171600ustar00rootroot000000000000000.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 tpl-1.6.1/lang/perl/tests/test13000077500000000000000000000004361230144017500164140ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test13.ans000066400000000000000000000022721230144017500171710ustar00rootroot00000000000000 ]> 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 tpl-1.6.1/lang/perl/tests/test14000077500000000000000000000003071230144017500164120ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test14.ans000066400000000000000000000000031230144017500171600ustar00rootroot00000000000000-2 tpl-1.6.1/lang/perl/tests/test14.tpl000066400000000000000000000000161230144017500172020ustar00rootroot00000000000000tplitpl-1.6.1/lang/perl/tests/test15000077500000000000000000000003071230144017500164130ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test15.ans000066400000000000000000000000031230144017500171610ustar00rootroot00000000000000-3 tpl-1.6.1/lang/perl/tests/test15.tpl000066400000000000000000000000161230144017500172030ustar00rootroot00000000000000tplitpl-1.6.1/lang/perl/tests/test16000077500000000000000000000004751230144017500164220ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test16.ans000066400000000000000000000000051230144017500171640ustar00rootroot00000000000000 tpl-1.6.1/lang/perl/tests/test17000077500000000000000000000005721230144017500164210ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test17.ans000066400000000000000000000000041230144017500171640ustar00rootroot000000000000001,a tpl-1.6.1/lang/perl/tests/test18000077500000000000000000000005211230144017500164140ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test18.ans000066400000000000000000000000061230144017500171670ustar00rootroot000000000000001 2 3 tpl-1.6.1/lang/perl/tests/test19000077500000000000000000000006261230144017500164230ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test19.ans000066400000000000000000000000221230144017500171660ustar00rootroot000000000000001 2 3 -1 -2 -3 -4 tpl-1.6.1/lang/perl/tests/test2000077500000000000000000000004621230144017500163310ustar00rootroot00000000000000#!/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" } tpl-1.6.1/lang/perl/tests/test2.ans000066400000000000000000000000241230144017500171000ustar00rootroot000000000000000 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test20000077500000000000000000000011451230144017500164100ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test20.ans000066400000000000000000000000241230144017500171600ustar00rootroot00000000000000tpl files identical tpl-1.6.1/lang/perl/tests/test21000077500000000000000000000003551230144017500164130ustar00rootroot00000000000000#!/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"); tpl-1.6.1/lang/perl/tests/test21.ans000066400000000000000000000000051230144017500171600ustar00rootroot00000000000000A(i) tpl-1.6.1/lang/perl/tests/test22000077500000000000000000000004621230144017500164130ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test22.ans000066400000000000000000000000051230144017500171610ustar00rootroot00000000000000i#i# tpl-1.6.1/lang/perl/tests/test23000077500000000000000000000006031230144017500164110ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test3000077500000000000000000000004271230144017500163330ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test3.ans000066400000000000000000000015721230144017500171120ustar00rootroot00000000000000 ]> 0 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test4000077500000000000000000000007541230144017500163370ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test4.ans000066400000000000000000000000241230144017500171020ustar00rootroot00000000000000tpl files identical tpl-1.6.1/lang/perl/tests/test5000077500000000000000000000005571230144017500163410ustar00rootroot00000000000000#!/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); } tpl-1.6.1/lang/perl/tests/test5.ans000066400000000000000000000000241230144017500171030ustar00rootroot000000000000000 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test6000077500000000000000000000004271230144017500163360ustar00rootroot00000000000000#!/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; tpl-1.6.1/lang/perl/tests/test6.ans000066400000000000000000000015721230144017500171150ustar00rootroot00000000000000 ]> 0 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tests/test7000077500000000000000000000007541230144017500163420ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test7.ans000066400000000000000000000000241230144017500171050ustar00rootroot00000000000000tpl files identical tpl-1.6.1/lang/perl/tests/test8000077500000000000000000000010301230144017500163270ustar00rootroot00000000000000#!/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"; tpl-1.6.1/lang/perl/tests/test8.ans000066400000000000000000000000241230144017500171060ustar00rootroot00000000000000tpl files identical tpl-1.6.1/lang/perl/tests/test9000077500000000000000000000005501230144017500163360ustar00rootroot00000000000000#!/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" } tpl-1.6.1/lang/perl/tests/test9.ans000066400000000000000000000000241230144017500171070ustar00rootroot000000000000000 1 2 3 4 5 6 7 8 9 tpl-1.6.1/lang/perl/tplfmt000077500000000000000000000007411230144017500154340ustar00rootroot00000000000000#!/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; } tpl-1.6.1/lang/perl/tplxml000077500000000000000000000253251230144017500154530ustar00rootroot00000000000000#!/usr/bin/perl # tplxml # by Troy Hanson 27 Feb 2006 # convert between tpl and XML # Copyright (c) 2005-2013, Troy Hanson http://troydhanson.github.com/tpl/ # 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. 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"; tpl-1.6.1/src/000077500000000000000000000000001230144017500131025ustar00rootroot00000000000000tpl-1.6.1/src/Makefile.am000066400000000000000000000002711230144017500151360ustar00rootroot00000000000000SUBDIRS = 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 tpl-1.6.1/src/tpl.c000066400000000000000000002712221230144017500140530ustar00rootroot00000000000000/* Copyright (c) 2005-2013, Troy D. Hanson http://troydhanson.github.com/tpl/ 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.6 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 #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #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; } tpl-1.6.1/src/tpl.h000066400000000000000000000106671230144017500140640ustar00rootroot00000000000000/* Copyright (c) 2005-2013, Troy D. Hanson http://troydhanson.github.com/tpl/ 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 */ tpl-1.6.1/src/win/000077500000000000000000000000001230144017500136775ustar00rootroot00000000000000tpl-1.6.1/src/win/Makefile.am000066400000000000000000000002011230144017500157240ustar00rootroot00000000000000noinst_LTLIBRARIES = libwinmmap.la noinst_HEADERS = mman.h libwinmmap_la_SOURCES = nonempty.c libwinmmap_la_LIBADD = @LTLIBOBJS@ tpl-1.6.1/src/win/README000066400000000000000000000005561230144017500145650ustar00rootroot00000000000000This 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 tpl-1.6.1/src/win/mman.h000066400000000000000000000033121230144017500147770ustar00rootroot00000000000000#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_ */ tpl-1.6.1/src/win/mmap.c000066400000000000000000000067331230144017500150060ustar00rootroot00000000000000#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; } tpl-1.6.1/src/win/nonempty.c000066400000000000000000000002761230144017500157210ustar00rootroot00000000000000/* 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; } tpl-1.6.1/tests/000077500000000000000000000000001230144017500134555ustar00rootroot00000000000000tpl-1.6.1/tests/Makefile000066400000000000000000000056501230144017500151230ustar00rootroot00000000000000# 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 test125 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 tpl-1.6.1/tests/Makefile.alt000066400000000000000000000013611230144017500156750ustar00rootroot00000000000000# 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 tpl-1.6.1/tests/Makefile.mingw000066400000000000000000000011751230144017500162410ustar00rootroot00000000000000# 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 tpl-1.6.1/tests/README000066400000000000000000000143461230144017500143450ustar00rootroot00000000000000Run "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 test125: test to manifest a free bug with S(s)# per Andrei Diaconu tpl-1.6.1/tests/dbx.sh000066400000000000000000000007121230144017500145660ustar00rootroot00000000000000#!/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; tpl-1.6.1/tests/do_tests.cygwin000077500000000000000000000006431230144017500165310ustar00rootroot00000000000000#!/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"; tpl-1.6.1/tests/int64_align.c000066400000000000000000000006031230144017500157360ustar00rootroot00000000000000#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"); } tpl-1.6.1/tests/malign.c000066400000000000000000000007401230144017500150710ustar00rootroot00000000000000#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"); } tpl-1.6.1/tests/mgwtest.c000066400000000000000000000021541230144017500153150ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/other/000077500000000000000000000000001230144017500145765ustar00rootroot00000000000000tpl-1.6.1/tests/other/Makefile000066400000000000000000000004131230144017500162340ustar00rootroot00000000000000SRCDIR=../../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 tpl-1.6.1/tests/other/README000066400000000000000000000001751230144017500154610ustar00rootroot00000000000000Assorted other tests that are not part of the standard test suite. other1: a C++ program using "this" as a structure pointer tpl-1.6.1/tests/other/do_tests000077500000000000000000000006471230144017500163570ustar00rootroot00000000000000#!/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; tpl-1.6.1/tests/other/other1.ans000066400000000000000000000000561230144017500165040ustar00rootroot00000000000000buffer: tpl i: 9999 c: this is a test string. tpl-1.6.1/tests/other/other1.cpp000066400000000000000000000013321230144017500165030ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/sizes000077500000000000000000000007701230144017500145440ustar00rootroot00000000000000#!/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. /; tpl-1.6.1/tests/test1-mingw.c000066400000000000000000000004221230144017500157760ustar00rootroot00000000000000#include #include "tpl.h" int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, "Hello", "Hello Demo", MB_OK); return (0); } tpl-1.6.1/tests/test1.ans000066400000000000000000000000071230144017500152150ustar00rootroot00000000000000j is 1 tpl-1.6.1/tests/test1.c000066400000000000000000000006271230144017500146660ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test10.ans000066400000000000000000000001061230144017500152750ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test10.c000066400000000000000000000003611230144017500147410ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test10.tpl000066400000000000000000000000711230144017500153140ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test100.ans000066400000000000000000000000201230144017500153500ustar00rootroot00000000000000fmt: S(ic) p: 1 tpl-1.6.1/tests/test100.c000066400000000000000000000006741230144017500150300ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test101.ans000066400000000000000000000000651230144017500153620ustar00rootroot00000000000000TPL_DATAPEEK format mismatches tpl iamge peek failed tpl-1.6.1/tests/test101.c000066400000000000000000000010241230144017500150170ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test102.ans000066400000000000000000000000141230144017500153550ustar00rootroot00000000000000fmt: c q: ! tpl-1.6.1/tests/test102.c000066400000000000000000000005771230144017500150340ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test103.ans000066400000000000000000000000711230144017500153610ustar00rootroot00000000000000fmt: S(iscsiu) pi: 1, ps: hello, pc: ^, pt: world, pi: 2 tpl-1.6.1/tests/test103.c000066400000000000000000000012421230144017500150230ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test104.ans000066400000000000000000000000701230144017500153610ustar00rootroot00000000000000fmt: S(iscsiu) pi: 1, ps: NULL, pc: ^, pt: world, pi: 2 tpl-1.6.1/tests/test104.c000066400000000000000000000012501230144017500150230ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test105.ans000066400000000000000000000000761230144017500153700ustar00rootroot00000000000000i: 1, s: hello, w: world, c: $ i: 1, s: hello, w: world, c: $ tpl-1.6.1/tests/test105.c000066400000000000000000000012321230144017500150240ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test106.ans000066400000000000000000000011521230144017500153650ustar00rootroot00000000000000testing 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 tpl-1.6.1/tests/test106.c000066400000000000000000000055541230144017500150400ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test108.ans000066400000000000000000000000671230144017500153730ustar00rootroot00000000000000structure matches original other fields match original tpl-1.6.1/tests/test108.c000066400000000000000000000025101230144017500150270ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test109.ans000066400000000000000000000000371230144017500153710ustar00rootroot00000000000000sizeof(s): 12 structures match tpl-1.6.1/tests/test109.c000066400000000000000000000012151230144017500150310ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test11.ans000066400000000000000000000000411230144017500152740ustar00rootroot00000000000000test11.tpl: not a valid tpl file tpl-1.6.1/tests/test11.c000066400000000000000000000004531230144017500147440ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test11.tpl000066400000000000000000000000711230144017500153150ustar00rootroot00000000000000tpl9A(z)  tpl-1.6.1/tests/test110.ans000066400000000000000000000000211230144017500153520ustar00rootroot00000000000000structures match tpl-1.6.1/tests/test110.c000066400000000000000000000047351230144017500150330ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test111.ans000066400000000000000000000000211230144017500153530ustar00rootroot00000000000000structures match tpl-1.6.1/tests/test111.c000066400000000000000000000015471230144017500150320ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test112.ans000066400000000000000000000000211230144017500153540ustar00rootroot00000000000000structures match tpl-1.6.1/tests/test112.c000066400000000000000000000016311230144017500150250ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test113.ans000066400000000000000000000000711230144017500153620ustar00rootroot00000000000000structures match A matches B matches C matches D matches tpl-1.6.1/tests/test113.c000066400000000000000000000023101230144017500150210ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test114.ans000066400000000000000000000000171230144017500153630ustar00rootroot00000000000000matrices match tpl-1.6.1/tests/test114.c000066400000000000000000000015001230144017500150220ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test119.ans000066400000000000000000000000151230144017500153660ustar00rootroot00000000000000size is 1726 tpl-1.6.1/tests/test119.c000066400000000000000000000012521230144017500150330ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test12.ans000066400000000000000000000000411230144017500152750ustar00rootroot00000000000000test12.tpl: not a valid tpl file tpl-1.6.1/tests/test12.c000066400000000000000000000004531230144017500147450ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test12.tpl000066400000000000000000000000711230144017500153160ustar00rootroot00000000000000tpl9A(i)iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiitpl-1.6.1/tests/test120.ans000066400000000000000000000002251230144017500153610ustar00rootroot00000000000000testing 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 tpl-1.6.1/tests/test120.c000066400000000000000000000012241230144017500150220ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test121.ans000066400000000000000000000000351230144017500153610ustar00rootroot00000000000000one two three eins zwei drei tpl-1.6.1/tests/test121.c000066400000000000000000000012211230144017500150200ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test122.ans000066400000000000000000000000231230144017500153570ustar00rootroot000000000000001 abc 3.140000 a 1 tpl-1.6.1/tests/test122.c000066400000000000000000000013721230144017500150300ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test123.ans000066400000000000000000000000161230144017500153620ustar00rootroot00000000000000caught error! tpl-1.6.1/tests/test123.c000066400000000000000000000012541230144017500150300ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test124.ans000066400000000000000000000000561230144017500153670ustar00rootroot00000000000000mapped freed abcdefghi first jklmnopqr second tpl-1.6.1/tests/test124.c000066400000000000000000000012301230144017500150230ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test125.c000066400000000000000000000015361230144017500150350ustar00rootroot00000000000000#include "tpl.h" struct Test { char *string; }; struct Test tests[] = {{"first"}, {"second"},{"third"}}; char buffer[5000]; /* this doesn't matter- just a place to dump to */ /* this test is useful under valgrind to detect an unfreed string bug */ int main(int argc, char *argv[]) { tpl_node *tn; tn = tpl_map("S(s)#", &tests, 3); tpl_pack(tn, 0); tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buffer, sizeof(buffer)); /* at the next line, when the tpl tree is freed, the string node * is followed by a pound node. The string node has actually had * its data member multipled by the pound node's factor, but we * don't know that, until after we freed the string node at tpl.c:710. * if you run this example under valgrind --leak-check=full you can * see 13 bytes lost which are "second" and "third" above */ tpl_free(tn); return 0; } tpl-1.6.1/tests/test13.ans000066400000000000000000000000411230144017500152760ustar00rootroot00000000000000test13.tpl: not a valid tpl file tpl-1.6.1/tests/test13.c000066400000000000000000000004531230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test13.tpl000066400000000000000000000000711230144017500153170ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test14.ans000066400000000000000000000000411230144017500152770ustar00rootroot00000000000000test14.tpl: not a valid tpl file tpl-1.6.1/tests/test14.c000066400000000000000000000004531230144017500147470ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test14.tpl000066400000000000000000000000711230144017500153200ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test15.ans000066400000000000000000000000411230144017500153000ustar00rootroot00000000000000test15.tpl: not a valid tpl file tpl-1.6.1/tests/test15.c000066400000000000000000000004531230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test15.tpl000066400000000000000000000000711230144017500153210ustar00rootroot00000000000000tpt9A(i)  tpl-1.6.1/tests/test16.ans000066400000000000000000000000411230144017500153010ustar00rootroot00000000000000test16.tpl: not a valid tpl file tpl-1.6.1/tests/test16.c000066400000000000000000000004531230144017500147510ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test16.tpl000066400000000000000000000000711230144017500153220ustar00rootroot00000000000000tpl:A(i)  tpl-1.6.1/tests/test17.ans000066400000000000000000000000461230144017500153070ustar00rootroot00000000000000test17.tpl: format signature mismatch tpl-1.6.1/tests/test17.c000066400000000000000000000004531230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test17.tpl000066400000000000000000000000711230144017500153230ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test18.ans000066400000000000000000000000431230144017500153050ustar00rootroot00000000000000failed to parse A(i tpl map failed tpl-1.6.1/tests/test18.c000066400000000000000000000003631230144017500147530ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test19.ans000066400000000000000000000000451230144017500153100ustar00rootroot00000000000000failed to parse A(i)) tpl map failed tpl-1.6.1/tests/test19.c000066400000000000000000000003711230144017500147530ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test2.ans000066400000000000000000000000071230144017500152160ustar00rootroot00000000000000j is 1 tpl-1.6.1/tests/test2.c000066400000000000000000000005531230144017500146650ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test20.ans000066400000000000000000000000441230144017500152770ustar00rootroot00000000000000failed to parse iA() tpl map failed tpl-1.6.1/tests/test20.c000066400000000000000000000003611230144017500147420ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test21.ans000066400000000000000000000000741230144017500153030ustar00rootroot00000000000000i,j are 0,1 i,j are 2,3 i,j are 4,5 i,j are 6,7 i,j are 8,9 tpl-1.6.1/tests/test21.c000066400000000000000000000006301230144017500147420ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test22.ans000066400000000000000000000001061230144017500153000ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test22.c000066400000000000000000000005761230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test23.ans000066400000000000000000000001061230144017500153010ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test23.c000066400000000000000000000011551230144017500147470ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test24.ans000066400000000000000000000001061230144017500153020ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test24.c000066400000000000000000000010271230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test24.tpl000066400000000000000000000000721230144017500153220ustar00rootroot00000000000000tpl9A(u)  tpl-1.6.1/tests/test25.ans000066400000000000000000000000411230144017500153010ustar00rootroot00000000000000test25.tpl: not a valid tpl file tpl-1.6.1/tests/test25.c000066400000000000000000000007751230144017500147600ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test25.tpl000066400000000000000000000000721230144017500153230ustar00rootroot00000000000000tpl9A(u)  tpl-1.6.1/tests/test26.ans000066400000000000000000000000461230144017500153070ustar00rootroot000000000000003 tpls gathered. 999045 is their sum. tpl-1.6.1/tests/test26.c000066400000000000000000000043421230144017500147530ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test26_0.tpl000066400000000000000000000000711230144017500155420ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test26_1.tpl000066400000000000000000000076611230144017500155570ustar00rootroot00000000000000tplA(i)  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test26_2.tpl000066400000000000000000000037201230144017500155500ustar00rootroot00000000000000tplA(i)  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test26_3.tpl000066400000000000000000000037411230144017500155540ustar00rootroot00000000000000      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test27.ans000066400000000000000000000001061230144017500153050ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test27.c000066400000000000000000000014361230144017500147550ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test28.ans000066400000000000000000000000201230144017500153010ustar00rootroot00000000000000sum is 49995000 tpl-1.6.1/tests/test28.c000066400000000000000000000014271230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test29.ans000066400000000000000000000000531230144017500153100ustar00rootroot00000000000000sum is 49995000 abcdefghijklmnopqrstuvwxyz tpl-1.6.1/tests/test29.c000066400000000000000000000021261230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test3.ans000066400000000000000000000001061230144017500152170ustar00rootroot00000000000000j 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 tpl-1.6.1/tests/test3.c000066400000000000000000000005741230144017500146710ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test30.ans000066400000000000000000000000261230144017500153000ustar00rootroot00000000000000buffer length: 4 good tpl-1.6.1/tests/test30.c000066400000000000000000000016451230144017500147510ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test31.ans000066400000000000000000000000221230144017500152750ustar00rootroot00000000000000buffer length: 0 tpl-1.6.1/tests/test31.c000066400000000000000000000016031230144017500147440ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test32.ans000066400000000000000000000001561230144017500153060ustar00rootroot00000000000000buffer length: 4 good buffer length: 4 ood buffer length: 4 od e buffer length: 4 d eg buffer length: 4 egg tpl-1.6.1/tests/test32.c000066400000000000000000000020451230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test33.ans000066400000000000000000000000421230144017500153010ustar00rootroot00000000000000sizeof(double) is 8 y is 1.000000 tpl-1.6.1/tests/test33.c000066400000000000000000000006551230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test34.ans000066400000000000000000000003301230144017500153020ustar00rootroot00000000000000sizeof(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 tpl-1.6.1/tests/test34.c000066400000000000000000000007211230144017500147470ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test35.ans000066400000000000000000000000001230144017500152750ustar00rootroot00000000000000tpl-1.6.1/tests/test35.c000066400000000000000000000005231230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test36.ans000066400000000000000000000000561230144017500153110ustar00rootroot00000000000000id 0, user joe id 1, user bob id 2, user cary tpl-1.6.1/tests/test36.c000066400000000000000000000005651230144017500147570ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test37.ans000066400000000000000000000000001230144017500152770ustar00rootroot00000000000000tpl-1.6.1/tests/test37.c000066400000000000000000000004641230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test38.ans000066400000000000000000000003631230144017500153140ustar00rootroot00000000000000unpacking 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 tpl-1.6.1/tests/test38.c000066400000000000000000000005761230144017500147630ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test39.ans000066400000000000000000000000651230144017500153140ustar00rootroot00000000000000test39.tpl: not a valid tpl file load failed (rc=-1) tpl-1.6.1/tests/test39.c000066400000000000000000000004751230144017500147620ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test39.tpl000066400000000000000000000000711230144017500153270ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test4.ans000066400000000000000000000001061230144017500152200ustar00rootroot00000000000000j 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 tpl-1.6.1/tests/test4.c000066400000000000000000000006521230144017500146670ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test40.ans000066400000000000000000000000001230144017500152710ustar00rootroot00000000000000tpl-1.6.1/tests/test40.c000066400000000000000000000004641230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test41.ans000066400000000000000000000000141230144017500152770ustar00rootroot00000000000000a b 1 2 3 tpl-1.6.1/tests/test41.c000066400000000000000000000004731230144017500147510ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test42.ans000066400000000000000000000000001230144017500152730ustar00rootroot00000000000000tpl-1.6.1/tests/test42.c000066400000000000000000000005361230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test43.ans000066400000000000000000000000001230144017500152740ustar00rootroot00000000000000tpl-1.6.1/tests/test43.c000066400000000000000000000006071230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test44.ans000066400000000000000000000000001230144017500152750ustar00rootroot00000000000000tpl-1.6.1/tests/test44.c000066400000000000000000000007121230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test45.ans000066400000000000000000000000341230144017500153050ustar00rootroot00000000000000x is 0.500000 y is 0.500000 tpl-1.6.1/tests/test45.c000066400000000000000000000006101230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test46.ans000066400000000000000000000000001230144017500152770ustar00rootroot00000000000000tpl-1.6.1/tests/test46.c000066400000000000000000000003551230144017500147550ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test47.ans000066400000000000000000000000001230144017500153000ustar00rootroot00000000000000tpl-1.6.1/tests/test47.c000066400000000000000000000003621230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test48.ans000066400000000000000000000000241230144017500153070ustar00rootroot000000000000000 1 2 3 4 5 6 7 8 9 tpl-1.6.1/tests/test48.c000066400000000000000000000004141230144017500147530ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test49.ans000066400000000000000000000000001230144017500153020ustar00rootroot00000000000000tpl-1.6.1/tests/test49.c000066400000000000000000000004621230144017500147570ustar00rootroot00000000000000 #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); } tpl-1.6.1/tests/test5.ans000066400000000000000000000013511230144017500152240ustar00rootroot00000000000000j 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 tpl-1.6.1/tests/test5.c000066400000000000000000000010371230144017500146660ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test50.ans000066400000000000000000000000121230144017500152750ustar00rootroot00000000000000bob betty tpl-1.6.1/tests/test50.c000066400000000000000000000006021230144017500147430ustar00rootroot00000000000000 #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); } tpl-1.6.1/tests/test51.ans000066400000000000000000000000261230144017500153030ustar00rootroot00000000000000num_tpls: 4, sum: 180 tpl-1.6.1/tests/test51.c000066400000000000000000000030041230144017500147430ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test51_0.tpl000066400000000000000000000000711230144017500155400ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test51_1.tpl000066400000000000000000000001621230144017500155420ustar00rootroot00000000000000tpl9A(i)  tpl9A(i)  tpl-1.6.1/tests/test51_2.tpl000066400000000000000000000000361230144017500155430ustar00rootroot00000000000000tpl9A(i) tpl-1.6.1/tests/test51_3.tpl000066400000000000000000000000241230144017500155410ustar00rootroot00000000000000tpl-1.6.1/tests/test51_4.tpl000066400000000000000000000000071230144017500155430ustar00rootroot00000000000000 tpl-1.6.1/tests/test52.ans000066400000000000000000000000261230144017500153040ustar00rootroot00000000000000---------- --> j is 1 tpl-1.6.1/tests/test52.c000066400000000000000000000010621230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test53.ans000066400000000000000000000000411230144017500153020ustar00rootroot00000000000000---------- --> j is 1 ---------- tpl-1.6.1/tests/test53.c000066400000000000000000000011311230144017500147440ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test54.ans000066400000000000000000000001151230144017500153050ustar00rootroot00000000000000tpl_mem_gather aborted by app callback tpl_gather_mem returned <= 0, exiting tpl-1.6.1/tests/test54.c000066400000000000000000000033311230144017500147510ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test54_0.tpl000066400000000000000000000000711230144017500155430ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test54_1.tpl000066400000000000000000000001621230144017500155450ustar00rootroot00000000000000tpl9A(i)  tpl9A(i)  tpl-1.6.1/tests/test54_2.tpl000066400000000000000000000000361230144017500155460ustar00rootroot00000000000000tpl9A(i) tpl-1.6.1/tests/test54_3.tpl000066400000000000000000000000241230144017500155440ustar00rootroot00000000000000tpl-1.6.1/tests/test54_4.tpl000066400000000000000000000000071230144017500155460ustar00rootroot00000000000000 tpl-1.6.1/tests/test55.ans000066400000000000000000000001141230144017500153050ustar00rootroot00000000000000tpl_fd_gather aborted by app callback 2 tpls gathered. 499545 is their sum. tpl-1.6.1/tests/test55.c000066400000000000000000000046671230144017500147670ustar00rootroot00000000000000#include #include #include #include #include #include #include "tpl.h" #define DEBUG 0 extern tpl_hook_t tpl_hook; int num_tpls = 0, sum_tpls = 0; int tpl_cb(void *tpl, size_t tpllen, void*data) { int i; tpl_node *tn; tpl_hook.oops = printf; if (DEBUG) printf("obtained tpl of length %d\n", (int)tpllen); tn = tpl_map("A(i)", &i); tpl_load(tn, TPL_MEM, tpl, tpllen); num_tpls++; while (tpl_unpack(tn,1) > 0) sum_tpls += i; tpl_free(tn); /* this next line is a hack to test the callback's ability * to abort further tpl processing by returning < 0 */ if (num_tpls == 1) return -1; return 0; } int main() { FILE *f1,*f2; int fdflags,fd,fd1,fd2; int selrc, maxfd; tpl_gather_t *gs1=NULL,*gs2=NULL,**gs; struct timeval tv; fd_set rset; f1 = popen("cat test26_0.tpl;sleep 1; cat test26_1.tpl", "r"); fd1 = fileno(f1); fdflags = fcntl(fd1, F_GETFL, 0); fcntl( fd1, F_SETFL, fdflags | O_NONBLOCK); f2 = popen("cat test26_2.tpl;sleep 1; cat test26_3.tpl", "r"); fd2 = fileno(f2); fdflags = fcntl(fd2, F_GETFL, 0); fcntl( fd2, F_SETFL, fdflags | O_NONBLOCK); while (1) { FD_ZERO( &rset ); if (fd1 >= 0) FD_SET( fd1, &rset ); if (fd2 >= 0) FD_SET( fd2, &rset ); if (fd1 == -1 && fd2 == -1) { printf("%d tpls gathered.\n",num_tpls); printf("%d is their sum.\n",sum_tpls); return(0); } maxfd=0; if (fd1>maxfd) maxfd = fd1; if (fd2>maxfd) maxfd = fd2; tv.tv_sec = 5; tv.tv_usec = 0; selrc = select(maxfd+1, &rset, NULL, NULL, &tv ); if (selrc == -1) { perror("select()"); } else if (selrc) { for(fd=0;fd0\n"); } } } } else { if (DEBUG) printf("timeout\n"); } } return(0); } tpl-1.6.1/tests/test55_0.tpl000066400000000000000000000000711230144017500155440ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test55_1.tpl000066400000000000000000000076611230144017500155610ustar00rootroot00000000000000tplA(i)  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test55_2.tpl000066400000000000000000000037201230144017500155520ustar00rootroot00000000000000tplA(i)  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test55_3.tpl000066400000000000000000000037411230144017500155560ustar00rootroot00000000000000      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~tpl-1.6.1/tests/test56.ans000066400000000000000000000000731230144017500153120ustar00rootroot00000000000000hs2.s1 length: 5 hs2.s1: draco hs2.s2 length: 2 hs2.s2: po tpl-1.6.1/tests/test56.c000066400000000000000000000015461230144017500147610ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test57.ans000066400000000000000000000000001230144017500153010ustar00rootroot00000000000000tpl-1.6.1/tests/test57.c000066400000000000000000000007611230144017500147600ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test58.ans000066400000000000000000000000671230144017500153170ustar00rootroot00000000000000hs.s1 length: 5 hs.s1: draco hs.s2 length: 2 hs.s2: po tpl-1.6.1/tests/test58.c000066400000000000000000000011361230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test59.ans000066400000000000000000000001151230144017500153120ustar00rootroot00000000000000hs2.a: t hs2.s1 length: 5 hs2.s1: draco hs2.b: h hs2.s2 length: 2 hs2.s2: po tpl-1.6.1/tests/test59.c000066400000000000000000000017701230144017500147630ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test6.ans000066400000000000000000000000231230144017500152200ustar00rootroot00000000000000t is hello, world! tpl-1.6.1/tests/test6.c000066400000000000000000000006611230144017500146710ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test60.ans000066400000000000000000000001061230144017500153020ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test60.c000066400000000000000000000006211230144017500147450ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test61.ans000066400000000000000000000002261230144017500153060ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test61.c000066400000000000000000000006341230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test61_0.tpl000066400000000000000000000000711230144017500155410ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test61_1.tpl000066400000000000000000000000711230144017500155420ustar00rootroot00000000000000tpl9A(i) tpl-1.6.1/tests/test62.ans000066400000000000000000000002251230144017500153060ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test62.c000066400000000000000000000010031230144017500147420ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test62_0.tpl000066400000000000000000000000711230144017500155420ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test63.ans000066400000000000000000000002251230144017500153070ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test63.c000066400000000000000000000010171230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test64.ans000066400000000000000000000000631230144017500153100ustar00rootroot00000000000000s is beta sh.str is delta bin.addr is zeta, size 5 tpl-1.6.1/tests/test64.c000066400000000000000000000043051230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test65.ans000066400000000000000000000000001230144017500153000ustar00rootroot00000000000000tpl-1.6.1/tests/test65.c000066400000000000000000000004451230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test66.ans000066400000000000000000000000001230144017500153010ustar00rootroot00000000000000tpl-1.6.1/tests/test66.c000066400000000000000000000005601230144017500147550ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test67.ans000066400000000000000000000001031230144017500153060ustar00rootroot00000000000000load succeeded /tmp/test67.tpl: array lengths mismatch load failed tpl-1.6.1/tests/test67.c000066400000000000000000000017471230144017500147660ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test68.ans000066400000000000000000000000001230144017500153030ustar00rootroot00000000000000tpl-1.6.1/tests/test68.c000066400000000000000000000015001230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test7.ans000066400000000000000000000000631230144017500152250ustar00rootroot00000000000000t is wonderful t is prince of peace t is counselor tpl-1.6.1/tests/test7.c000066400000000000000000000010711230144017500146660ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test70.ans000066400000000000000000000000341230144017500153030ustar00rootroot00000000000000a 1 hi o matilda y 3.140000 tpl-1.6.1/tests/test70.c000066400000000000000000000014441230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test71.ans000066400000000000000000000000341230144017500153040ustar00rootroot00000000000000# 1 hi o matilda y 3.140000 tpl-1.6.1/tests/test71.c000066400000000000000000000014471230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test72.ans000066400000000000000000000000341230144017500153050ustar00rootroot00000000000000a 1 hi o matilda y 3.140000 tpl-1.6.1/tests/test72.c000066400000000000000000000014441230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test73.ans000066400000000000000000000000361230144017500153100ustar00rootroot00000000000000a 1 hi o matilda tdh 3.140000 tpl-1.6.1/tests/test73.c000066400000000000000000000015221230144017500147520ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test74.ans000066400000000000000000000035671230144017500153250ustar00rootroot000000000000001234 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 tpl-1.6.1/tests/test74.c000066400000000000000000000015051230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test75.ans000066400000000000000000000000221230144017500153050ustar00rootroot00000000000000string byte array tpl-1.6.1/tests/test75.c000066400000000000000000000015711230144017500147600ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test76.ans000066400000000000000000000000221230144017500153060ustar00rootroot00000000000000string byte array tpl-1.6.1/tests/test76.c000066400000000000000000000016131230144017500147560ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test77.ans000066400000000000000000000000221230144017500153070ustar00rootroot00000000000000string byte array tpl-1.6.1/tests/test77.c000066400000000000000000000015721230144017500147630ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test78.ans000066400000000000000000000000001230144017500153040ustar00rootroot00000000000000tpl-1.6.1/tests/test78.c000066400000000000000000000006111230144017500147550ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test79.ans000066400000000000000000000000221230144017500153110ustar00rootroot00000000000000got a got 3 got 4 tpl-1.6.1/tests/test79.c000066400000000000000000000007021230144017500147570ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test8.ans000066400000000000000000000002521230144017500152260ustar00rootroot00000000000000d 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 tpl-1.6.1/tests/test8.c000066400000000000000000000011001230144017500146600ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test80.ans000066400000000000000000000000271230144017500153060ustar00rootroot00000000000000a 0 1 2 3 4 5 6 7 8 9 tpl-1.6.1/tests/test80.c000066400000000000000000000011151230144017500147460ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test83.ans000066400000000000000000000003521230144017500153120ustar00rootroot00000000000000& 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 tpl-1.6.1/tests/test83.c000077500000000000000000000014611230144017500147600ustar00rootroot00000000000000#include #include "tpl.h" #define ILEN 10 #define KLEN 8 #define FLEN 5 struct st { char c; double f[FLEN]; }; int main() { tpl_node *tn; /* some meaningless test data */ struct st s; int j; int i[ILEN]; int k[KLEN]; char a; char b; tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); tpl_load(tn,TPL_FILE,"/tmp/test82.tpl"); tpl_unpack(tn,0); printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]); while( tpl_unpack(tn,1) > 0) { for(j=0;j 0) { printf("%c ", b); for(j=0;j #include "tpl.h" #define ILEN 10 #define KLEN 8 #define FLEN 5 struct st { char c; double f[FLEN]; }; int main() { tpl_node *tn; /* some meaningless test data */ struct st s; int j; int i[ILEN]; int k[KLEN]; char a; char b; tn = tpl_map("cA(i#)S(cf#)A(ci#)", &a, i, ILEN, &s, FLEN, &b, k, KLEN); tpl_load(tn,TPL_FILE,"test84_0.tpl"); tpl_unpack(tn,0); printf("%c %c %.2f %.2f %.2f %.2f %.2f \n", a, s.c, s.f[0], s.f[1], s.f[2], s.f[3], s.f[4]); while( tpl_unpack(tn,1) > 0) { for(j=0;j 0) { printf("%c ", b); for(j=0;j 0) { for(j=0;j 0) { printf("%c ", b); for(j=0;j #include #include "tpl.h" int main() { char *fmt; fmt = tpl_peek(TPL_FILE, "test85.tpl"); if (fmt) { printf("%s\n",fmt); free(fmt); } return 0; } tpl-1.6.1/tests/test85.tpl000066400000000000000000000004641230144017500153360ustar00rootroot00000000000000tpl4cA(i#)S(cf#)A(ci#) &z??ffffff?333333??xd,X y^&Rz,X tpl-1.6.1/tests/test86.ans000066400000000000000000000000101230144017500153040ustar00rootroot00000000000000A(A(i)) tpl-1.6.1/tests/test86.c000066400000000000000000000007321230144017500147600ustar00rootroot00000000000000#include #include #include "tpl.h" int main() { tpl_node *tn; int o,i; void *addr; int sz; char *fmt; tn = tpl_map("A(A(i))",&i); for(o=0;o<10;o++) { for(i=o; i < o+10; i++) tpl_pack(tn,2); tpl_pack(tn,1); } tpl_dump(tn,TPL_MEM,&addr,&sz); tpl_free(tn); fmt = tpl_peek(TPL_MEM, addr, sz); if (fmt) { printf("%s\n",fmt); free(fmt); } free(addr); return(0); } tpl-1.6.1/tests/test87.ans000066400000000000000000000000201230144017500153060ustar00rootroot00000000000000sum is 49995000 tpl-1.6.1/tests/test87.c000066400000000000000000000020141230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test88.ans000066400000000000000000000000001230144017500153050ustar00rootroot00000000000000tpl-1.6.1/tests/test88.c000066400000000000000000000004561230144017500147650ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test89.ans000066400000000000000000000000131230144017500153120ustar00rootroot000000000000001 abc 3.14 tpl-1.6.1/tests/test89.c000066400000000000000000000005601230144017500147620ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test9.ans000066400000000000000000000001061230144017500152250ustar00rootroot00000000000000i 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 tpl-1.6.1/tests/test9.c000066400000000000000000000003601230144017500146700ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test9.tpl000066400000000000000000000000711230144017500152440ustar00rootroot00000000000000tpl9A(i)  tpl-1.6.1/tests/test90.ans000066400000000000000000000000231230144017500153030ustar00rootroot00000000000000cn is equal to cn2 tpl-1.6.1/tests/test90.c000066400000000000000000000010261230144017500147500ustar00rootroot00000000000000#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 ) ; } tpl-1.6.1/tests/test91.ans000066400000000000000000000000231230144017500153040ustar00rootroot00000000000000cn is equal to cn2 tpl-1.6.1/tests/test91.c000066400000000000000000000010301230144017500147440ustar00rootroot00000000000000#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 ) ; } tpl-1.6.1/tests/test92.ans000066400000000000000000000000001230144017500153000ustar00rootroot00000000000000tpl-1.6.1/tests/test92.c000066400000000000000000000021071230144017500147530ustar00rootroot00000000000000#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 ) ; } tpl-1.6.1/tests/test93.ans000066400000000000000000000000121230144017500153040ustar00rootroot00000000000000s is null tpl-1.6.1/tests/test93.c000066400000000000000000000006461230144017500147620ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test94.ans000066400000000000000000000000651230144017500153150ustar00rootroot00000000000000s is hello s is NULL s is hello s is NULL s is hello tpl-1.6.1/tests/test94.c000066400000000000000000000010221230144017500147500ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test95.ans000066400000000000000000000000251230144017500153120ustar00rootroot00000000000000s1 NULL s2 s3 hello tpl-1.6.1/tests/test95.c000066400000000000000000000010341230144017500147540ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test96.ans000066400000000000000000000001511230144017500153130ustar00rootroot00000000000000s1 NULL s2 s3 hello s1 NULL s2 s3 hello s1 NULL s2 s3 hello s1 NULL s2 s3 hello s1 NULL s2 s3 hello tpl-1.6.1/tests/test96.c000066400000000000000000000011351230144017500147570ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test97.ans000066400000000000000000000000261230144017500153150ustar00rootroot00000000000000j is -128, v is 65535 tpl-1.6.1/tests/test97.c000066400000000000000000000007231230144017500147620ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test98.ans000066400000000000000000000001021230144017500153110ustar00rootroot00000000000000j is -128, v is 65535 j is -129, v is 65534 j is -130, v is 65533 tpl-1.6.1/tests/test98.c000066400000000000000000000010551230144017500147620ustar00rootroot00000000000000#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); } tpl-1.6.1/tests/test99.ans000066400000000000000000000000251230144017500153160ustar00rootroot00000000000000cA(i#)S(cf#)A(ci#) & tpl-1.6.1/tests/test99.c000077500000000000000000000004111230144017500147610ustar00rootroot00000000000000#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; } tpl-1.6.1/tests/test99.tpl000066400000000000000000000004641230144017500153430ustar00rootroot00000000000000tpl4cA(i#)S(cf#)A(ci#) &z??ffffff?333333??xd,X y^&Rz,X tpl-1.6.1/tests/threads/000077500000000000000000000000001230144017500151075ustar00rootroot00000000000000tpl-1.6.1/tests/threads/Makefile000066400000000000000000000011341230144017500165460ustar00rootroot00000000000000TPLSRC = ../../src PROGS = test1 # Thread support requires compiler-specific options # ---------------------------------------------------------------------------- # GNU CFLAGS = -I$(TPLSRC) -g -pthread # Solaris (Studio 11 on Sparc Ultra3) #CFLAGS = -I$(TPLSRC) -g -mt # ---------------------------------------------------------------------------- all: $(PROGS) run_tests tpl.o : $(TPLSRC)/tpl.c $(TPLSRC)/tpl.h $(CC) -c $(CFLAGS) $(TPLSRC)/tpl.c $(PROGS) : tpl.o $(CC) $(CFLAGS) -o $@ $(@).c tpl.o run_tests: $(PROGS) perl ../do_tests .PHONY: clean clean: rm -f $(PROGS) tpl.o test*.out tpl-1.6.1/tests/threads/README000066400000000000000000000001561230144017500157710ustar00rootroot00000000000000Tests for using tpl in threaded programs. thread_ipc.c a thread version of the pipe IPC test ../test28.c tpl-1.6.1/tests/threads/test1.ans000066400000000000000000000000461230144017500166520ustar00rootroot00000000000000sum is 49995000 thread result: 0 null tpl-1.6.1/tests/threads/test1.c000066400000000000000000000020361230144017500163140ustar00rootroot00000000000000#include #include #include "tpl.h" #include #include #include #include #include #include #include #include int fd[2]; void *thread_routine( void *arg ) { tpl_node *tn; int i,sum=0; /* child */ tn = tpl_map("A(u)",&i); tpl_load(tn, TPL_FD, fd[0]); while (tpl_unpack(tn,1) > 0) sum += i; tpl_free(tn); printf("sum is %d\n", sum); return NULL; } int main() { tpl_node *tn; unsigned i; int status; pthread_t thread_id; void *thread_result; pipe(fd); if ( status = pthread_create( &thread_id, NULL, thread_routine, NULL )) { printf("failure: status %d\n", status); exit(-1); } /* parent */ tn = tpl_map("A(u)",&i); for(i=0;i<10000;i++) tpl_pack(tn,1); tpl_dump(tn,TPL_FD, fd[1] ); tpl_free(tn); status = pthread_join( thread_id, &thread_result ); printf("thread result: %d %s\n", status, thread_result ? "non-null":"null"); }