Move to subdirectory in preparation for repo merge.
This commit is contained in:
parent
c539cfd526
commit
eeece41b83
|
@ -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
|
|
@ -1,459 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2009 AMQP Working Group.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
<amqp major="0" minor="9" revision="1" port="5672">
|
||||
<constant name="frame-method" value="1"/>
|
||||
<constant name="frame-header" value="2"/>
|
||||
<constant name="frame-body" value="3"/>
|
||||
<constant name="frame-heartbeat" value="8"/>
|
||||
<constant name="frame-min-size" value="4096"/>
|
||||
<constant name="frame-end" value="206"/>
|
||||
<constant name="reply-success" value="200"/>
|
||||
<constant name="content-too-large" value="311" class="soft-error"/>
|
||||
<constant name="no-consumers" value="313" class="soft-error"/>
|
||||
<constant name="connection-forced" value="320" class="hard-error"/>
|
||||
<constant name="invalid-path" value="402" class="hard-error"/>
|
||||
<constant name="access-refused" value="403" class="soft-error"/>
|
||||
<constant name="not-found" value="404" class="soft-error"/>
|
||||
<constant name="resource-locked" value="405" class="soft-error"/>
|
||||
<constant name="precondition-failed" value="406" class="soft-error"/>
|
||||
<constant name="frame-error" value="501" class="hard-error"/>
|
||||
<constant name="syntax-error" value="502" class="hard-error"/>
|
||||
<constant name="command-invalid" value="503" class="hard-error"/>
|
||||
<constant name="channel-error" value="504" class="hard-error"/>
|
||||
<constant name="unexpected-frame" value="505" class="hard-error"/>
|
||||
<constant name="resource-error" value="506" class="hard-error"/>
|
||||
<constant name="not-allowed" value="530" class="hard-error"/>
|
||||
<constant name="not-implemented" value="540" class="hard-error"/>
|
||||
<constant name="internal-error" value="541" class="hard-error"/>
|
||||
<domain name="class-id" type="short"/>
|
||||
<domain name="consumer-tag" type="shortstr"/>
|
||||
<domain name="delivery-tag" type="longlong"/>
|
||||
<domain name="exchange-name" type="shortstr">
|
||||
<assert check="length" value="127"/>
|
||||
<assert check="regexp" value="^[a-zA-Z0-9-_.:]*$"/>
|
||||
</domain>
|
||||
<domain name="method-id" type="short"/>
|
||||
<domain name="no-ack" type="bit"/>
|
||||
<domain name="no-local" type="bit"/>
|
||||
<domain name="no-wait" type="bit"/>
|
||||
<domain name="path" type="shortstr">
|
||||
<assert check="notnull"/>
|
||||
<assert check="length" value="127"/>
|
||||
</domain>
|
||||
<domain name="peer-properties" type="table"/>
|
||||
<domain name="queue-name" type="shortstr">
|
||||
<assert check="length" value="127"/>
|
||||
<assert check="regexp" value="^[a-zA-Z0-9-_.:]*$"/>
|
||||
</domain>
|
||||
<domain name="redelivered" type="bit"/>
|
||||
<domain name="message-count" type="long"/>
|
||||
<domain name="reply-code" type="short">
|
||||
<assert check="notnull"/>
|
||||
</domain>
|
||||
<domain name="reply-text" type="shortstr">
|
||||
<assert check="notnull"/>
|
||||
</domain>
|
||||
<domain name="bit" type="bit"/>
|
||||
<domain name="octet" type="octet"/>
|
||||
<domain name="short" type="short"/>
|
||||
<domain name="long" type="long"/>
|
||||
<domain name="longlong" type="longlong"/>
|
||||
<domain name="shortstr" type="shortstr"/>
|
||||
<domain name="longstr" type="longstr"/>
|
||||
<domain name="timestamp" type="timestamp"/>
|
||||
<domain name="table" type="table"/>
|
||||
<class name="connection" handler="connection" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<method name="start" synchronous="1" index="10">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<response name="start-ok"/>
|
||||
<field name="version-major" domain="octet"/>
|
||||
<field name="version-minor" domain="octet"/>
|
||||
<field name="server-properties" domain="peer-properties"/>
|
||||
<field name="mechanisms" domain="longstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="locales" domain="longstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
</method>
|
||||
<method name="start-ok" synchronous="1" index="11">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="client-properties" domain="peer-properties"/>
|
||||
<field name="mechanism" domain="shortstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="response" domain="longstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="locale" domain="shortstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
</method>
|
||||
<method name="secure" synchronous="1" index="20">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<response name="secure-ok"/>
|
||||
<field name="challenge" domain="longstr"/>
|
||||
</method>
|
||||
<method name="secure-ok" synchronous="1" index="21">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="response" domain="longstr">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
</method>
|
||||
<method name="tune" synchronous="1" index="30">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<response name="tune-ok"/>
|
||||
<field name="channel-max" domain="short"/>
|
||||
<field name="frame-max" domain="long"/>
|
||||
<field name="heartbeat" domain="short"/>
|
||||
</method>
|
||||
<method name="tune-ok" synchronous="1" index="31">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="channel-max" domain="short">
|
||||
<assert check="notnull"/>
|
||||
<assert check="le" method="tune" field="channel-max"/>
|
||||
</field>
|
||||
<field name="frame-max" domain="long"/>
|
||||
<field name="heartbeat" domain="short"/>
|
||||
</method>
|
||||
<method name="open" synchronous="1" index="40">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="open-ok"/>
|
||||
<field name="virtual-host" domain="path"/>
|
||||
<field name="reserved-1" type="shortstr" reserved="1"/>
|
||||
<field name="reserved-2" type="bit" reserved="1"/>
|
||||
</method>
|
||||
<method name="open-ok" synchronous="1" index="41">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="reserved-1" type="shortstr" reserved="1"/>
|
||||
</method>
|
||||
<method name="close" synchronous="1" index="50">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="close-ok"/>
|
||||
<field name="reply-code" domain="reply-code"/>
|
||||
<field name="reply-text" domain="reply-text"/>
|
||||
<field name="class-id" domain="class-id"/>
|
||||
<field name="method-id" domain="method-id"/>
|
||||
</method>
|
||||
<method name="close-ok" synchronous="1" index="51">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<chassis name="server" implement="MUST"/>
|
||||
</method>
|
||||
</class>
|
||||
<class name="channel" handler="channel" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<method name="open" synchronous="1" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="open-ok"/>
|
||||
<field name="reserved-1" type="shortstr" reserved="1"/>
|
||||
</method>
|
||||
<method name="open-ok" synchronous="1" index="11">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="reserved-1" type="longstr" reserved="1"/>
|
||||
</method>
|
||||
<method name="flow" synchronous="1" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<response name="flow-ok"/>
|
||||
<field name="active" domain="bit"/>
|
||||
</method>
|
||||
<method name="flow-ok" index="21">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="active" domain="bit"/>
|
||||
</method>
|
||||
<method name="close" synchronous="1" index="40">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="close-ok"/>
|
||||
<field name="reply-code" domain="reply-code"/>
|
||||
<field name="reply-text" domain="reply-text"/>
|
||||
<field name="class-id" domain="class-id"/>
|
||||
<field name="method-id" domain="method-id"/>
|
||||
</method>
|
||||
<method name="close-ok" synchronous="1" index="41">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<chassis name="server" implement="MUST"/>
|
||||
</method>
|
||||
</class>
|
||||
<class name="exchange" handler="channel" index="40">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<method name="declare" synchronous="1" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="declare-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="exchange" domain="exchange-name">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="type" domain="shortstr"/>
|
||||
<field name="passive" domain="bit"/>
|
||||
<field name="durable" domain="bit"/>
|
||||
<field name="reserved-2" type="bit" reserved="1"/>
|
||||
<field name="reserved-3" type="bit" reserved="1"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
<field name="arguments" domain="table"/>
|
||||
</method>
|
||||
<method name="declare-ok" synchronous="1" index="11">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="delete" synchronous="1" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="delete-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="exchange" domain="exchange-name">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="if-unused" domain="bit"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
</method>
|
||||
<method name="delete-ok" synchronous="1" index="21">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
</class>
|
||||
<class name="queue" handler="channel" index="50">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<method name="declare" synchronous="1" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="declare-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="passive" domain="bit"/>
|
||||
<field name="durable" domain="bit"/>
|
||||
<field name="exclusive" domain="bit"/>
|
||||
<field name="auto-delete" domain="bit"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
<field name="arguments" domain="table"/>
|
||||
</method>
|
||||
<method name="declare-ok" synchronous="1" index="11">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="queue" domain="queue-name">
|
||||
<assert check="notnull"/>
|
||||
</field>
|
||||
<field name="message-count" domain="message-count"/>
|
||||
<field name="consumer-count" domain="long"/>
|
||||
</method>
|
||||
<method name="bind" synchronous="1" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="bind-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
<field name="arguments" domain="table"/>
|
||||
</method>
|
||||
<method name="bind-ok" synchronous="1" index="21">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="unbind" synchronous="1" index="50">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="unbind-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
<field name="arguments" domain="table"/>
|
||||
</method>
|
||||
<method name="unbind-ok" synchronous="1" index="51">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="purge" synchronous="1" index="30">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="purge-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
</method>
|
||||
<method name="purge-ok" synchronous="1" index="31">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="message-count" domain="message-count"/>
|
||||
</method>
|
||||
<method name="delete" synchronous="1" index="40">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="delete-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="if-unused" domain="bit"/>
|
||||
<field name="if-empty" domain="bit"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
</method>
|
||||
<method name="delete-ok" synchronous="1" index="41">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="message-count" domain="message-count"/>
|
||||
</method>
|
||||
</class>
|
||||
<class name="basic" handler="channel" index="60">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<chassis name="client" implement="MAY"/>
|
||||
<field name="content-type" domain="shortstr"/>
|
||||
<field name="content-encoding" domain="shortstr"/>
|
||||
<field name="headers" domain="table"/>
|
||||
<field name="delivery-mode" domain="octet"/>
|
||||
<field name="priority" domain="octet"/>
|
||||
<field name="correlation-id" domain="shortstr"/>
|
||||
<field name="reply-to" domain="shortstr"/>
|
||||
<field name="expiration" domain="shortstr"/>
|
||||
<field name="message-id" domain="shortstr"/>
|
||||
<field name="timestamp" domain="timestamp"/>
|
||||
<field name="type" domain="shortstr"/>
|
||||
<field name="user-id" domain="shortstr"/>
|
||||
<field name="app-id" domain="shortstr"/>
|
||||
<field name="reserved" domain="shortstr"/>
|
||||
<method name="qos" synchronous="1" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="qos-ok"/>
|
||||
<field name="prefetch-size" domain="long"/>
|
||||
<field name="prefetch-count" domain="short"/>
|
||||
<field name="global" domain="bit"/>
|
||||
</method>
|
||||
<method name="qos-ok" synchronous="1" index="11">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="consume" synchronous="1" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="consume-ok"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="consumer-tag" domain="consumer-tag"/>
|
||||
<field name="no-local" domain="no-local"/>
|
||||
<field name="no-ack" domain="no-ack"/>
|
||||
<field name="exclusive" domain="bit"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
<field name="arguments" domain="table"/>
|
||||
</method>
|
||||
<method name="consume-ok" synchronous="1" index="21">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="consumer-tag" domain="consumer-tag"/>
|
||||
</method>
|
||||
<method name="cancel" synchronous="1" index="30">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="cancel-ok"/>
|
||||
<field name="consumer-tag" domain="consumer-tag"/>
|
||||
<field name="no-wait" domain="no-wait"/>
|
||||
</method>
|
||||
<method name="cancel-ok" synchronous="1" index="31">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="consumer-tag" domain="consumer-tag"/>
|
||||
</method>
|
||||
<method name="publish" content="1" index="40">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
<field name="mandatory" domain="bit"/>
|
||||
<field name="immediate" domain="bit"/>
|
||||
</method>
|
||||
<method name="return" content="1" index="50">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="reply-code" domain="reply-code"/>
|
||||
<field name="reply-text" domain="reply-text"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
</method>
|
||||
<method name="deliver" content="1" index="60">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
<field name="consumer-tag" domain="consumer-tag"/>
|
||||
<field name="delivery-tag" domain="delivery-tag"/>
|
||||
<field name="redelivered" domain="redelivered"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
</method>
|
||||
<method name="get" synchronous="1" index="70">
|
||||
<response name="get-ok"/>
|
||||
<response name="get-empty"/>
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="reserved-1" type="short" reserved="1"/>
|
||||
<field name="queue" domain="queue-name"/>
|
||||
<field name="no-ack" domain="no-ack"/>
|
||||
</method>
|
||||
<method name="get-ok" synchronous="1" content="1" index="71">
|
||||
<chassis name="client" implement="MAY"/>
|
||||
<field name="delivery-tag" domain="delivery-tag"/>
|
||||
<field name="redelivered" domain="redelivered"/>
|
||||
<field name="exchange" domain="exchange-name"/>
|
||||
<field name="routing-key" domain="shortstr"/>
|
||||
<field name="message-count" domain="message-count"/>
|
||||
</method>
|
||||
<method name="get-empty" synchronous="1" index="72">
|
||||
<chassis name="client" implement="MAY"/>
|
||||
<field name="reserved-1" type="shortstr" reserved="1"/>
|
||||
</method>
|
||||
<method name="ack" index="80">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="delivery-tag" domain="delivery-tag"/>
|
||||
<field name="multiple" domain="bit"/>
|
||||
</method>
|
||||
<method name="reject" index="90">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="delivery-tag" domain="delivery-tag"/>
|
||||
<field name="requeue" domain="bit"/>
|
||||
</method>
|
||||
<method name="recover-async" index="100" deprecated="1">
|
||||
<chassis name="server" implement="MAY"/>
|
||||
<field name="requeue" domain="bit"/>
|
||||
</method>
|
||||
<method name="recover" index="110">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<field name="requeue" domain="bit"/>
|
||||
</method>
|
||||
<method name="recover-ok" synchronous="1" index="111">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
</class>
|
||||
<class name="tx" handler="channel" index="90">
|
||||
<chassis name="server" implement="SHOULD"/>
|
||||
<chassis name="client" implement="MAY"/>
|
||||
<method name="select" synchronous="1" index="10">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="select-ok"/>
|
||||
</method>
|
||||
<method name="select-ok" synchronous="1" index="11">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="commit" synchronous="1" index="20">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="commit-ok"/>
|
||||
</method>
|
||||
<method name="commit-ok" synchronous="1" index="21">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
<method name="rollback" synchronous="1" index="30">
|
||||
<chassis name="server" implement="MUST"/>
|
||||
<response name="rollback-ok"/>
|
||||
</method>
|
||||
<method name="rollback-ok" synchronous="1" index="31">
|
||||
<chassis name="client" implement="MUST"/>
|
||||
</method>
|
||||
</class>
|
||||
</amqp>
|
699
doc/Sexp.txt
699
doc/Sexp.txt
|
@ -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)
|
||||
\<carriage-return> -- causes carriage-return to be ignored.
|
||||
\<line-feed> -- causes linefeed to be ignored
|
||||
\<carriage-return><line-feed> -- causes CRLF to be ignored.
|
||||
\<line-feed><carriage-return> -- 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:
|
||||
<x>* means 0 or more occurrences of <x>
|
||||
<x>+ means 1 or more occurrences of <x>
|
||||
<x>? means 0 or 1 occurrences of <x>
|
||||
parentheses are used for grouping, as in (<x> | <y>)*
|
||||
|
||||
For canonical and basic transport:
|
||||
|
||||
<sexpr> :: <string> | <list>
|
||||
<string> :: <display>? <simple-string> ;
|
||||
<simple-string> :: <raw> ;
|
||||
<display> :: "[" <simple-string> "]" ;
|
||||
<raw> :: <decimal> ":" <bytes> ;
|
||||
<decimal> :: <decimal-digit>+ ;
|
||||
-- decimal numbers should have no unnecessary leading zeros
|
||||
<bytes> -- any string of bytes, of the indicated length
|
||||
<list> :: "(" <sexp>* ")" ;
|
||||
<decimal-digit> :: "0" | ... | "9" ;
|
||||
|
||||
For advanced transport:
|
||||
|
||||
<sexpr> :: <string> | <list>
|
||||
<string> :: <display>? <simple-string> ;
|
||||
<simple-string> :: <raw> | <token> | <base-64> | <hexadecimal> |
|
||||
<quoted-string> ;
|
||||
<display> :: "[" <simple-string> "]" ;
|
||||
<raw> :: <decimal> ":" <bytes> ;
|
||||
<decimal> :: <decimal-digit>+ ;
|
||||
-- decimal numbers should have no unnecessary leading zeros
|
||||
<bytes> -- any string of bytes, of the indicated length
|
||||
<token> :: <tokenchar>+ ;
|
||||
<base-64> :: <decimal>? "|" ( <base-64-char> | <whitespace> )* "|" ;
|
||||
<hexadecimal> :: "#" ( <hex-digit> | <white-space> )* "#" ;
|
||||
<quoted-string> :: <decimal>? <quoted-string-body>
|
||||
<quoted-string-body> :: "\"" <bytes> "\""
|
||||
<list> :: "(" ( <sexp> | <whitespace> )* ")" ;
|
||||
<whitespace> :: <whitespace-char>* ;
|
||||
<token-char> :: <alpha> | <decimal-digit> | <simple-punc> ;
|
||||
<alpha> :: <upper-case> | <lower-case> | <digit> ;
|
||||
<lower-case> :: "a" | ... | "z" ;
|
||||
<upper-case> :: "A" | ... | "Z" ;
|
||||
<decimal-digit> :: "0" | ... | "9" ;
|
||||
<hex-digit> :: <decimal-digit> | "A" | ... | "F" | "a" | ... | "f" ;
|
||||
<simple-punc> :: "-" | "." | "/" | "_" | ":" | "*" | "+" | "=" ;
|
||||
<whitespace-char> :: " " | "\t" | "\r" | "\n" ;
|
||||
<base-64-char> :: <alpha> | <decimal-digit> | "+" | "/" | "=" ;
|
||||
<null> :: "" ;
|
||||
|
||||
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 <length> <octet-string>
|
||||
|
||||
For example (here k = 2)
|
||||
|
||||
01 0003 a b c
|
||||
|
||||
8.2.2 Octet-string with display-hint
|
||||
|
||||
This is represented as follows:
|
||||
|
||||
02 <length>
|
||||
01 <length> <octet-string> /* for display-type */
|
||||
01 <length> <octet-string> /* 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 <length> <item1> <item2> <item3> ... <itemn> 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] <a href="http://www.clark.net/pub/cme/html/spki.html">SPKI--A
|
||||
Simple Public Key Infrastructure</a>
|
||||
|
||||
[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
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
*.o
|
||||
messages.h
|
||||
messages.c
|
||||
cmsg
|
||||
depend.mk
|
|
@ -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
|
|
@ -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
|
|
@ -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"]
|
||||
}
|
||||
]
|
|
@ -1,93 +0,0 @@
|
|||
/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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 <serverhostname>\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;
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#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 <serverhostname> [<idchar> [<qclass>]]\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;
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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 <serverhostname>\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;
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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 <serverhostname> [<hz_limit> [<msgcount>]]\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;
|
||||
}
|
229
specgen.py
229
specgen.py
|
@ -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()
|
Loading…
Reference in New Issue