diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1550b3b..0000000 --- a/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -scratch/ -*.o -server/messages.h -server/messages.c -server/cmsg -server/test1 -server/test3 -server/test1_latency -server/test3_latency -depend.mk diff --git a/amqp0-9-1.stripped.xml b/amqp0-9-1.stripped.xml deleted file mode 100644 index 762519b..0000000 --- a/amqp0-9-1.stripped.xml +++ /dev/null @@ -1,459 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/Sexp.txt b/doc/Sexp.txt deleted file mode 100644 index caf4542..0000000 --- a/doc/Sexp.txt +++ /dev/null @@ -1,699 +0,0 @@ -Network Working Group R. Rivest -Internet Draft May 4, 1997 -Expires November 4, 1997 - - - S-Expressions - draft-rivest-sexp-00.txt - - -Status of this Memo - - Distribution of this memo is unlimited. - - This document is an Internet-Draft. Internet Drafts are working - documents of the Internet Engineering Task Force (IETF), its Areas, - and its Working Groups. Note that other groups may also distribute - working documents as Internet Drafts. - - Internet Drafts are draft documents valid for a maximum of six - months, and may be updated, replaced, or obsoleted by other documents - at any time. It is not appropriate to use Internet Drafts as - reference material, or to cite them other than as a ``working draft'' - or ``work in progress.'' - - To learn the current status of any Internet-Draft, please check the - ``1id-abstracts.txt'' listing contained in the internet-drafts Shadow - Directories on: ftp.is.co.za (Africa), nic.nordu.net (Europe), - ds.internic.net (US East Coast), ftp.isi.edu (US West Coast), - or munnari.oz.au (Pacific Rim) - - -Abstract - -This memo describes a data structure called "S-expressions" that are -suitable for representing arbitrary complex data structures. We make -precise the encodings of S-expressions: we give a "canonical form" for -S-expressions, described two "transport" representations, and also -describe an "advanced" format for display to people. - - - -1. Introduction - -S-expressions are data structures for representing complex data. They -are either byte-strings ("octet-strings") or lists of simpler -S-expressions. Here is a sample S-expression: - - (snicker "abc" (#03# |YWJj|)) - -It is a list of length three: - - -- the octet-string "snicker" - - -- the octet-string "abc" - - -- a sub-list containing two elements: - - the hexadecimal constant #03# - - the base-64 constant |YWJj| (which is the same as "abc") - -This note gives a specific proposal for constructing and utilizing -S-expressions. The proposal is independent of any particular application. - -Here are the design goals for S-expressions: - - -- generality: S-expressions should be good at representing arbitrary - data. - - -- readability: it should be easy for someone to examine and - understand the structure of an S-expression. - - -- economy: S-expressions should represent data compactly. - - -- tranportability: S-expressions should be easy to transport - over communication media (such as email) that are known to be - less than perfect. - - -- flexibility: S-expressions should make it relatively simple to - modify and extend data structures. - - -- canonicalization: it should be easy to produce a unique - "canonical" form of an S-expression, for digital signature purposes. - - -- efficiency: S-expressions should admit in-memory representations - that allow efficient processing. - - -Section 2 gives an introduction to S-expressions. -Section 3 discusses the character sets used. -Section 4 presents the various representations of octet-strings. -Section 5 describes how to represent lists. -Section 6 discusses how S-expressions are represented for various uses. -Section 7 gives a BNF syntax for S-expressions. -Section 8 talks about how S-expressions might be represented in memory. -Section 9 briefly describes implementations for handling S-expressions. -Section 10 discusses how applications might utilize S-expressions. -Section 11 gives historical notes on S-expressions. -Section 12 gives references. - -2. S-expressions -- informal introduction - -Informally, an S-expression is either: - -- an octet-string, or - -- a finite list of simpler S-expressions. - -An octet-string is a finite sequence of eight-bit octets. There may be -many different but equivalent ways of representing an octet-string - - abc -- as a token - - "abc" -- as a quoted string - - #616263# -- as a hexadecimal string - - 3:abc -- as a length-prefixed "verbatim" encoding - - {MzphYmM=} -- as a base-64 encoding of the verbatim encoding - (that is, an encoding of "3:abc") - - |YWJj| -- as a base-64 encoding of the octet-string "abc" - -These encodings are all equivalent; they all denote the same octet string. - -We will give details of these encodings later on, and also describe how to -give a "display type" to a byte string. - -A list is a finite sequence of zero or more simpler S-expressions. A list -may be represented by using parentheses to surround the sequence of encodings -of its elements, as in: - - (abc (de #6667#) "ghi jkl") - -As we see, there is variability possible in the encoding of an -S-expression. In some cases, it is desirable to standardize or -restrict the encodings; in other cases it is desirable to have no -restrictions. The following are the target cases we aim to handle: - - -- a "transport" encoding for transporting the S-expression between - computers. - - -- a "canonical" encoding, used when signing the S-expression. - - -- an "advanced" encoding used for input/output to people. - - -- an "in-memory" encoding used for processing the S-expression in - the computer. - -These need not be different; in this proposal the canonical encoding -is the same as the transport encoding, for example. In this note we -propose (related) encoding techniques for each of these uses. - -3. Character set - -We will be describing encodings of S-expressions. Except when giving -"verbatim" encodings, the character set used is limited to the following -characters in US-ASCII: - Alphabetic: A B ... Z a b ... z - numeric: 0 1 ... 9 - whitespace: space, horizontal tab, vertical tab, form-feed - carriage-return, line-feed - The following graphics characters, which we call "pseudo-alphabetic": - - hyphen or minus - . period - / slash - _ underscore - : colon - * asterisk - + plus - = equal - The following graphics characters, which are "reserved punctuation": - ( left parenthesis - ) right parenthesis - [ left bracket - ] right bracket - { left brace - } right brace - | vertical bar - # number sign - " double quote - & ampersand - \ backslash - The following characters are unused and unavailable, except in - "verbatim" encodings: - ! exclamation point - % percent - ^ circumflex - ~ tilde - ; semicolon - ' apostrophe - , comma - < less than - > greater than - ? question mark - - -4. Octet string representations - -This section describes in detail the ways in which an octet-string may -be represented. - -We recall that an octet-string is any finite sequence of octets, and -that the octet-string may have length zero. - - -4.1 Verbatim representation - -A verbatim encoding of an octet string consists of four parts: - - -- the length (number of octets) of the octet-string, - given in decimal most significant digit first, with - no leading zeros. - - -- a colon ":" - - -- the octet string itself, verbatim. - -There are no blanks or whitespace separating the parts. No "escape -sequences" are interpreted in the octet string. This encoding is also -called a "binary" or "raw" encoding. - -Here are some sample verbatim encodings: - - 3:abc - 7:subject - 4::::: - 12:hello world! - 10:abcdefghij - 0: - -4.2 Quoted-string representation - -The quoted-string representation of an octet-string consists of: - - -- an optional decimal length field - - -- an initial double-quote (") - - -- the octet string with "C" escape conventions (\n,etc) - - -- a final double-quote (") - -The specified length is the length of the resulting string after any -escape sequences have been handled. The string does not have any -"terminating NULL" that C includes, and the length does not count such -a character. - -The length is optional. - -The escape conventions within the quoted string are as follows (these follow -the "C" programming language conventions, with an extension for -ignoring line terminators of just LF or CRLF): - \b -- backspace - \t -- horizontal tab - \v -- vertical tab - \n -- new-line - \f -- form-feed - \r -- carriage-return - \" -- double-quote - \' -- single-quote - \\ -- back-slash - \ooo -- character with octal value ooo (all three digits - must be present) - \xhh -- character with hexadecimal value hh (both digits - must be present) - \ -- causes carriage-return to be ignored. - \ -- causes linefeed to be ignored - \ -- causes CRLF to be ignored. - \ -- causes LFCR to be ignored. - -Here are some examples of quoted-string encodings: - - "subject" - "hi there" - 7"subject" - 3"\n\n\n" - "This has\n two lines." - "This has\ - one." - "" - -4.3 Token representation - -An octet string that meets the following conditions may be given -directly as a "token". - - -- it does not begin with a digit - - -- it contains only characters that are - -- alphabetic (upper or lower case), - -- numeric, or - -- one of the eight "pseudo-alphabetic" punctuation marks: - - . / _ : * + = - (Note: upper and lower case are not equivalent.) - (Note: A token may begin with punctuation, including ":"). - -Here are some examples of token representations: - - subject - not-before - class-of-1997 - //microsoft.com/names/smith - * - - -4.4 Hexadecimal representation - -An octet-string may be represented with a hexadecimal encoding consisting of: - - -- an (optional) decimal length of the octet string - - -- a sharp-sign "#" - - -- a hexadecimal encoding of the octet string, with each octet - represented with two hexadecimal digits, most significant - digit first. - - -- a sharp-sign "#" - -There may be whitespace inserted in the midst of the hexadecimal -encoding arbitrarily; it is ignored. It is an error to have -characters other than whitespace and hexadecimal digits. - -Here are some examples of hexadecimal encodings: - - #616263# -- represents "abc" - 3#616263# -- also represents "abc" - # 616 - 263 # -- also represents "abc" - - -4.5 Base-64 representation - -An octet-string may be represented in a base-64 coding consisting of: - - -- an (optional) decimal length of the octet string - - -- a vertical bar "|" - - -- the rfc 1521 base-64 encoding of the octet string. - - -- a final vertical bar "|" - -The base-64 encoding uses only the characters - A-Z a-z 0-9 + / = -It produces four characters of output for each three octets of input. -If the input has one or two left-over octets of input, it produces an -output block of length four ending in two or one equals signs, respectively. -Output routines compliant with this standard MUST output the equals signs -as specified. Input routines MAY accept inputs where the equals signs are -dropped. - -There may be whitespace inserted in the midst of the base-64 encoding -arbitrarily; it is ignored. It is an error to have characters other -than whitespace and base-64 characters. - -Here are some examples of base-64 encodings: - - |YWJj| -- represents "abc" - | Y W - J j | -- also represents "abc" - 3|YWJj| -- also represents "abc" - |YWJjZA==| -- represents "abcd" - |YWJjZA| -- also represents "abcd" - - -4.6 Display hint - -Any octet string may be preceded by a single "display hint". - -The purposes of the display hint is to provide information on how -to display the octet string to a user. It has no other function. -Many of the MIME types work here. - -A display-hint is an octet string surrounded by square brackets. -There may be whitespace separating the octet string from the -surrounding brackets. Any of the legal formats may be used for the -octet string. - -Here are some examples of display-hints: - - [image/gif] - [URI] - [charset=unicode-1-1] - [text/richtext] - [application/postscript] - [audio/basic] - ["http://abc.com/display-types/funky.html"] - -In applications an octet-string that is untyped may be considered to have -a pre-specified "default" mime type. The mime type - "text/plain; charset=iso-8859-1" -is the standard default. - - -4.7 Equality of octet-strings - -Two octet strings are considered to be "equal" if and only if they -have the same display hint and the same data octet strings. - -Note that octet-strings are "case-sensitive"; the octet-string "abc" -is not equal to the octet-string "ABC". - -An untyped octet-string can be compared to another octet-string (typed -or not) by considering it as a typed octet-string with the default -mime-type. - - -5. Lists - -Just as with octet-strings, there are several ways to represent an -S-expression. Whitespace may be used to separate list elements, but -they are only required to separate two octet strings when otherwise -the two octet strings might be interpreted as one, as when one token -follows another. Also, whitespace may follow the initial left -parenthesis, or precede the final right parenthesis. - -Here are some examples of encodings of lists: - - (a b c) - - ( a ( b c ) ( ( d e ) ( e f ) ) ) - - (11:certificate(6:issuer3:bob)(7:subject5:alice)) - - ({3Rt=} "1997" murphy 3:{XC++}) - - -6. Representation types - -There are three "types" of representations: - - -- canonical - - -- basic transport - - -- advanced transport - -The first two MUST be supported by any implementation; the last is -optional. - - -6.1 Canonical representation - -This canonical representation is used for digital signature purposes, -transmission, etc. It is uniquely defined for each S-expression. It -is not particularly readable, but that is not the point. It is -intended to be very easy to parse, to be reasonably economical, and to -be unique for any S-expression. - -The "canonical" form of an S-expression represents each octet-string -in verbatim mode, and represents each list with no blanks separating -elements from each other or from the surrounding parentheses. - -Here are some examples of canonical representations of S-expressions: - - (6:issuer3:bob) - - (4:icon[12:image/bitmap]9:xxxxxxxxx) - - (7:subject(3:ref5:alice6:mother)) - - -6.2 Basic transport representation - -There are two forms of the "basic transport" representation: - - -- the canonical representation - - -- an rfc-2045 base-64 representation of the canonical representation, - surrounded by braces. - -The transport mechanism is intended to provide a universal means of -representing S-expressions for transport from one machine to another. - -Here are some examples of an S-expression represented in basic -transport mode: - - (1:a1:b1:c) - - {KDE6YTE6YjE6YykA} - - (this is the same S-expression encoded in base-64) - -There is a difference between the brace notation for base-64 used here -and the || notation for base-64'd octet-strings described above. Here -the base-64 contents are converted to octets, and then re-scanned as -if they were given originally as octets. With the || notation, the -contents are just turned into an octet-string. - - -6.3 Advanced transport representation - -The "advanced transport" representation is intended to provide more -flexible and readable notations for documentation, design, debugging, -and (in some cases) user interface. - -The advanced transport representation allows all of the representation -forms described above, include quoted strings, base-64 and hexadecimal -representation of strings, tokens, representations of strings with -omitted lengths, and so on. - - -7. BNF for syntax - -We give separate BNF's for canonical and advanced forms of S-expressions. -We use the following notation: - * means 0 or more occurrences of - + means 1 or more occurrences of - ? means 0 or 1 occurrences of - parentheses are used for grouping, as in ( | )* - -For canonical and basic transport: - - :: | - :: ? ; - :: ; - :: "[" "]" ; - :: ":" ; - :: + ; - -- decimal numbers should have no unnecessary leading zeros - -- any string of bytes, of the indicated length - :: "(" * ")" ; - :: "0" | ... | "9" ; - -For advanced transport: - - :: | - :: ? ; - :: | | | | - ; - :: "[" "]" ; - :: ":" ; - :: + ; - -- decimal numbers should have no unnecessary leading zeros - -- any string of bytes, of the indicated length - :: + ; - :: ? "|" ( | )* "|" ; - :: "#" ( | )* "#" ; - :: ? - :: "\"" "\"" - :: "(" ( | )* ")" ; - :: * ; - :: | | ; - :: | | ; - :: "a" | ... | "z" ; - :: "A" | ... | "Z" ; - :: "0" | ... | "9" ; - :: | "A" | ... | "F" | "a" | ... | "f" ; - :: "-" | "." | "/" | "_" | ":" | "*" | "+" | "=" ; - :: " " | "\t" | "\r" | "\n" ; - :: | | "+" | "/" | "=" ; - :: "" ; - -8. In-memory representations - -For processing, the S-expression would typically be parsed and represented -in memory in a more more amenable to efficient processing. We suggest -two alternatives: - - -- "list-structure" - - -- "array-layout" - -We only sketch these here, as they are only suggestive. The code referenced -below illustrates these styles in more detail. - - -8.1. List-structure memory representation - -Here there are separate records for simple-strings, strings, and -lists. An S-expression of the form ("abc" "de") would require two -records for the simple strings, two for the strings, and two for the -list elements. This is a fairly conventional representation, and -details are omitted here. - -8.2 Array-layout memory representation - -Here each S-expression is represented as a contiguous array of bytes. -The first byte codes the "type" of the S-expression: - - 01 octet-string - - 02 octet-string with display-hint - - 03 beginning of list (and 00 is used for "end of list") - -Each of the three types is immediately followed by a k-byte integer -indicating the size (in bytes) of the following representation. Here -k is an integer that depends on the implementation, it might be -anywhere from 2 to 8, but would be fixed for a given implementation; -it determines the size of the objects that can be handled. The transport -and canonical representations are independent of the choice of k made by -the implementation. - -Although the length of lists are not given in the usual S-expression -notations, it is easy to fill them in when parsing; when you reach a -right-parenthesis you know how long the list representation was, and -where to go back to fill in the missing length. - - -8.2.1 Octet string - -This is represented as follows: - - 01 - -For example (here k = 2) - - 01 0003 a b c - -8.2.2 Octet-string with display-hint - -This is represented as follows: - - 02 - 01 /* for display-type */ - 01 /* for octet-string */ - -For example, the S-expression - - [gif] #61626364# - -would be represented as (with k = 2) - - 02 000d - 01 0003 g i f - 01 0004 61 62 63 64 - -8.2.3 List - -This is represented as - - 03 ... 00 - -For example, the list (abc [d]ef (g)) is represented in memory as (with k=2) - - 03 001b - 01 0003 a b c - 02 0009 - 01 0001 d - 01 0002 e f - 03 0005 - 01 0001 g - 00 - 00 - -9. Code - -There is code available for reading and parsing the various -S-expression formats proposed here. - -See http://theory.lcs.mit.edu/~rivest/sexp.html - - -10. Utilization of S-expressions - -This note has described S-expressions in general form. Application writers -may wish to restrict their use of S-expressions in various ways. Here are -some possible restrictions that might be considered: - - -- no display-hints - -- no lengths on hexadecimal, quoted-strings, or base-64 encodings - -- no empty lists - -- no empty octet-strings - -- no lists having another list as its first element - -- no base-64 or hexadecimal encodings - -- fixed limits on the size of octet-strings - -11. Historical note - -The S-expression technology described here was originally developed -for ``SDSI'' (the Simple Distributed Security Infrastructure by -Lampson and Rivest [SDSI]) in 1996, although the origins clearly date -back to McCarthy's LISP programming language. It was further refined -and improved during the merger of SDSI and SPKI [SPKI] during the -first half of 1997. S-expressions are similar to, but more readable -and flexible than, Bernstein's "net-strings" [BERN]. - -12. References - -[SDSI] "A Simple Distributed Security Architecture", by - Butler Lampson, and Ronald L. Rivest - http://theory.lcs.mit.edu/~cis/sdsi.html - -[SPKI] SPKI--A - Simple Public Key Infrastructure - -[BERN] Dan Bernstein's "net-strings"; Internet Draft - draft-bernstein-netstrings-02.txt - -Author's Address - - Ronald L. Rivest - Room 324, 545 Technology Square - MIT Laboratory for Computer Science - Cambridge, MA 02139 - - rivest@theory.lcs.mit.edu - - diff --git a/experiments/cmsg/.gitignore b/experiments/cmsg/.gitignore new file mode 100644 index 0000000..8c0763f --- /dev/null +++ b/experiments/cmsg/.gitignore @@ -0,0 +1,5 @@ +*.o +messages.h +messages.c +cmsg +depend.mk diff --git a/server/Makefile b/experiments/cmsg/Makefile similarity index 85% rename from server/Makefile rename to experiments/cmsg/Makefile index dda4a5a..d225937 100644 --- a/server/Makefile +++ b/experiments/cmsg/Makefile @@ -25,10 +25,10 @@ $(TARGET): $(OBJECTS) %.o: %.c $(CC) $(CFLAGS) -c $< -messages.c: messages.json codegen.py +messages.c: ../../protocol/messages.json codegen.py python codegen.py body > $@ -messages.h: messages.json codegen.py +messages.h: ../../protocol/messages.json codegen.py python codegen.py header > $@ clean: @@ -36,7 +36,6 @@ clean: rm -f $(OBJECTS) rm -rf *.dSYM rm -f depend.mk messages.c messages.h - rm -f test1 test3 test1.o test3.o test1_latency test3_latency test1_latency.o test3_latency.o depend.mk: touch messages.h diff --git a/TODO b/experiments/cmsg/TODO similarity index 100% rename from TODO rename to experiments/cmsg/TODO diff --git a/server/cmsg_private.h b/experiments/cmsg/cmsg_private.h similarity index 100% rename from server/cmsg_private.h rename to experiments/cmsg/cmsg_private.h diff --git a/server/codegen.py b/experiments/cmsg/codegen.py similarity index 96% rename from server/codegen.py rename to experiments/cmsg/codegen.py index 0810bd1..f982fef 100644 --- a/server/codegen.py +++ b/experiments/cmsg/codegen.py @@ -21,8 +21,8 @@ class MessageType: def format_args(self, template, separator = ', '): return separator.join([template % (x,) for x in self.argnames]) -with file("messages.json") as f: - spec = map(MessageType, json.load(f)) +with file("../../protocol/messages.json") as f: + spec = map(MessageType, json.load(f)['definitions']) def entrypoint_header(): print copyright_stmt diff --git a/server/dataq.c b/experiments/cmsg/dataq.c similarity index 100% rename from server/dataq.c rename to experiments/cmsg/dataq.c diff --git a/server/dataq.h b/experiments/cmsg/dataq.h similarity index 100% rename from server/dataq.h rename to experiments/cmsg/dataq.h diff --git a/server/direct.c b/experiments/cmsg/direct.c similarity index 100% rename from server/direct.c rename to experiments/cmsg/direct.c diff --git a/server/direct.h b/experiments/cmsg/direct.h similarity index 100% rename from server/direct.h rename to experiments/cmsg/direct.h diff --git a/server/fanout.c b/experiments/cmsg/fanout.c similarity index 100% rename from server/fanout.c rename to experiments/cmsg/fanout.c diff --git a/server/fanout.h b/experiments/cmsg/fanout.h similarity index 100% rename from server/fanout.h rename to experiments/cmsg/fanout.h diff --git a/server/harness.c b/experiments/cmsg/harness.c similarity index 100% rename from server/harness.c rename to experiments/cmsg/harness.c diff --git a/server/harness.h b/experiments/cmsg/harness.h similarity index 100% rename from server/harness.h rename to experiments/cmsg/harness.h diff --git a/server/hashtable.c b/experiments/cmsg/hashtable.c similarity index 100% rename from server/hashtable.c rename to experiments/cmsg/hashtable.c diff --git a/server/hashtable.h b/experiments/cmsg/hashtable.h similarity index 100% rename from server/hashtable.h rename to experiments/cmsg/hashtable.h diff --git a/server/main.c b/experiments/cmsg/main.c similarity index 100% rename from server/main.c rename to experiments/cmsg/main.c diff --git a/server/meta.c b/experiments/cmsg/meta.c similarity index 100% rename from server/meta.c rename to experiments/cmsg/meta.c diff --git a/server/meta.h b/experiments/cmsg/meta.h similarity index 100% rename from server/meta.h rename to experiments/cmsg/meta.h diff --git a/experiments/direct_scheduling.patch b/experiments/cmsg/misc/direct_scheduling.patch similarity index 100% rename from experiments/direct_scheduling.patch rename to experiments/cmsg/misc/direct_scheduling.patch diff --git a/t0 b/experiments/cmsg/misc/t0 similarity index 100% rename from t0 rename to experiments/cmsg/misc/t0 diff --git a/t1 b/experiments/cmsg/misc/t1 similarity index 100% rename from t1 rename to experiments/cmsg/misc/t1 diff --git a/t2 b/experiments/cmsg/misc/t2 similarity index 100% rename from t2 rename to experiments/cmsg/misc/t2 diff --git a/t4 b/experiments/cmsg/misc/t4 similarity index 100% rename from t4 rename to experiments/cmsg/misc/t4 diff --git a/t5 b/experiments/cmsg/misc/t5 similarity index 100% rename from t5 rename to experiments/cmsg/misc/t5 diff --git a/t6 b/experiments/cmsg/misc/t6 similarity index 100% rename from t6 rename to experiments/cmsg/misc/t6 diff --git a/server/net.c b/experiments/cmsg/net.c similarity index 100% rename from server/net.c rename to experiments/cmsg/net.c diff --git a/server/net.h b/experiments/cmsg/net.h similarity index 100% rename from server/net.h rename to experiments/cmsg/net.h diff --git a/server/node.c b/experiments/cmsg/node.c similarity index 100% rename from server/node.c rename to experiments/cmsg/node.c diff --git a/server/node.h b/experiments/cmsg/node.h similarity index 100% rename from server/node.h rename to experiments/cmsg/node.h diff --git a/server/queue.c b/experiments/cmsg/queue.c similarity index 100% rename from server/queue.c rename to experiments/cmsg/queue.c diff --git a/server/queue.h b/experiments/cmsg/queue.h similarity index 100% rename from server/queue.h rename to experiments/cmsg/queue.h diff --git a/server/ref.h b/experiments/cmsg/ref.h similarity index 100% rename from server/ref.h rename to experiments/cmsg/ref.h diff --git a/server/relay.c b/experiments/cmsg/relay.c similarity index 100% rename from server/relay.c rename to experiments/cmsg/relay.c diff --git a/server/relay.h b/experiments/cmsg/relay.h similarity index 100% rename from server/relay.h rename to experiments/cmsg/relay.h diff --git a/server/sexp.c b/experiments/cmsg/sexp.c similarity index 100% rename from server/sexp.c rename to experiments/cmsg/sexp.c diff --git a/server/sexp.h b/experiments/cmsg/sexp.h similarity index 100% rename from server/sexp.h rename to experiments/cmsg/sexp.h diff --git a/server/sexpio.c b/experiments/cmsg/sexpio.c similarity index 100% rename from server/sexpio.c rename to experiments/cmsg/sexpio.c diff --git a/server/sexpio.h b/experiments/cmsg/sexpio.h similarity index 100% rename from server/sexpio.h rename to experiments/cmsg/sexpio.h diff --git a/server/subscription.c b/experiments/cmsg/subscription.c similarity index 100% rename from server/subscription.c rename to experiments/cmsg/subscription.c diff --git a/server/subscription.h b/experiments/cmsg/subscription.h similarity index 100% rename from server/subscription.h rename to experiments/cmsg/subscription.h diff --git a/server/util.c b/experiments/cmsg/util.c similarity index 100% rename from server/util.c rename to experiments/cmsg/util.c diff --git a/lisp/main.lisp b/experiments/lisp/main.lisp similarity index 100% rename from lisp/main.lisp rename to experiments/lisp/main.lisp diff --git a/lisp/network.lisp b/experiments/lisp/network.lisp similarity index 100% rename from lisp/network.lisp rename to experiments/lisp/network.lisp diff --git a/lisp/packages.lisp b/experiments/lisp/packages.lisp similarity index 100% rename from lisp/packages.lisp rename to experiments/lisp/packages.lisp diff --git a/lisp/sexp.lisp b/experiments/lisp/sexp.lisp similarity index 100% rename from lisp/sexp.lisp rename to experiments/lisp/sexp.lisp diff --git a/server/messages.json b/server/messages.json deleted file mode 100644 index 0397e1d..0000000 --- a/server/messages.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "selector": "create", - "args": ["classname", "arg", "reply-sink", "reply-name"] - }, - { - "selector": "create-ok", - "args": ["info"] - }, - { - "selector": "create-failed", - "args": ["reason"] - }, - { - "selector": "subscribed", - "args": ["source", "filter", "sink", "name"] - }, - { - "selector": "unsubscribed", - "args": ["source", "filter", "sink", "name"] - }, - { - "selector": "post", - "args": ["name", "body", "token"] - }, - { - "selector": "subscribe", - "args": ["filter", "sink", "name", "reply_sink", "reply_name"] - }, - { - "selector": "subscribe-ok", - "args": ["token"] - }, - { - "selector": "unsubscribe", - "args": ["token"] - }, - { - "selector": "error", - "args": ["message", "details"] - } -] diff --git a/server/test1.c b/server/test1.c deleted file mode 100644 index 0beab33..0000000 --- a/server/test1.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -int main(int argc, char *argv[]) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in s; - FILE *f; - struct timeval start_time; - long bytecount = -1; - long last_report_bytecount = 0; - - if (argc < 2) { - fprintf(stderr, "Usage: test1 \n"); - exit(1); - } - - { - struct hostent *h = gethostbyname(argv[1]); - if (h == NULL) { - fprintf(stderr, "serverhostname lookup: %d\n", h_errno); - exit(1); - } - s.sin_family = AF_INET; - s.sin_addr.s_addr = * (uint32_t *) h->h_addr_list[0]; - s.sin_port = htons(5671); - } - - if (connect(fd, (struct sockaddr *) &s, sizeof(s)) < 0) return 1; - - { - int i = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i)); - } - - f = fdopen(fd, "a+"); - - fprintf(f, "(9:subscribe5:test10:0:5:test15:login)(4:post7:factory(6:create5:queue(2:q1)5:test11:k)0:)(4:post2:q1(9:subscribe0:5:test18:consumer5:test11:k)0:)\n"); - fflush(f); - -#define MESSAGESIZE 65 - - while (1) { - char buf[1024]; - size_t n = read(fd, buf, sizeof(buf)); - if (n == 0) break; - if (n >= 16) { - if (!memcmp(buf, "(4:post8:consumer", 16)) { - if (bytecount == -1) { - printf("Buffer at start: <<%.*s>>\n", (int) n, buf); - printf("Starting.\n"); - bytecount = 0; - gettimeofday(&start_time, NULL); - } - } - } - if (bytecount >= 0) { - bytecount += n; - if ((bytecount - last_report_bytecount) > (100000 * MESSAGESIZE)) { - struct timeval now; - double delta; - gettimeofday(&now, NULL); - delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0; - printf("So far received %ld bytes in %g seconds = %g bytes/sec and %g msgs/sec\n", - bytecount, - delta, - bytecount / delta, - bytecount / (delta * MESSAGESIZE)); - fflush(stdout); - last_report_bytecount = bytecount; - } - } - } - - return 0; -} diff --git a/server/test1_latency.c b/server/test1_latency.c deleted file mode 100644 index fb7d2f9..0000000 --- a/server/test1_latency.c +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define EXPECTEDPREFIX "(4:post8:consumer8:" - -static size_t hunt_for_latencies_in(char *buf, size_t count) { - struct timeval now; - char *pos = buf; - char *sentinel = buf + count; - size_t msgsize = 0; - - gettimeofday(&now, NULL); - - while (1) { - char *openptr = memchr(pos, '(', sentinel - pos); - char *closeptr; - uint32_t s, us; - - if (openptr == NULL) break; - - closeptr = memchr(openptr + 1, ')', sentinel - (openptr + 1)); - if (closeptr == NULL) break; - - memcpy(&s, openptr + strlen(EXPECTEDPREFIX), sizeof(uint32_t)); - memcpy(&us, openptr + strlen(EXPECTEDPREFIX) + sizeof(uint32_t), sizeof(uint32_t)); - s = ntohl(s); - us = ntohl(us); - - if (s != 0 || us != 0) { - double delta = (now.tv_sec - s) * 1000000.0 + (now.tv_usec - us); - printf("Latency %g microseconds (%g milliseconds)\n", delta, delta / 1000.0); - } - - msgsize = closeptr + 1 - openptr; - - pos = closeptr + 1; - } - - return msgsize; -} - -int main(int argc, char *argv[]) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in s; - FILE *f; - struct timeval start_time; - long bytecount = -1; - size_t message_size = 0; - long last_report_bytecount = 0; - char idchar = '1'; - char *qclass = "queue"; - - if (argc < 2) { - fprintf(stderr, "Usage: test1 [ []]\n"); - exit(1); - } - - if (argc > 2) { - idchar = argv[2][0]; - } - printf("Idchar: '%c'\n", idchar); - - if (argc > 3) { - qclass = argv[3]; - } - printf("Qclass: %s\n", qclass); - - { - struct hostent *h = gethostbyname(argv[1]); - if (h == NULL) { - fprintf(stderr, "serverhostname lookup: %d\n", h_errno); - exit(1); - } - s.sin_family = AF_INET; - s.sin_addr.s_addr = * (uint32_t *) h->h_addr_list[0]; - s.sin_port = htons(5671); - } - - if (connect(fd, (struct sockaddr *) &s, sizeof(s)) < 0) return 1; - - { - int i = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i)); - } - - f = fdopen(fd, "a+"); - - fprintf(f, "(9:subscribe5:test%c0:0:5:test%c5:login)(4:post7:factory(6:create%d:%s(2:q1)5:test%c1:k)0:)(4:post2:q1(9:subscribe0:5:test%c8:consumer5:test%c1:k)0:)\n", - idchar, idchar, (int) strlen(qclass), qclass, idchar, idchar, idchar); - fflush(f); - - while (1) { - char buf[1024]; - size_t n = read(fd, buf, sizeof(buf)); - if (n == 0) break; - if (n >= strlen(EXPECTEDPREFIX)) { - if (!memcmp(buf, EXPECTEDPREFIX, strlen(EXPECTEDPREFIX))) { - if (bytecount == -1) { - printf("Buffer at start: <<%.*s>>\n", (int) n, buf); - printf("Starting.\n"); - bytecount = 0; - gettimeofday(&start_time, NULL); - } - } - } - if (bytecount >= 0) { - size_t detected_msgsize = hunt_for_latencies_in(buf, n); - bytecount += n; - if (detected_msgsize != 0 && message_size == 0) { - message_size = detected_msgsize; - printf("message_size = %lu\n", message_size); - } - if (message_size != 0) { - if ((bytecount - last_report_bytecount) > (100000 * message_size)) { - struct timeval now; - double delta; - gettimeofday(&now, NULL); - delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0; - printf("So far received %ld bytes in %g seconds = %g bytes/sec and %g msgs/sec\n", - bytecount, - delta, - bytecount / delta, - bytecount / (delta * message_size)); - fflush(stdout); - last_report_bytecount = bytecount; - } - } - } - } - - return 0; -} diff --git a/server/test3.c b/server/test3.c deleted file mode 100644 index 48a2c59..0000000 --- a/server/test3.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -int main(int argc, char *argv[]) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in s; - FILE *f; - struct timeval start_time; - long bytecount = 0; - char const *msg = "(4:post2:q1(4:post0:6:XXXXXX0:)0:)"; - size_t msglen = strlen(msg); - int i; - - if (argc < 2) { - fprintf(stderr, "Usage: test1 \n"); - exit(1); - } - - { - struct hostent *h = gethostbyname(argv[1]); - if (h == NULL) { - fprintf(stderr, "serverhostname lookup: %d\n", h_errno); - exit(1); - } - s.sin_family = AF_INET; - s.sin_addr.s_addr = * (uint32_t *) h->h_addr_list[0]; - s.sin_port = htons(5671); - } - - if (connect(fd, (struct sockaddr *) &s, sizeof(s)) < 0) return 1; - - { - int i = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i)); - } - - f = fdopen(fd, "a+"); - - fprintf(f, "(9:subscribe5:test30:0:5:test35:login)"); - fflush(f); - - usleep(100000); - { - char buf[4096]; - size_t n = read(fd, buf, sizeof(buf)); - printf("Received: <<%.*s>>\n", (int) n, buf); - } - - gettimeofday(&start_time, NULL); - - for (i = 0; i < 10000000; i++) { - fwrite(msg, msglen, 1, f); - bytecount += msglen; - if ((bytecount % 100000) < msglen) { - struct timeval now; - double delta; - gettimeofday(&now, NULL); - delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0; - printf("So far sent %ld bytes in %g seconds = %g bytes/sec and %g msgs/sec\n", - bytecount, - delta, - bytecount / delta, - bytecount / (delta * msglen)); - fflush(stdout); - } - } - - fprintf(f, "(11:unsubscribe5:test3)"); - fflush(f); - - fclose(f); - - return 0; -} diff --git a/server/test3_latency.c b/server/test3_latency.c deleted file mode 100644 index 8cb2fcb..0000000 --- a/server/test3_latency.c +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static size_t build_message(char *message, uint32_t s, uint32_t us) { - char const *msg_prefix = "(4:post2:q1(4:post0:8:"; - char const *msg_suffix = "0:)0:)"; - size_t prefix_len = strlen(msg_prefix); - size_t suffix_len = strlen(msg_suffix); - uint32_t v; - size_t total_len = 0; - - memcpy(message + total_len, msg_prefix, prefix_len); - total_len += prefix_len; - v = htonl(s); - memcpy(message + total_len, &v, sizeof(uint32_t)); - total_len += sizeof(uint32_t); - v = htonl(us); - memcpy(message + total_len, &v, sizeof(uint32_t)); - total_len += sizeof(uint32_t); - memcpy(message + total_len, msg_suffix, suffix_len); - total_len += suffix_len; - - /* - printf("%d<<", total_len); - fwrite(message, total_len, 1, stdout); - printf(">>\n"); - */ - return total_len; -} - -int main(int argc, char *argv[]) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in s; - FILE *f; - struct timeval start_time; - long bytecount = 0; - int i; - unsigned long hz_limit = 1000000; - unsigned long msgcount = 10000000; - - assert(sizeof(uint32_t) == 4); - - if (argc < 2) { - fprintf(stderr, "Usage: test1 [ []]\n"); - exit(1); - } - - if (argc > 2) { - hz_limit = strtoul(argv[2], NULL, 0); - } - printf("hz_limit = %lu\n", hz_limit); - - if (argc > 3) { - msgcount = strtoul(argv[3], NULL, 0); - } - printf("msgcount = %lu\n", msgcount); - - { - struct hostent *h = gethostbyname(argv[1]); - if (h == NULL) { - fprintf(stderr, "serverhostname lookup: %d\n", h_errno); - exit(1); - } - s.sin_family = AF_INET; - s.sin_addr.s_addr = * (uint32_t *) h->h_addr_list[0]; - s.sin_port = htons(5671); - } - - if (connect(fd, (struct sockaddr *) &s, sizeof(s)) < 0) return 1; - - { - int i = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i)); - } - - f = fdopen(fd, "a+"); - - fprintf(f, "(9:subscribe5:test30:0:5:test35:login)"); - fflush(f); - - usleep(100000); - { - char buf[4096]; - size_t n = read(fd, buf, sizeof(buf)); - printf("Received: <<%.*s>>\n", (int) n, buf); - } - - gettimeofday(&start_time, NULL); - - for (i = 0; i < msgcount; i++) { - char message[1024]; - size_t msglen; - while (1) { - struct timeval now; - double delta; - gettimeofday(&now, NULL); - delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0; - if (i / delta <= hz_limit) break; - fflush(f); - usleep(1000); - } - if ((i % (hz_limit / 4)) == 0) { - struct timeval now; - gettimeofday(&now, NULL); - msglen = build_message(message, now.tv_sec, now.tv_usec); - } else { - msglen = build_message(message, 0, 0); - } - fwrite(message, msglen, 1, f); - bytecount += msglen; - if ((bytecount % 100000) < msglen) { - struct timeval now; - double delta; - gettimeofday(&now, NULL); - delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0; - printf("So far sent %ld bytes in %g seconds = %g bytes/sec and %g msgs/sec\n", - bytecount, - delta, - bytecount / delta, - bytecount / (delta * msglen)); - fflush(stdout); - } - } - - fprintf(f, "(11:unsubscribe5:test3)"); - fflush(f); - - fclose(f); - - return 0; -} diff --git a/specgen.py b/specgen.py deleted file mode 100644 index 7e21969..0000000 --- a/specgen.py +++ /dev/null @@ -1,229 +0,0 @@ -import xml.dom.minidom - -def constify(s): - s = s.replace('-', '_') - s = s.replace(' ', '_') - s = s.upper() - return s - -def cify(s): - s = constify(s) - s = s.lower() - return s - -def camelify(s): - s = constify(s) - s = s.split('_') - s = [s[0].lower()] + [w.capitalize() for w in s[1:]] - s = ''.join(s) - return s - -ctypemap = { - 'shortstr': 'cmsg_bytes_t', - 'longstr': 'cmsg_bytes_t', - 'table': 'cmsg_bytes_t', # TODO fix - 'octet': 'uint8_t', - 'short': 'uint16_t', - 'long': 'uint32_t', - 'longlong': 'uint64_t', - 'bit': 'uint8_t', - 'timestamp': 'uint64_t', -} - -class Constant: - def __init__(self, e): - self.name = e.getAttribute('name') - self.value = e.getAttribute('value') - self.class_ = e.getAttribute('class') or None - - def getName(self): - if self.class_: - return 'CMSG_AMQP_ERROR_' + constify(self.name) - else: - return 'CMSG_AMQP_' + constify(self.name) - - def getValue(self): - return self.value - -class Field: - def __init__(self, e): - self.name = e.getAttribute('name') - self.domain = e.getAttribute('domain') or e.getAttribute('type') - # self.reserved = bool(e.getAttribute('reserved')) - self.type = resolveDomain(self.domain) - - def getName(self): - return cify(self.name) - - def ctype(self): - return ctypemap[str(self.type)] - -class Entity: - def __init__(self, parent, e): - self.parent = parent - self.name = e.getAttribute('name') - self.index = int(e.getAttribute('index')) - self.fields = [Field(ee) \ - for ee in e.getElementsByTagName('field') \ - if ee.parentNode is e] - - def getName(self): - if self.parent: - return self.parent.getName() + '_' + cify(self.name) - else: - return cify(self.name) - - def printStructDefExtras(self): - pass - - def printStructDef(self, suffix): - if self.fields: - print - print 'typedef struct cmsg_amqp_%s_%s_t_ {' % (self.getName(), suffix) - self.printStructDefExtras() - for f in self.fields: - print ' %s %s;' % (f.ctype(), f.getName()) - print '} cmsg_amqp_%s_%s_t;' % (self.getName(), suffix) - -class BitWriter: - def __init__(self): - self.bit_offset = 0 - - def flush(self): - if self.bit_offset: - print ' write_amqp_octet(bit_buffer);' - self.bit_offset = 0 - - def emit(self, valueExpr): - if self.bit_offset == 0: - print ' bit_buffer = 0;' - print ' if (%s) bit_buffer |= 0x%02x;' % (valueExpr, 1 << self.bit_offset) - self.bit_offset += 1 - if self.bit_offset == 8: - self.flush() - -class Method(Entity): - def __init__(self, parent, e): - Entity.__init__(self, parent, e) - self.has_content = bool(e.getAttribute('content')) - self.synchronous = bool(e.getAttribute('synchronous')) - self.responses = [ee.getAttribute('name') for ee in e.getElementsByTagName('response')] - - def methodId(self): - return self.parent.index << 16 | self.index - - def printParseClause(self): - bit_offset = 0 - print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName()) - for f in self.fields: - if f.type == 'bit': - if bit_offset == 0: - print ' if (!parse_amqp_octet(&bit_buffer, &input, &offset))' - print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' - print ' output->body.%s.%s = (bit_buffer & 0x%02x) != 0;' % \ - (self.getName(), f.getName(), 1 << bit_offset) - bit_offset += 1 - if bit_offset == 8: bit_offset = 0 - else: - print ' if (!parse_amqp_%s(&output->body.%s.%s, &input, &offset))' % \ - (f.type, self.getName(), f.getName()) - print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' - print ' return 0;' - - def printWriteClause(self): - bw = BitWriter() - print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName()) - for f in self.fields: - if f.type == 'bit': - bw.emit('output->body.%s.%s' % (self.getName(), f.getName())) - else: - bw.flush() - print ' write_amqp_%s(output->body.%s.%s);' % \ - (f.type, self.getName(), f.getName()) - bw.flush() - print ' break;' - -class Class(Entity): - def __init__(self, e): - Entity.__init__(self, None, e) - self.methods = [Method(self, ee) for ee in e.getElementsByTagName('method')] - - def printStructDefExtras(self): - print ' uint32_t _flags;' - -def resolveDomain(n): - while n in domainmap and domainmap[n] != n: - n = domainmap[n] - return n - -specxml = xml.dom.minidom.parse("amqp0-9-1.stripped.xml") -constants = [Constant(e) for e in specxml.getElementsByTagName('constant')] -domainmap = dict((e.getAttribute('name'), e.getAttribute('type')) \ - for e in specxml.getElementsByTagName('domain')) - -classes = [Class(e) for e in specxml.getElementsByTagName('class')] -classmap = dict((c.name, c) for c in classes) - -def header(): - print '/* TODO: put copyright etc */' - print '/* Generated from AMQP spec version %s.%s.%s */' % \ - (specxml.documentElement.getAttribute('major'), - specxml.documentElement.getAttribute('minor'), - specxml.documentElement.getAttribute('revision')) - - print - for c in constants: - print '#define %s %s' % (c.getName(), c.getValue()) - - for c in classes: - c.printStructDef('properties') - - for c in classes: - for m in c.methods: - m.printStructDef('method') - - print - print 'typedef struct cmsg_amqp_method_t_ {' - print ' uint32_t id;' - print ' union {' - for c in classes: - for m in c.methods: - if m.fields: - print ' cmsg_amqp_%s_method_t %s;' % (m.getName(), m.getName()) - print ' } body;' - print '} cmsg_amqp_method_t;' - - print - print 'int parse_amqp_method(' - print ' cmsg_bytes_t input,' - print ' cmsg_amqp_method_t *output) {' - print ' size_t offset = 0;' - print ' uint8_t bit_buffer = 0;' - print ' if (!parse_amqp_long(&output->id, &input, &offset))' - print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' - print ' switch (output->id) {' - for c in classes: - for m in c.methods: - m.printParseClause() - print ' default:' - print ' warn("Invalid AMQP method number 0x%%08x", output->id);' - print ' return -CMSG_AMQP_ERROR_NOT_IMPLEMENTED;' - print ' }' - print '}' - - print - print 'void write_amqp_method(' - print ' IOHandle *h,' - print ' cmsg_amqp_method_t *m) {' - print ' uint8_t bit_buffer = 0;' - print ' write_amqp_long(&m->id);' - print ' switch (m->id) {' - for c in classes: - for m in c.methods: - m.printWriteClause() - print ' default:' - print ' die("Invalid AMQP method number 0x%%08x", m->id);' - print ' }' - print '}' - -header()