mirror of
https://github.com/troydhanson/tpl.git
synced 2024-12-30 01:06:53 +08:00
2372 lines
86 KiB
HTML
2372 lines
86 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
|
<meta name="generator" content="AsciiDoc 8.6.8" />
|
|
<title>tpl User Guide</title>
|
|
<style type="text/css">
|
|
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
|
|
|
|
/* Default font. */
|
|
body {
|
|
font-family: Georgia,serif;
|
|
}
|
|
|
|
/* Title font. */
|
|
h1, h2, h3, h4, h5, h6,
|
|
div.title, caption.title,
|
|
thead, p.table.header,
|
|
#toctitle,
|
|
#author, #revnumber, #revdate, #revremark,
|
|
#footer {
|
|
font-family: Arial,Helvetica,sans-serif;
|
|
}
|
|
|
|
body {
|
|
margin: 1em 5% 1em 5%;
|
|
}
|
|
|
|
a {
|
|
color: blue;
|
|
text-decoration: underline;
|
|
}
|
|
a:visited {
|
|
color: fuchsia;
|
|
}
|
|
|
|
em {
|
|
font-style: italic;
|
|
color: navy;
|
|
}
|
|
|
|
strong {
|
|
font-weight: bold;
|
|
color: #083194;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
color: #527bbd;
|
|
margin-top: 1.2em;
|
|
margin-bottom: 0.5em;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
h1, h2, h3 {
|
|
border-bottom: 2px solid silver;
|
|
}
|
|
h2 {
|
|
padding-top: 0.5em;
|
|
}
|
|
h3 {
|
|
float: left;
|
|
}
|
|
h3 + * {
|
|
clear: left;
|
|
}
|
|
h5 {
|
|
font-size: 1.0em;
|
|
}
|
|
|
|
div.sectionbody {
|
|
margin-left: 0;
|
|
}
|
|
|
|
hr {
|
|
border: 1px solid silver;
|
|
}
|
|
|
|
p {
|
|
margin-top: 0.5em;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
|
|
ul, ol, li > p {
|
|
margin-top: 0;
|
|
}
|
|
ul > li { color: #aaa; }
|
|
ul > li > * { color: black; }
|
|
|
|
.monospaced, code, pre {
|
|
font-family: "Courier New", Courier, monospace;
|
|
font-size: inherit;
|
|
color: navy;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
|
|
#author {
|
|
color: #527bbd;
|
|
font-weight: bold;
|
|
font-size: 1.1em;
|
|
}
|
|
#email {
|
|
}
|
|
#revnumber, #revdate, #revremark {
|
|
}
|
|
|
|
#footer {
|
|
font-size: small;
|
|
border-top: 2px solid silver;
|
|
padding-top: 0.5em;
|
|
margin-top: 4.0em;
|
|
}
|
|
#footer-text {
|
|
float: left;
|
|
padding-bottom: 0.5em;
|
|
}
|
|
#footer-badges {
|
|
float: right;
|
|
padding-bottom: 0.5em;
|
|
}
|
|
|
|
#preamble {
|
|
margin-top: 1.5em;
|
|
margin-bottom: 1.5em;
|
|
}
|
|
div.imageblock, div.exampleblock, div.verseblock,
|
|
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
|
|
div.admonitionblock {
|
|
margin-top: 1.0em;
|
|
margin-bottom: 1.5em;
|
|
}
|
|
div.admonitionblock {
|
|
margin-top: 2.0em;
|
|
margin-bottom: 2.0em;
|
|
margin-right: 10%;
|
|
color: #606060;
|
|
}
|
|
|
|
div.content { /* Block element content. */
|
|
padding: 0;
|
|
}
|
|
|
|
/* Block element titles. */
|
|
div.title, caption.title {
|
|
color: #527bbd;
|
|
font-weight: bold;
|
|
text-align: left;
|
|
margin-top: 1.0em;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
div.title + * {
|
|
margin-top: 0;
|
|
}
|
|
|
|
td div.title:first-child {
|
|
margin-top: 0.0em;
|
|
}
|
|
div.content div.title:first-child {
|
|
margin-top: 0.0em;
|
|
}
|
|
div.content + div.title {
|
|
margin-top: 0.0em;
|
|
}
|
|
|
|
div.sidebarblock > div.content {
|
|
background: #ffffee;
|
|
border: 1px solid #dddddd;
|
|
border-left: 4px solid #f0f0f0;
|
|
padding: 0.5em;
|
|
}
|
|
|
|
div.listingblock > div.content {
|
|
border: 1px solid #dddddd;
|
|
border-left: 5px solid #f0f0f0;
|
|
background: #f8f8f8;
|
|
padding: 0.5em;
|
|
}
|
|
|
|
div.quoteblock, div.verseblock {
|
|
padding-left: 1.0em;
|
|
margin-left: 1.0em;
|
|
margin-right: 10%;
|
|
border-left: 5px solid #f0f0f0;
|
|
color: #888;
|
|
}
|
|
|
|
div.quoteblock > div.attribution {
|
|
padding-top: 0.5em;
|
|
text-align: right;
|
|
}
|
|
|
|
div.verseblock > pre.content {
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
}
|
|
div.verseblock > div.attribution {
|
|
padding-top: 0.75em;
|
|
text-align: left;
|
|
}
|
|
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
|
|
div.verseblock + div.attribution {
|
|
text-align: left;
|
|
}
|
|
|
|
div.admonitionblock .icon {
|
|
vertical-align: top;
|
|
font-size: 1.1em;
|
|
font-weight: bold;
|
|
text-decoration: underline;
|
|
color: #527bbd;
|
|
padding-right: 0.5em;
|
|
}
|
|
div.admonitionblock td.content {
|
|
padding-left: 0.5em;
|
|
border-left: 3px solid #dddddd;
|
|
}
|
|
|
|
div.exampleblock > div.content {
|
|
border-left: 3px solid #dddddd;
|
|
padding-left: 0.5em;
|
|
}
|
|
|
|
div.imageblock div.content { padding-left: 0; }
|
|
span.image img { border-style: none; vertical-align: text-bottom; }
|
|
a.image:visited { color: white; }
|
|
|
|
dl {
|
|
margin-top: 0.8em;
|
|
margin-bottom: 0.8em;
|
|
}
|
|
dt {
|
|
margin-top: 0.5em;
|
|
margin-bottom: 0;
|
|
font-style: normal;
|
|
color: navy;
|
|
}
|
|
dd > *:first-child {
|
|
margin-top: 0.1em;
|
|
}
|
|
|
|
ul, ol {
|
|
list-style-position: outside;
|
|
}
|
|
ol.arabic {
|
|
list-style-type: decimal;
|
|
}
|
|
ol.loweralpha {
|
|
list-style-type: lower-alpha;
|
|
}
|
|
ol.upperalpha {
|
|
list-style-type: upper-alpha;
|
|
}
|
|
ol.lowerroman {
|
|
list-style-type: lower-roman;
|
|
}
|
|
ol.upperroman {
|
|
list-style-type: upper-roman;
|
|
}
|
|
|
|
div.compact ul, div.compact ol,
|
|
div.compact p, div.compact p,
|
|
div.compact div, div.compact div {
|
|
margin-top: 0.1em;
|
|
margin-bottom: 0.1em;
|
|
}
|
|
|
|
tfoot {
|
|
font-weight: bold;
|
|
}
|
|
td > div.verse {
|
|
white-space: pre;
|
|
}
|
|
|
|
div.hdlist {
|
|
margin-top: 0.8em;
|
|
margin-bottom: 0.8em;
|
|
}
|
|
div.hdlist tr {
|
|
padding-bottom: 15px;
|
|
}
|
|
dt.hdlist1.strong, td.hdlist1.strong {
|
|
font-weight: bold;
|
|
}
|
|
td.hdlist1 {
|
|
vertical-align: top;
|
|
font-style: normal;
|
|
padding-right: 0.8em;
|
|
color: navy;
|
|
}
|
|
td.hdlist2 {
|
|
vertical-align: top;
|
|
}
|
|
div.hdlist.compact tr {
|
|
margin: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.comment {
|
|
background: yellow;
|
|
}
|
|
|
|
.footnote, .footnoteref {
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
span.footnote, span.footnoteref {
|
|
vertical-align: super;
|
|
}
|
|
|
|
#footnotes {
|
|
margin: 20px 0 20px 0;
|
|
padding: 7px 0 0 0;
|
|
}
|
|
|
|
#footnotes div.footnote {
|
|
margin: 0 0 5px 0;
|
|
}
|
|
|
|
#footnotes hr {
|
|
border: none;
|
|
border-top: 1px solid silver;
|
|
height: 1px;
|
|
text-align: left;
|
|
margin-left: 0;
|
|
width: 20%;
|
|
min-width: 100px;
|
|
}
|
|
|
|
div.colist td {
|
|
padding-right: 0.5em;
|
|
padding-bottom: 0.3em;
|
|
vertical-align: top;
|
|
}
|
|
div.colist td img {
|
|
margin-top: 0.3em;
|
|
}
|
|
|
|
@media print {
|
|
#footer-badges { display: none; }
|
|
}
|
|
|
|
#toc {
|
|
margin-bottom: 2.5em;
|
|
}
|
|
|
|
#toctitle {
|
|
color: #527bbd;
|
|
font-size: 1.1em;
|
|
font-weight: bold;
|
|
margin-top: 1.0em;
|
|
margin-bottom: 0.1em;
|
|
}
|
|
|
|
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
div.toclevel2 {
|
|
margin-left: 2em;
|
|
font-size: 0.9em;
|
|
}
|
|
div.toclevel3 {
|
|
margin-left: 4em;
|
|
font-size: 0.9em;
|
|
}
|
|
div.toclevel4 {
|
|
margin-left: 6em;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
span.aqua { color: aqua; }
|
|
span.black { color: black; }
|
|
span.blue { color: blue; }
|
|
span.fuchsia { color: fuchsia; }
|
|
span.gray { color: gray; }
|
|
span.green { color: green; }
|
|
span.lime { color: lime; }
|
|
span.maroon { color: maroon; }
|
|
span.navy { color: navy; }
|
|
span.olive { color: olive; }
|
|
span.purple { color: purple; }
|
|
span.red { color: red; }
|
|
span.silver { color: silver; }
|
|
span.teal { color: teal; }
|
|
span.white { color: white; }
|
|
span.yellow { color: yellow; }
|
|
|
|
span.aqua-background { background: aqua; }
|
|
span.black-background { background: black; }
|
|
span.blue-background { background: blue; }
|
|
span.fuchsia-background { background: fuchsia; }
|
|
span.gray-background { background: gray; }
|
|
span.green-background { background: green; }
|
|
span.lime-background { background: lime; }
|
|
span.maroon-background { background: maroon; }
|
|
span.navy-background { background: navy; }
|
|
span.olive-background { background: olive; }
|
|
span.purple-background { background: purple; }
|
|
span.red-background { background: red; }
|
|
span.silver-background { background: silver; }
|
|
span.teal-background { background: teal; }
|
|
span.white-background { background: white; }
|
|
span.yellow-background { background: yellow; }
|
|
|
|
span.big { font-size: 2em; }
|
|
span.small { font-size: 0.6em; }
|
|
|
|
span.underline { text-decoration: underline; }
|
|
span.overline { text-decoration: overline; }
|
|
span.line-through { text-decoration: line-through; }
|
|
|
|
div.unbreakable { page-break-inside: avoid; }
|
|
|
|
|
|
/*
|
|
* xhtml11 specific
|
|
*
|
|
* */
|
|
|
|
div.tableblock {
|
|
margin-top: 1.0em;
|
|
margin-bottom: 1.5em;
|
|
}
|
|
div.tableblock > table {
|
|
border: 3px solid #527bbd;
|
|
}
|
|
thead, p.table.header {
|
|
font-weight: bold;
|
|
color: #527bbd;
|
|
}
|
|
p.table {
|
|
margin-top: 0;
|
|
}
|
|
/* Because the table frame attribute is overriden by CSS in most browsers. */
|
|
div.tableblock > table[frame="void"] {
|
|
border-style: none;
|
|
}
|
|
div.tableblock > table[frame="hsides"] {
|
|
border-left-style: none;
|
|
border-right-style: none;
|
|
}
|
|
div.tableblock > table[frame="vsides"] {
|
|
border-top-style: none;
|
|
border-bottom-style: none;
|
|
}
|
|
|
|
|
|
/*
|
|
* html5 specific
|
|
*
|
|
* */
|
|
|
|
table.tableblock {
|
|
margin-top: 1.0em;
|
|
margin-bottom: 1.5em;
|
|
}
|
|
thead, p.tableblock.header {
|
|
font-weight: bold;
|
|
color: #527bbd;
|
|
}
|
|
p.tableblock {
|
|
margin-top: 0;
|
|
}
|
|
table.tableblock {
|
|
border-width: 3px;
|
|
border-spacing: 0px;
|
|
border-style: solid;
|
|
border-color: #527bbd;
|
|
border-collapse: collapse;
|
|
}
|
|
th.tableblock, td.tableblock {
|
|
border-width: 1px;
|
|
padding: 4px;
|
|
border-style: solid;
|
|
border-color: #527bbd;
|
|
}
|
|
|
|
table.tableblock.frame-topbot {
|
|
border-left-style: hidden;
|
|
border-right-style: hidden;
|
|
}
|
|
table.tableblock.frame-sides {
|
|
border-top-style: hidden;
|
|
border-bottom-style: hidden;
|
|
}
|
|
table.tableblock.frame-none {
|
|
border-style: hidden;
|
|
}
|
|
|
|
th.tableblock.halign-left, td.tableblock.halign-left {
|
|
text-align: left;
|
|
}
|
|
th.tableblock.halign-center, td.tableblock.halign-center {
|
|
text-align: center;
|
|
}
|
|
th.tableblock.halign-right, td.tableblock.halign-right {
|
|
text-align: right;
|
|
}
|
|
|
|
th.tableblock.valign-top, td.tableblock.valign-top {
|
|
vertical-align: top;
|
|
}
|
|
th.tableblock.valign-middle, td.tableblock.valign-middle {
|
|
vertical-align: middle;
|
|
}
|
|
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
|
|
vertical-align: bottom;
|
|
}
|
|
|
|
|
|
/*
|
|
* manpage specific
|
|
*
|
|
* */
|
|
|
|
body.manpage h1 {
|
|
padding-top: 0.5em;
|
|
padding-bottom: 0.5em;
|
|
border-top: 2px solid silver;
|
|
border-bottom: 2px solid silver;
|
|
}
|
|
body.manpage h2 {
|
|
border-style: none;
|
|
}
|
|
body.manpage div.sectionbody {
|
|
margin-left: 3em;
|
|
}
|
|
|
|
@media print {
|
|
body.manpage div#toc { display: none; }
|
|
}
|
|
|
|
|
|
@media screen {
|
|
body {
|
|
max-width: 50em; /* approximately 80 characters wide */
|
|
margin-left: 16em;
|
|
}
|
|
|
|
#toc {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
width: 13em;
|
|
padding: 0.5em;
|
|
padding-bottom: 1.5em;
|
|
margin: 0;
|
|
overflow: auto;
|
|
border-right: 3px solid #f8f8f8;
|
|
background-color: white;
|
|
}
|
|
|
|
#toc .toclevel1 {
|
|
margin-top: 0.5em;
|
|
}
|
|
|
|
#toc .toclevel2 {
|
|
margin-top: 0.25em;
|
|
display: list-item;
|
|
color: #aaaaaa;
|
|
}
|
|
|
|
#toctitle {
|
|
margin-top: 0.5em;
|
|
}
|
|
}
|
|
</style>
|
|
<script type="text/javascript">
|
|
/*<![CDATA[*/
|
|
var asciidoc = { // Namespace.
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Table Of Contents generator
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
/* Author: Mihai Bazon, September 2002
|
|
* http://students.infoiasi.ro/~mishoo
|
|
*
|
|
* Table Of Content generator
|
|
* Version: 0.4
|
|
*
|
|
* Feel free to use this script under the terms of the GNU General Public
|
|
* License, as long as you do not remove or alter this notice.
|
|
*/
|
|
|
|
/* modified by Troy D. Hanson, September 2006. License: GPL */
|
|
/* modified by Stuart Rackham, 2006, 2009. License: GPL */
|
|
|
|
// toclevels = 1..4.
|
|
toc: function (toclevels) {
|
|
|
|
function getText(el) {
|
|
var text = "";
|
|
for (var i = el.firstChild; i != null; i = i.nextSibling) {
|
|
if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
|
|
text += i.data;
|
|
else if (i.firstChild != null)
|
|
text += getText(i);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
function TocEntry(el, text, toclevel) {
|
|
this.element = el;
|
|
this.text = text;
|
|
this.toclevel = toclevel;
|
|
}
|
|
|
|
function tocEntries(el, toclevels) {
|
|
var result = new Array;
|
|
var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
|
|
// Function that scans the DOM tree for header elements (the DOM2
|
|
// nodeIterator API would be a better technique but not supported by all
|
|
// browsers).
|
|
var iterate = function (el) {
|
|
for (var i = el.firstChild; i != null; i = i.nextSibling) {
|
|
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
|
|
var mo = re.exec(i.tagName);
|
|
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
|
|
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
|
|
}
|
|
iterate(i);
|
|
}
|
|
}
|
|
}
|
|
iterate(el);
|
|
return result;
|
|
}
|
|
|
|
var toc = document.getElementById("toc");
|
|
if (!toc) {
|
|
return;
|
|
}
|
|
|
|
// Delete existing TOC entries in case we're reloading the TOC.
|
|
var tocEntriesToRemove = [];
|
|
var i;
|
|
for (i = 0; i < toc.childNodes.length; i++) {
|
|
var entry = toc.childNodes[i];
|
|
if (entry.nodeName.toLowerCase() == 'div'
|
|
&& entry.getAttribute("class")
|
|
&& entry.getAttribute("class").match(/^toclevel/))
|
|
tocEntriesToRemove.push(entry);
|
|
}
|
|
for (i = 0; i < tocEntriesToRemove.length; i++) {
|
|
toc.removeChild(tocEntriesToRemove[i]);
|
|
}
|
|
|
|
// Rebuild TOC entries.
|
|
var entries = tocEntries(document.getElementById("content"), toclevels);
|
|
for (var i = 0; i < entries.length; ++i) {
|
|
var entry = entries[i];
|
|
if (entry.element.id == "")
|
|
entry.element.id = "_toc_" + i;
|
|
var a = document.createElement("a");
|
|
a.href = "#" + entry.element.id;
|
|
a.appendChild(document.createTextNode(entry.text));
|
|
var div = document.createElement("div");
|
|
div.appendChild(a);
|
|
div.className = "toclevel" + entry.toclevel;
|
|
toc.appendChild(div);
|
|
}
|
|
if (entries.length == 0)
|
|
toc.parentNode.removeChild(toc);
|
|
},
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Footnotes generator
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
/* Based on footnote generation code from:
|
|
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
|
|
*/
|
|
|
|
footnotes: function () {
|
|
// Delete existing footnote entries in case we're reloading the footnodes.
|
|
var i;
|
|
var noteholder = document.getElementById("footnotes");
|
|
if (!noteholder) {
|
|
return;
|
|
}
|
|
var entriesToRemove = [];
|
|
for (i = 0; i < noteholder.childNodes.length; i++) {
|
|
var entry = noteholder.childNodes[i];
|
|
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
|
|
entriesToRemove.push(entry);
|
|
}
|
|
for (i = 0; i < entriesToRemove.length; i++) {
|
|
noteholder.removeChild(entriesToRemove[i]);
|
|
}
|
|
|
|
// Rebuild footnote entries.
|
|
var cont = document.getElementById("content");
|
|
var spans = cont.getElementsByTagName("span");
|
|
var refs = {};
|
|
var n = 0;
|
|
for (i=0; i<spans.length; i++) {
|
|
if (spans[i].className == "footnote") {
|
|
n++;
|
|
var note = spans[i].getAttribute("data-note");
|
|
if (!note) {
|
|
// Use [\s\S] in place of . so multi-line matches work.
|
|
// Because JavaScript has no s (dotall) regex flag.
|
|
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
|
|
spans[i].innerHTML =
|
|
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
|
|
"' title='View footnote' class='footnote'>" + n + "</a>]";
|
|
spans[i].setAttribute("data-note", note);
|
|
}
|
|
noteholder.innerHTML +=
|
|
"<div class='footnote' id='_footnote_" + n + "'>" +
|
|
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
|
|
n + "</a>. " + note + "</div>";
|
|
var id =spans[i].getAttribute("id");
|
|
if (id != null) refs["#"+id] = n;
|
|
}
|
|
}
|
|
if (n == 0)
|
|
noteholder.parentNode.removeChild(noteholder);
|
|
else {
|
|
// Process footnoterefs.
|
|
for (i=0; i<spans.length; i++) {
|
|
if (spans[i].className == "footnoteref") {
|
|
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
|
|
href = href.match(/#.*/)[0]; // Because IE return full URL.
|
|
n = refs[href];
|
|
spans[i].innerHTML =
|
|
"[<a href='#_footnote_" + n +
|
|
"' title='View footnote' class='footnote'>" + n + "</a>]";
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
install: function(toclevels) {
|
|
var timerId;
|
|
|
|
function reinstall() {
|
|
asciidoc.footnotes();
|
|
if (toclevels) {
|
|
asciidoc.toc(toclevels);
|
|
}
|
|
}
|
|
|
|
function reinstallAndRemoveTimer() {
|
|
clearInterval(timerId);
|
|
reinstall();
|
|
}
|
|
|
|
timerId = setInterval(reinstall, 500);
|
|
if (document.addEventListener)
|
|
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
|
|
else
|
|
window.onload = reinstallAndRemoveTimer;
|
|
}
|
|
|
|
}
|
|
asciidoc.install(2);
|
|
/*]]>*/
|
|
</script>
|
|
</head>
|
|
<body class="article">
|
|
<div id="header">
|
|
<h1>tpl User Guide</h1>
|
|
<span id="author">Troy D. Hanson</span><br />
|
|
<span id="email"><code><<a href="mailto:tdh@tkhanson.net">tdh@tkhanson.net</a>></code></span><br />
|
|
<span id="revnumber">version 1.5,</span>
|
|
<span id="revdate">February 2010</span>
|
|
<div id="toc">
|
|
<div id="toctitle">Table of Contents</div>
|
|
<noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
<div id="preamble">
|
|
<div class="sectionbody">
|
|
<div class="paragraph"><p>To download tpl, follow this link back to the
|
|
<a href="https://github.com/troydhanson/tpl">GitHub project page</a>.</p></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_overview">Overview</h2>
|
|
<div class="sectionbody">
|
|
<div class="sect2">
|
|
<h3 id="_serialization_in_c">Serialization in C</h3>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_uses_for_tpl">Uses for tpl</h3>
|
|
<div class="paragraph"><p>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 <code>A(si)</code>. If the program needs two such lists (say, one for regular
|
|
users and one for administrators) this could be expressed as <code>A(si)A(si)</code>. It
|
|
is easy to read and write this kind of structured data using tpl.</p></div>
|
|
<div class="paragraph"><p>Tpl can also be used as an IPC message format. It handles byte order issues
|
|
and deframing individual messages off of a stream automatically.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_expressing_type">Expressing type</h3>
|
|
<div class="paragraph"><p>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:</p></div>
|
|
<div class="ulist"><ul>
|
|
<li>
|
|
<p>
|
|
<code>A(is)</code> is a variable-length array of integer-string pairs
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
<code>A(is)A(is)</code> are two such arrays, completely independent of one another
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
<code>S(ci)</code> is a structure containing a char and integer
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
<code>S(ci)#</code> is a fixed-length array of the latter structure
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
<code>A(A(i))</code> is a nested array, that is, an array of integer arrays
|
|
</p>
|
|
</li>
|
|
</ul></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_the_tpl_image">The tpl image</h3>
|
|
<div class="paragraph"><p>A tpl image is the serialized form of a tpl, stored in a memory buffer or file,
|
|
or written to a file descriptor.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_what_8217_s_in_a_tpl_image">What’s in a tpl image?</h4>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_no_framing_needed">No framing needed</h4>
|
|
<div class="paragraph"><p>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
|
|
<code>tpl_gather</code> (or <code>tpl_load</code> in <code>TPL_FD</code> 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.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_data_portability">Data portability</h4>
|
|
<div class="paragraph"><p>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 <a href="#types">format character</a> 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 <a href="#trouble_with_double">special difficulties</a>. <em>No guarantees
|
|
are made with regard to floating point portability.</em> That said, because many
|
|
modern CPU’s use IEEE 754 floating point representation, data is likely to be
|
|
portable among them.</p></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_xml_and_perl">XML and Perl</h3>
|
|
<div class="paragraph"><p><em>Note: The <code>tplxml</code> utility and the Perl module are currently unsupported in tpl 1.5.</em></p></div>
|
|
<div class="sect3">
|
|
<h4 id="_xml">XML</h4>
|
|
<div class="paragraph"><p>While a tpl image is a binary entity, you can view any tpl image in XML format
|
|
using the included <code>tplxml</code> utility, located in the <code>lang/perl</code> directory.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tplxml file.tpl > file.xml
|
|
tplxml file.xml > file.tpl</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The utility is bidirectional, as shown. The file extension is not important;
|
|
<code>tplxml</code> 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 <code>tplxml</code> 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.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_perl">Perl</h4>
|
|
<div class="paragraph"><p>There is a Perl module in <code>lang/perl/Tpl.pm</code>. The <a href="perl.html">Perl API</a>
|
|
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.</p></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_platforms">Platforms</h3>
|
|
<div class="paragraph"><p>The tpl software was developed for POSIX systems and has been tested on 32- and 64-bit
|
|
platforms including:</p></div>
|
|
<div class="ulist"><ul>
|
|
<li>
|
|
<p>
|
|
Linux
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
Solaris
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
Mac OS X
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
OpenBSD
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<p>
|
|
Windows using Visual Studio 2008 or 2010, or Cygwin or MinGW
|
|
</p>
|
|
</li>
|
|
</ul></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_bsd_licensed">BSD licensed</h3>
|
|
<div class="paragraph"><p>This software is made available under the
|
|
<a href="license.html">revised BSD license</a>.
|
|
It is free and open source.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_download">Download</h3>
|
|
<div class="paragraph"><p>You can clone tpl, or get a zipfile, from the
|
|
<a href="https://github.com/troydhanson/tpl">GitHub project page</a>.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_getting_help">Getting help</h3>
|
|
<div class="paragraph"><p>Please ask on Github if you need help. You can email the author at
|
|
Troy D. Hanson <<a href="mailto:tdh@tkhanson.net">tdh@tkhanson.net</a>>, but I am often behind on email by weeks or
|
|
months. Sorry!</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_contributing">Contributing</h3>
|
|
<div class="paragraph"><p>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!</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_news">News</h3>
|
|
<div class="paragraph"><p>The author has a blog for <a href="http://troydhanson.wordpress.com/">software updates</a>
|
|
<span class="image">
|
|
<img src="rss.png" alt="(RSS)" />
|
|
</span>. You can also follow @troydhanson on Twitter for updates.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_other_software">Other software</h4>
|
|
<div class="paragraph"><p>Other open-source software by the author is listed at <a href="http://tkhanson.net">http://tkhanson.net</a>.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_build_and_install">Build and install</h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="sect2">
|
|
<h3 id="_as_source">As source</h3>
|
|
<div class="paragraph"><p>The simplest way to use tpl is to copy the source files <code>tpl.h</code> and <code>tpl.c</code>
|
|
(from the <code>src/</code> directory) right into your project, and build them with the
|
|
rest of your source files. No special compiler flags are required.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_as_a_library">As a library</h3>
|
|
<div class="paragraph"><p>Alternatively, to build tpl as a library, from the top-level directory, run:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>./configure
|
|
make
|
|
make install</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>This installs a static library <code>libtpl.a</code> and a shared library (e.g.,
|
|
<code>libtpl.so</code>), if your system supports them, in standard places. The installation
|
|
directory can be customized using <code>./configure --prefix=/some/directory</code>. Run
|
|
<code>configure --help</code> for further options.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_test_suite">Test suite</h4>
|
|
<div class="paragraph"><p>You can compile and run the built-in test suite by running:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>cd tests/
|
|
make</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_on_windows">On Windows</h3>
|
|
<div class="sect3">
|
|
<h4 id="_dll">DLL</h4>
|
|
<div class="paragraph"><p>On the tpl home page, a Visual Studio 2008 solution package is available for
|
|
download. This zip file contains pre-built 32- and 64-bit versions of tpl as a
|
|
DLL. If you like, you can build the DLL yourself using VS2008 or VS2010 (the
|
|
free Express Edition is sufficient) by opening the solution file and choosing
|
|
Build Solution.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_non_dll_usage">Non-DLL usage</h4>
|
|
<div class="paragraph"><p>Alternatively, tpl can be used directly (instead of as a DLL) by compiling
|
|
the tpl sources right into your program. To do this, add <code>tpl.c</code>, <code>tpl.h</code>,
|
|
<code>win/mman.h</code> and <code>win/mmap.c</code> to your program’s source and header files and
|
|
add the preprocessor definition <code>TPL_NOLIB</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_mingw_cygwin">MinGW/Cygwin</h4>
|
|
<div class="paragraph"><p>Prior to tpl release 1.5, using tpl on Windows required building it with MinGW
|
|
or Cygwin. This is no longer necessary. If you want to build it that way anyway,
|
|
use the non-Windows (i.e. tar.bz2) tpl download and follow the "configure; make;
|
|
make install" approach.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_api_concepts">API concepts</h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="sect2">
|
|
<h3 id="_order_of_functions">Order of functions</h3>
|
|
<div class="paragraph"><p>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).</p></div>
|
|
<div class="tableblock">
|
|
<table rules="none"
|
|
width="50%"
|
|
frame="border"
|
|
cellspacing="0" cellpadding="4">
|
|
<caption class="title">Table 1. Order of usage</caption>
|
|
<col width="9%" />
|
|
<col width="45%" />
|
|
<col width="45%" />
|
|
<thead>
|
|
<tr>
|
|
<th align="center" valign="top">Step </th>
|
|
<th align="center" valign="top"> If you’re serializing…</th>
|
|
<th align="center" valign="top"> If you’re deserializing…</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">1.</p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_map()</code></p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_map()</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">2.</p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_pack()</code></p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_load()</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">3.</p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_dump()</code></p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_unpack()</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">4.</p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_free()</code></p></td>
|
|
<td align="center" valign="top"><p class="table"><code>tpl_free()</code></p></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="format">Format string</h3>
|
|
<div class="paragraph"><p>When a tpl is created using <code>tpl_map()</code>, 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 <code>tpl_map</code>:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tpl_node *tn;
|
|
char c;
|
|
int i[10];
|
|
tn = tpl_map("ci#", &c, i, 10); /* ci# is our format string */</code></pre>
|
|
</div></div>
|
|
<div class="tableblock" id="types">
|
|
<table rules="none"
|
|
width="90%"
|
|
frame="border"
|
|
cellspacing="0" cellpadding="4">
|
|
<caption class="title">Table 2. Supported format characters</caption>
|
|
<col width="11%" />
|
|
<col width="44%" />
|
|
<col width="44%" />
|
|
<thead>
|
|
<tr>
|
|
<th align="center" valign="top">Type </th>
|
|
<th align="left" valign="top"> Description </th>
|
|
<th align="left" valign="top"> Required argument type</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>j</code></p></td>
|
|
<td align="left" valign="top"><p class="table">16-bit signed int</p></td>
|
|
<td align="left" valign="top"><p class="table">int16_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>v</code></p></td>
|
|
<td align="left" valign="top"><p class="table">16-bit unsigned int</p></td>
|
|
<td align="left" valign="top"><p class="table">uint16_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>i</code></p></td>
|
|
<td align="left" valign="top"><p class="table">32-bit signed int</p></td>
|
|
<td align="left" valign="top"><p class="table">int32_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>u</code></p></td>
|
|
<td align="left" valign="top"><p class="table">32-bit unsigned int</p></td>
|
|
<td align="left" valign="top"><p class="table">uint32_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>I</code></p></td>
|
|
<td align="left" valign="top"><p class="table">64-bit signed int</p></td>
|
|
<td align="left" valign="top"><p class="table">int64_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>U</code></p></td>
|
|
<td align="left" valign="top"><p class="table">64-bit unsigned int</p></td>
|
|
<td align="left" valign="top"><p class="table">uint64_t* or equivalent</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>c</code></p></td>
|
|
<td align="left" valign="top"><p class="table">character (byte)</p></td>
|
|
<td align="left" valign="top"><p class="table">char*</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>s</code></p></td>
|
|
<td align="left" valign="top"><p class="table">string</p></td>
|
|
<td align="left" valign="top"><p class="table">char**</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>f</code></p></td>
|
|
<td align="left" valign="top"><p class="table">64-bit double precision float</p></td>
|
|
<td align="left" valign="top"><p class="table">double* (varies by platform)</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>#</code></p></td>
|
|
<td align="left" valign="top"><p class="table">array length; modifies preceding <code>iujvIUcsf</code> or <code>S(...)</code></p></td>
|
|
<td align="left" valign="top"><p class="table">int</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>B</code></p></td>
|
|
<td align="left" valign="top"><p class="table">binary buffer (arbitrary-length)</p></td>
|
|
<td align="left" valign="top"><p class="table">tpl_bin*</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>S</code></p></td>
|
|
<td align="left" valign="top"><p class="table">structure (…)</p></td>
|
|
<td align="left" valign="top"><p class="table">struct *</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>$</code></p></td>
|
|
<td align="left" valign="top"><p class="table">nested structure (…)</p></td>
|
|
<td align="left" valign="top"><p class="table">none</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table"><code>A</code></p></td>
|
|
<td align="left" valign="top"><p class="table">array (…)</p></td>
|
|
<td align="left" valign="top"><p class="table">none</p></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_explicit_sizes">Explicit sizes</h4>
|
|
<div class="paragraph"><p>The sizes of data types such as <code>long</code> and <code>double</code> 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
|
|
<code>int32_t</code> (defined in <code>inttypes.h</code>) in your program if you find this helpful.</p></div>
|
|
<div class="sect4">
|
|
<h5 id="trouble_with_double">The trouble with double</h5>
|
|
<div class="paragraph"><p>Unfortunately there are no standard explicit-sized floating-point types-- no
|
|
<code>float64_t</code>, for example. If you plan to serialize <code>double</code> on your platform
|
|
using tpl’s <code>f</code> format character, first be sure that your <code>double</code> 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.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="arrays">Arrays</h3>
|
|
<div class="paragraph"><p>Arrays come in two kinds: <strong>fixed-length</strong> and <strong>variable-length</strong> 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.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_fixed_length_vs_variable_length_arrays">Fixed-length vs. Variable-length arrays</h4>
|
|
<div class="dlist"><dl>
|
|
<dt class="hdlist1">
|
|
Notation
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Fixed-length arrays are denoted like <code>i#</code> (a simple type followed by one or
|
|
more <code>#</code> signs), but variable-length arrays are denoted like <code>A(i)</code>.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
Element handling
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
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.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
Array length
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
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.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
Element types
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Elements of fixed-length arrays can be the integer, byte, double, string
|
|
types or structures. (This excludes format characters <code>BA</code>). Fixed-length
|
|
arrays can also be multi-dimensional like <code>i##</code>. Variable-length arrays can
|
|
have simple or complex elements-- for example, an array of ints <code>A(i)</code>, an
|
|
array of int/double pairs <code>A(if)</code>, or even nested arrays like <code>A(A(if))</code>.
|
|
</p>
|
|
</dd>
|
|
</dl></div>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="listingblock" id="fixed_pack">
|
|
<div class="title">Packing 0-9 as a fixed-length array</div>
|
|
<div class="content">
|
|
<pre><code>#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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>Note that the length of the fixed-length array (10) was passed as an argument to
|
|
<code>tpl_map()</code>. The corresponding unpacking <a href="#fixed_unpack">example</a> is listed
|
|
further below. Now let’s see how we would pack 0-9 as a variable-length array:</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Packing 0-9 as a variable-length array</div>
|
|
<div class="content">
|
|
<pre><code>#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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>Notice how we called <code>tpl_pack</code> in a loop, once for each element 0-9. Again,
|
|
there is a corresponding unpacking <a href="#var_unpack">example</a> 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.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="index">Index numbers</h4>
|
|
<div class="paragraph"><p>Index numbers identify a particular variable-length array in the format string.
|
|
Each <code>A(...)</code> in a format string has its own index number. The index numbers
|
|
are assigned left-to-right starting from 1. Examples:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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) */</code></pre>
|
|
</div></div>
|
|
<div class="sect4">
|
|
<h5 id="_special_index_number_0">Special index number 0</h5>
|
|
<div class="paragraph"><p>The special index number 0 designates all the format characters that are not
|
|
inside an <code>A(...)</code>. Examples of what index 0 does (and does not) designate:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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) */</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>An index number is passed to <code>tpl_pack</code> and <code>tpl_unpack</code> to specify which
|
|
variable-length array (or non-array, in the case of index number 0) to act upon.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_integers">Integers</h3>
|
|
<div class="paragraph"><p>The array examples <a href="#fixed_pack">above</a> 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.</p></div>
|
|
<div class="listingblock" id="fixed_unpack">
|
|
<div class="title">Unpacking 0-9 from a fixed-length array</div>
|
|
<div class="content">
|
|
<pre><code>#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 */
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>For completeness, let’s also see how to unpack a variable-length integer array.</p></div>
|
|
<div class="listingblock" id="var_unpack">
|
|
<div class="title">Unpacking 0-9 from a variable-length array</div>
|
|
<div class="content">
|
|
<pre><code>#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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="sect3">
|
|
<h4 id="multidim_int">Multi-dimensional arrays</h4>
|
|
<div class="paragraph"><p>A multi-dimensional matrix of integers can be packed and unpacked the same way
|
|
as any fixed-length array.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>int xy[XDIM][YDIM];
|
|
...
|
|
tn = tpl_map("i##", xy, XDIM, YDIM);
|
|
tpl_pack(tn, 0);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>This single call to <code>tpl_pack</code> packs the entire matrix.</p></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_strings">Strings</h3>
|
|
<div class="paragraph"><p>Tpl can serialize C strings. A different format is used for <code>char*</code> vs. <code>char[ ]</code>
|
|
as described below. Let’s look at <code>char*</code> first:</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Packing a string</div>
|
|
<div class="content">
|
|
<pre><code> #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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The <code>char*</code> must point to a null-terminated string or be a <code>NULL</code> pointer.</p></div>
|
|
<div class="paragraph"><p>When deserializing (unpacking) a C string, space for it will be allocated
|
|
automatically, but you are responsible for freeing it (unless it is <code>NULL</code>):</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Unpacking a string</div>
|
|
<div class="content">
|
|
<pre><code> #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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="sect3">
|
|
<h4 id="_char_vs_char">char* vs char[ ]</h4>
|
|
<div class="paragraph"><p>The <code>s</code> format character is only for use with <code>char*</code> types. In the example
|
|
above, <code>s</code> is a <code>char*</code>. If it had been a <code>char s[14]</code>, we would use the format
|
|
characters <code>c#</code> 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 <code>c#</code> need not be
|
|
null-terminated.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_arrays_of_strings">Arrays of strings</h4>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>Later, when unpacking these strings, the programmer must remember to free them
|
|
one by one, after they are no longer needed.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>char *olabels[2][3];
|
|
int i,j;</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tn = tpl_map("s##", olabels, 2, 3);
|
|
tpl_load(tn,TPL_FILE,filename);
|
|
tpl_unpack(tn,0);
|
|
tpl_free(tn);</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>for(i=0;i<2;i++) {
|
|
for(j=0;j<3;j++) {
|
|
printf("%s\n", olabels[i][j]);
|
|
free(olabels[i][j]);
|
|
}
|
|
}</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_binary_buffers">Binary buffers</h3>
|
|
<div class="paragraph"><p>Packing an arbitrary-length binary buffer (tpl format character <code>B</code>) makes use
|
|
of the <code>tpl_bin</code> structure. You must declare this structure and populate it
|
|
with the address and length of the binary buffer to be packed.</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Packing a binary buffer</div>
|
|
<div class="content">
|
|
<pre><code> #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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>When you unpack a binary buffer, tpl will automatically allocate it, and will
|
|
populate your <code>tpl_bin</code> structure with its address and length. You are
|
|
responsible for eventually freeing the buffer.</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Unpacking a binary buffer</div>
|
|
<div class="content">
|
|
<pre><code> #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 */
|
|
}</code></pre>
|
|
</div></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_structures">Structures</h3>
|
|
<div class="paragraph"><p>You can use tpl to pack and unpack structures, and arrays of structures.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct ci {
|
|
char c;
|
|
int i;
|
|
};
|
|
struct ci s = {'a', 1};</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tn = tpl_map("S(ci)", &s); /* pass structure address */
|
|
tpl_pack(tn, 0);
|
|
tpl_dump(tn, TPL_FILE, "struct.tpl");
|
|
tpl_free(tn);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>As shown, omit the individual arguments for the format characters inside the
|
|
parenthesis. The exception is for fixed-length arrays; when <code>S(...)</code> contains a
|
|
<code>#</code> character, its length argument is required: <code>tpl_map("S(f#i)", &s, 10);</code></p></div>
|
|
<div class="paragraph"><p>When using the <code>S(...)</code> format, the only characters allowed inside the
|
|
parentheses are <code>iujvcsfIU#$()</code>.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_structure_arrays">Structure arrays</h4>
|
|
<div class="paragraph"><p>Arrays of structures are the same as simple arrays. Fixed- or variable- length
|
|
arrays are supported.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct ci sa[100], one;</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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)*/</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The differences between fixed- and variable-length arrays are explained in the
|
|
<a href="#arrays">Arrays</a> section.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_nested_structures">Nested structures</h4>
|
|
<div class="paragraph"><p>When dealing with nested structures, the outermost structure uses the <code>S</code> format
|
|
character, and the inner nested structures use the <code>$</code> format. Only the
|
|
<em>outermost</em> structure’s address is given to <code>tpl_map</code>.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct inner_t {
|
|
char a;
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct outer_t {
|
|
char b;
|
|
struct inner_t i;
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tpl_node *tn;
|
|
struct outer_t outer = {'b', {'a'}};</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tn = tpl_map("S(c$(c))", &outer);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_linked_lists">Linked lists</h3>
|
|
<div class="paragraph"><p>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 <code>A(...)</code> 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.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct element {
|
|
char c;
|
|
int i;
|
|
struct element *next;
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>struct element *list, *i, tmp;
|
|
tpl_node *tn;</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>/* add some elements to list.. (not shown)*/</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>Unpacking is similar. The <code>for</code> loop is just replaced with:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>while( tpl_unpack(tn,1) > 0) {
|
|
struct element *newelt = malloc(sizeof(struct element));
|
|
*newelt = tmp;
|
|
add_to_list(list, newelt);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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 <em>pointer swizzling</em> to make this easier.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="_api">API</h2>
|
|
<div class="sectionbody">
|
|
<div class="sect2">
|
|
<h3 id="tpl_map">tpl_map</h3>
|
|
<div class="paragraph"><p>The only way to create a tpl is to call <code>tpl_map()</code>. The first argument is the
|
|
<a href="#format">format string</a>. This is followed by a list of arguments as required by
|
|
the particular characters in the format string. E.g,</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tpl_node *tn;
|
|
int i;
|
|
tn = tpl_map( "A(i)", &i );</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="paragraph"><p>This function returns a <code>tpl_node*</code> on success, or <code>NULL</code> on failure.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_pack">tpl_pack</h3>
|
|
<div class="paragraph"><p>The function <code>tpl_pack()</code> packs data into a tpl. The arguments to
|
|
<code>tpl_pack()</code> are a <code>tpl_node*</code> and an <a href="#index">index number</a>.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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 */</code></pre>
|
|
</div></div>
|
|
<div class="sidebarblock">
|
|
<div class="content">
|
|
<div class="title">Data is copied when packed</div>
|
|
<div class="paragraph"><p>Every call to <code>tpl_pack()</code> immediately <em>copies</em> the data being packed. Thus
|
|
the program is free to immediately overwrite or re-use the packed variables.</p></div>
|
|
</div></div>
|
|
<div class="sect3">
|
|
<h4 id="_index_number_0">Index number 0</h4>
|
|
<div class="paragraph"><p>It is necessary to pack index number 0 only if the format string contains
|
|
characters that are not inside an <code>A(...)</code>, such as the <code>i</code> in the format string
|
|
<code>iA(c)</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_variable_length_arrays">Variable-length arrays</h4>
|
|
<div class="sect4">
|
|
<h5 id="_adding_elements_to_an_array">Adding elements to an array</h5>
|
|
<div class="paragraph"><p>To add elements to a variable-length array, call <code>tpl_pack()</code> repeatedly. Each
|
|
call adds another element to the array.</p></div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="_zero_length_arrays_are_ok">Zero-length arrays are ok</h5>
|
|
<div class="paragraph"><p>It’s perfectly acceptable to pack nothing into a variable-length array,
|
|
resulting in a zero-length array.</p></div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="nested_pack">Packing nested arrays</h5>
|
|
<div class="paragraph"><p>In a format string containing a nested, variable-length array, such as
|
|
<code>A(A(s))</code>, the inner, child array should be packed prior to the parent array.</p></div>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="admonitionblock">
|
|
<table><tr>
|
|
<td class="icon">
|
|
<div class="title">Tip</div>
|
|
</td>
|
|
<td class="content">When dealing with nested arrays like <code>A(A(i))</code>, <em>pack</em> them from the "inside
|
|
out" (child first), but <em>unpack</em> them from the "outside in" (parent first).</td>
|
|
</tr></table>
|
|
</div>
|
|
<div class="paragraph"><p>The example below creates a tpl having the format string <code>A(A(c))</code>.</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Packing nested arrays</div>
|
|
<div class="content">
|
|
<pre><code>#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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>This creates a nested array in which the parent has two elements: the first
|
|
element is the two-element nested array <em>a</em>, <em>b</em>; and the second element is
|
|
the three-element nested array <em>1</em>, <em>2</em>, <em>3</em>.
|
|
The <a href="#nested_unpack">nested unpacking example</a> shows how this tpl is unpacked.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_dump">tpl_dump</h3>
|
|
<div class="paragraph"><p>After packing a tpl, <code>tpl_dump()</code> 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.</p></div>
|
|
<div class="tableblock">
|
|
<table rules="none"
|
|
width="80%"
|
|
frame="border"
|
|
cellspacing="0" cellpadding="4">
|
|
<col width="30%" />
|
|
<col width="70%" />
|
|
<thead>
|
|
<tr>
|
|
<th align="center" valign="top">Write to… </th>
|
|
<th align="left" valign="top">Usage</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">file</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_dump(tn, TPL_FILE, "file.tpl" );</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">file descriptor</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_dump(tn, TPL_FD, 2);</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">memory</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_dump(tn, TPL_MEM, &addr, &len );</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">caller’s memory</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_dump(tn, TPL_MEM|TPL_PREALLOCD, buf, sizeof(buf));</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" valign="top"><p class="table">just get size</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_dump(tn, TPL_GETSIZE, &sz);</code></p></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph"><p>The first argument is the <code>tpl_node*</code> and the second is one of these constants:</p></div>
|
|
<div class="dlist"><dl>
|
|
<dt class="hdlist1">
|
|
<code>TPL_FILE</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Writes the tpl to a file whose name is given in the following argument.
|
|
The file is created with permissions 664 (<code>rw-rw-r--</code>) unless further
|
|
restricted by the process <code>umask</code>.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_FD</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
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.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_MEM</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Writes the tpl to a memory buffer. The following two arguments must be a
|
|
<code>void\*\*</code> and a <code>size_t*</code>. The function will allocate a buffer and store
|
|
its address and length into these locations. The caller is responsible to
|
|
<code>free()</code> the buffer when done using it.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_MEM|TPL_PREALLOCD</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Writes the tpl to a memory buffer that the caller has already allocated or
|
|
declared. The following two arguments must be a <code>void*</code> and a <code>size_t</code>
|
|
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 <code>tpl_load</code> in <code>TPL_EXCESS_OK</code>
|
|
mode, as shown <a href="#excess_ok">here.</a>
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_GETSIZE</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
This special mode does not actually dump the tpl. Instead it places the size
|
|
that the dump <em>would</em> require into the <code>size_t</code> pointed to by the
|
|
following argument.
|
|
</p>
|
|
</dd>
|
|
</dl></div>
|
|
<div class="paragraph"><p>The return value is 0 on success, or -1 on error.</p></div>
|
|
<div class="paragraph"><p>The <code>tpl_dump()</code> function does not free the tpl. Use <code>tpl_free()</code> to release
|
|
the tpl’s resources when done.</p></div>
|
|
<div class="admonitionblock">
|
|
<table><tr>
|
|
<td class="icon">
|
|
<div class="title">Tip</div>
|
|
</td>
|
|
<td class="content">
|
|
<div class="title">Back-to-back tpl images require no delimiter</div>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 <code>tpl_load</code> will read just one at a time
|
|
even if multiple images are contiguous.</td>
|
|
</tr></table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_load">tpl_load</h3>
|
|
<div class="paragraph"><p>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 <code>tpl_map()</code> will be cross-checked
|
|
for equality with the format string stored in the tpl image.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tn = tpl_map( "A(i)", &i );
|
|
tpl_load( tn, TPL_FILE, "demo.tpl" );</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The first argument to <code>tpl_load()</code> is the <code>tpl_node*</code>. The second argument is
|
|
one of the constants:</p></div>
|
|
<div class="dlist"><dl>
|
|
<dt class="hdlist1">
|
|
<code>TPL_FILE</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Loads the tpl from the file named in the following argument. It is also
|
|
possible to bitwise-OR this flag with <code>TPL_EXCESS_OK</code> as explained below.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_MEM</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
Loads the tpl from a memory buffer. The following two arguments must be a
|
|
<code>void*</code> and a <code>size_t</code>, specifying the buffer address and size,
|
|
respectively. The caller must not free the memory buffer until after
|
|
freeing the tpl with <code>tpl_free()</code>. (If the caller wishes to hand over
|
|
responsibility for freeing the memory buffer, so that it’s automatically
|
|
freed along with the tpl when <code>tpl_free()</code> is called, the constant
|
|
<code>TPL_UFREE</code> may be bitwise-OR’d with <code>TPL_MEM</code> to achieve this).
|
|
Furthermore, <code>TPL_MEM</code> may be bitwise-OR’d with <code>TPL_EXCESS_OK</code>, explained
|
|
below.
|
|
</p>
|
|
</dd>
|
|
<dt class="hdlist1">
|
|
<code>TPL_FD</code>
|
|
</dt>
|
|
<dd>
|
|
<p>
|
|
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.
|
|
</p>
|
|
</dd>
|
|
</dl></div>
|
|
<div class="paragraph"><p>During loading, the tpl image will be extensively checked for internal validity.</p></div>
|
|
<div class="paragraph"><p>This function returns 0 on success or -1 on error.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="excess_ok"><code>TPL_EXCESS_OK</code></h4>
|
|
<div class="paragraph"><p>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 <code>TPL_EXCESS_OK</code> can be OR’d with <code>TPL_MEM</code> or <code>TPL_FILE</code>
|
|
to relax this requirement.</p></div>
|
|
<div class="paragraph"><p>A situation where this flag can be useful is in conjunction with <code>tpl_dump</code> in
|
|
the <code>TPL_MEM|TPL_PREALLOCD</code> mode. In this example, the program does not concern
|
|
itself with the actual tpl size as long as <code>LEN</code> is sufficiently large.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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);</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_unpack">tpl_unpack</h3>
|
|
<div class="paragraph"><p>The <code>tpl_unpack()</code> function unpacks data from the tpl. When data is unpacked,
|
|
it is copied to the C program variables originally specified in <code>tpl_map()</code>.
|
|
The first argument to <code>tpl_unpack</code> is the <code>tpl_node*</code> for the tpl and the
|
|
second argument is an <a href="#index">index number</a>.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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 */</code></pre>
|
|
</div></div>
|
|
<div class="sect3">
|
|
<h4 id="_index_number_0_2">Index number 0</h4>
|
|
<div class="paragraph"><p>It is necessary to unpack index number 0 only if the format string contains
|
|
characters that are not inside an <code>A(...)</code>, such as the <code>i</code> in the format string
|
|
<code>iA(c)</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_variable_length_arrays_2">Variable-length arrays</h4>
|
|
<div class="sect4">
|
|
<h5 id="_unpacking_elements_from_an_array">Unpacking elements from an array</h5>
|
|
<div class="paragraph"><p>For variable-length arrays, each call to <code>tpl_unpack()</code> 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 <code>while</code> loop:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>while( tpl_unpack( tn, 1 ) > 0 ) {
|
|
/* got another element */
|
|
}</code></pre>
|
|
</div></div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="_array_length">Array length</h5>
|
|
<div class="paragraph"><p>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 <code>tpl_Alen()</code> to
|
|
get this number.</p></div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="nested_unpack">Unpacking nested arrays</h5>
|
|
<div class="paragraph"><p>In a format string containing a nested variable-length array such as <code>A(A(s))</code>,
|
|
unpack the outer, parent array before unpacking the child array.</p></div>
|
|
<div class="paragraph"><p>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 <code>A(A(c))</code>.</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Unpacking nested arrays</div>
|
|
<div class="content">
|
|
<pre><code>#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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The file <code>test40.tpl</code> is from the <a href="#nested_pack">nested packing example</a>. When
|
|
run, this program prints:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>a b
|
|
1 2 3</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_free">tpl_free</h3>
|
|
<div class="paragraph"><p>The final step for any tpl is to release it using <code>tpl_free()</code>. Its only
|
|
argument is the the <code>tpl_node*</code> to free.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tpl_free( tn );</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>This function does not return a value (it is <code>void</code>).</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_alen">tpl_Alen</h3>
|
|
<div class="paragraph"><p>This function takes a <code>tpl_node*</code> and an index number and returns an <code>int</code>
|
|
specifying the number of elements in the variable-length array.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>num_elements = tpl_Alen(tn, index);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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).</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_peek">tpl_peek</h3>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="sect3">
|
|
<h4 id="_format_peek">Format peek</h4>
|
|
<div class="paragraph"><p>The format string can be obtained
|
|
like this:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>fmt = tpl_peek(TPL_FILE, "file.tpl");
|
|
fmt = tpl_peek(TPL_MEM, addr, sz);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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 <code>NULL</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_array_length_peek">Array length peek</h4>
|
|
<div class="paragraph"><p>The lengths of all fixed-length arrays in the format string can be queried using
|
|
the <code>TPL_FXLENS</code> 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.</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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);
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The <code>TPL_FXLENS</code> mode is mutually exclusive with <code>TPL_DATAPEEK</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_data_peek">Data peek</h4>
|
|
<div class="paragraph"><p>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 <code>A(...)</code> types.
|
|
Suppose the tpl image in <code>file.tpl</code> has the format string <code>siA(i)</code>. Then the
|
|
index 0 format characters are <code>si</code>. This is how to peek at their content:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>char *s;
|
|
int i;
|
|
fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "si", &s, &i);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>Now <code>s</code>, <code>i</code>, and <code>fmt</code> have been populated with data. The caller must
|
|
eventually free <code>fmt</code> and <code>s</code> because they are allocated strings.
|
|
Of course, it works with <code>TPL_MEM</code> as well as <code>TPL_FILE</code>. Notice that
|
|
<code>TPL_DATAPEEK</code> was OR’d with the mode. You can also specify <em>any leading
|
|
portion</em> of the index 0 format if you don’t want to peek at the whole thing:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "file.tpl", "s", &s);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The <code>TPL_DATAPEEK</code> mode is mutually exclusive with <code>TPL_FXLENS</code>.</p></div>
|
|
<div class="sect4">
|
|
<h5 id="_structure_peek">Structure peek</h5>
|
|
<div class="paragraph"><p>Lastly you can peek into <code>S(...)</code> structures in index 0, but omit the
|
|
surrounding <code>S(...)</code> 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 <code>struct.tpl</code> has the format string
|
|
<code>S(si)</code>, you can peek at its data in these ways:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "s", &s);
|
|
fmt = tpl_peek(TPL_FILE | TPL_DATAPEEK, "struct.tpl", "si", &s, &i);</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tpl_jot">tpl_jot</h3>
|
|
<div class="paragraph"><p>This is a quick shortcut for generating a tpl. It can be used instead of the
|
|
usual "map, pack, dump, and free" lifecycle. With <code>tpl_jot</code> all those steps are
|
|
handled for you. It only works for simple formats-- namely, those without
|
|
<code>A(...)</code> in their format string. Here is how it is used:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>char *hello = "hello", *world = "world";
|
|
tpl_jot( TPL_FILE, "file.tpl", "ss", &hello, &world);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>It supports the three standard modes, <code>TPL_FILE</code>, <code>TPL_FD</code> and <code>TPL_MEM</code>.
|
|
It returns -1 on failure (such as a bad format string or error writing the
|
|
file) or 0 on success.</p></div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="hooks">tpl_hook</h3>
|
|
<div class="paragraph"><p>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.</p></div>
|
|
<div class="paragraph"><p>A global structure called <code>tpl_hook</code> encapsulates the hooks. A program can
|
|
reconfigure any hook by specifying an alternative function whose prototype
|
|
matches the default. For example:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>#include "tpl.h"
|
|
extern tpl_hook_t tpl_hook;</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>int main() {
|
|
tpl_hook.oops = printf;
|
|
...
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="tableblock">
|
|
<table rules="none"
|
|
width="90%"
|
|
frame="border"
|
|
cellspacing="0" cellpadding="4">
|
|
<caption class="title">Table 3. Configurable hooks</caption>
|
|
<col width="33%" />
|
|
<col width="33%" />
|
|
<col width="33%" />
|
|
<thead>
|
|
<tr>
|
|
<th align="left" valign="top">Hook </th>
|
|
<th align="left" valign="top">Description </th>
|
|
<th align="left" valign="top"> Default</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.oops</code></p></td>
|
|
<td align="left" valign="top"><p class="table">log error messages</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_oops</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.malloc</code></p></td>
|
|
<td align="left" valign="top"><p class="table">allocate memory</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>malloc</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.realloc</code></p></td>
|
|
<td align="left" valign="top"><p class="table">reallocate memory</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>realloc</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.free</code></p></td>
|
|
<td align="left" valign="top"><p class="table">free memory</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>free</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.fatal</code></p></td>
|
|
<td align="left" valign="top"><p class="table">log fatal message and exit</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_fatal</code></p></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="left" valign="top"><p class="table"><code>tpl_hook.gather_max</code></p></td>
|
|
<td align="left" valign="top"><p class="table">tpl_gather max image size</p></td>
|
|
<td align="left" valign="top"><p class="table"><code>0 (unlimited)</code></p></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_the_oops_hook">The oops hook</h4>
|
|
<div class="paragraph"><p>The <code>oops</code> has the same prototype as <code>printf</code>. The built-in default oops
|
|
handling function writes the error message to <code>stderr</code>.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_the_fatal_hook">The fatal hook</h4>
|
|
<div class="paragraph"><p>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:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>void fatal_fcn(char *fmt, ...);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The <code>fatal</code> hook must not return. It must either exit, <em>or</em> if the program needs
|
|
to handle the failure and keep executing, <code>setjmp</code> and <code>longjmp</code> can be used.
|
|
The default behavior is to <code>exit(-1)</code>.</p></div>
|
|
<div class="listingblock">
|
|
<div class="title">Using longjmp in a fatal error handler</div>
|
|
<div class="content">
|
|
<pre><code>#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;
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>This example is included in <code>tests/test123.c</code>. When run, this program prints:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>unsupported option @
|
|
failed to parse @
|
|
caught error!</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="_tpl_gather">tpl_gather</h3>
|
|
<div class="sidebarblock">
|
|
<div class="content">
|
|
<div class="title">Most programs don’t need this</div>
|
|
<div class="paragraph"><p>Normally, <code>tpl_load()</code> 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
|
|
<code>tpl_gather</code>. 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.</p></div>
|
|
</div></div>
|
|
<div class="paragraph"><p>The prototype for this function is:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>int tpl_gather( int mode, ...);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>The <code>mode</code> argument is one of three constants listed below, which must be
|
|
followed by the mode-specific required arguments:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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</code></pre>
|
|
</div></div>
|
|
<div class="admonitionblock">
|
|
<table><tr>
|
|
<td class="icon">
|
|
<div class="title">Note</div>
|
|
</td>
|
|
<td class="content">
|
|
<div class="title"><code>tpl_hook.gather_max</code></div>All modes honor <code>tpl_hook.gather_max</code>, 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 <code>tpl_gather</code> 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.)</td>
|
|
</tr></table>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_code_tpl_gather_blocking_code"><code>TPL_GATHER_BLOCKING</code></h4>
|
|
<div class="paragraph"><p>In this mode, <code>tpl_gather</code> blocks while reading file descriptor <code>fd</code> 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 <code>img</code> and its size
|
|
is placed in <code>sz</code>. 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.</p></div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_code_tpl_gather_nonblocking_code"><code>TPL_GATHER_NONBLOCKING</code></h4>
|
|
<div class="paragraph"><p>This mode is for non-blocking, event-driven programs that implement their
|
|
own file descriptor readability testing using <code>select()</code> 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 <code>fd</code> 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.</p></div>
|
|
<div class="paragraph"><p>For each file descriptor on which <code>tpl_gather</code> will be used, the caller must
|
|
declare a <code>tpl_gather_t*</code> and initialize it to <code>NULL</code>. Thereafter it will be
|
|
used internally by <code>tpl_gather</code> whenever data is readable on the descriptor.</p></div>
|
|
<div class="paragraph"><p>The callback will only be invoked whenever <code>tpl_gather()</code> has accumulated one
|
|
complete tpl image. It must have this prototype:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>int (tpl_gather_cb)(void *img, size_t sz, void *data);</code></pre>
|
|
</div></div>
|
|
<div class="paragraph"><p>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 <code>tpl_gather</code> 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.</p></div>
|
|
<div class="paragraph"><p>The return value of <code>tpl_gather()</code> 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.</p></div>
|
|
<div class="sect4">
|
|
<h5 id="_typical_usage">Typical Usage</h5>
|
|
<div class="paragraph"><p>The program will have established a file descriptor in non-blocking mode and
|
|
be monitoring it for readability, using <code>select()</code>. Whenever it’s readable, the
|
|
program calls <code>tpl_gather()</code>. In skeletal terms:</p></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>tpl_gather_t *gt=NULL;
|
|
int rc;</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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);
|
|
}
|
|
}</code></pre>
|
|
</div></div>
|
|
<div class="literalblock">
|
|
<div class="content">
|
|
<pre><code>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) */
|
|
}</code></pre>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="_code_tpl_gather_mem_code"><code>TPL_GATHER_MEM</code></h4>
|
|
<div class="paragraph"><p>This mode is identical to <code>TPL_GATHER_NONBLOCKING</code> 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 <code>TPL_GATHER_NONBLOCKING</code> mode except that instead of a file
|
|
descriptor, it takes a buffer address and size. The return values are also the
|
|
same as for <code>TPL_GATHER_NONBLOCKING</code> noting of course there is no file
|
|
descriptor to close on a non-positive return value.</p></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="footnotes"><hr /></div>
|
|
<div id="footer">
|
|
<div id="footer-text">
|
|
Version 1.5<br />
|
|
Last updated 2013-03-12 17:19:43 EDT
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|