Add files for swift over UDP.
authorAdrian Bondrescu <adi.bondrescu@gmail.com>
Sun, 19 Aug 2012 18:40:53 +0000 (21:40 +0300)
committerAdrian Bondrescu <adi.bondrescu@gmail.com>
Sun, 19 Aug 2012 18:40:53 +0000 (21:40 +0300)
130 files changed:
src/libswift_udp/BUGS [new file with mode: 0644]
src/libswift_udp/ChangeLog [new file with mode: 0644]
src/libswift_udp/LICENSE [new file with mode: 0644]
src/libswift_udp/Makefile [new file with mode: 0644]
src/libswift_udp/README [new file with mode: 0644]
src/libswift_udp/SConstruct [new file with mode: 0644]
src/libswift_udp/TODO [new file with mode: 0644]
src/libswift_udp/availability.cpp [new file with mode: 0644]
src/libswift_udp/availability.h [new file with mode: 0644]
src/libswift_udp/avgspeed.cpp [new file with mode: 0644]
src/libswift_udp/avgspeed.h [new file with mode: 0644]
src/libswift_udp/bin.cpp [new file with mode: 0644]
src/libswift_udp/bin.h [new file with mode: 0644]
src/libswift_udp/bin_utils.h [new file with mode: 0644]
src/libswift_udp/bingrep.cpp [new file with mode: 0644]
src/libswift_udp/binheap.cpp [new file with mode: 0644]
src/libswift_udp/binheap.h [new file with mode: 0644]
src/libswift_udp/binmap.cpp [new file with mode: 0644]
src/libswift_udp/binmap.h [new file with mode: 0644]
src/libswift_udp/channel.cpp [new file with mode: 0644]
src/libswift_udp/cmdgw.cpp [new file with mode: 0644]
src/libswift_udp/compat.cpp [new file with mode: 0644]
src/libswift_udp/compat.h [new file with mode: 0644]
src/libswift_udp/do_tests.sh [new file with mode: 0755]
src/libswift_udp/doc/apusapus.png [new file with mode: 0644]
src/libswift_udp/doc/binmaps-alenex.pdf [new file with mode: 0644]
src/libswift_udp/doc/cc-states.png [new file with mode: 0644]
src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.nroff [new file with mode: 0644]
src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.txt [new file with mode: 0644]
src/libswift_udp/doc/index.html [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/IEEEtran.bst [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/IEEEtran.cls [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/swarm.png [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.bib [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.pdf [new file with mode: 0644]
src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.tex [new file with mode: 0644]
src/libswift_udp/doc/p2tp-lancaster.pdf [new file with mode: 0755]
src/libswift_udp/doc/p2tp-uml-er.pdf [new file with mode: 0644]
src/libswift_udp/doc/state-diagram.pdf [new file with mode: 0644]
src/libswift_udp/doc/style.css [new file with mode: 0644]
src/libswift_udp/doc/swift.css [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/AUTHORS [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/CMakeLists.txt [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/COPYING [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/ChangeLog [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/Makefile [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/Makefile.am [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/Makefile.common [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/Makefile.in [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/Makefile.nmake [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/README [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/moduleinfo.h [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/moduleinfo.nmake [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/packet-swift.c [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/plugin.c [new file with mode: 0644]
src/libswift_udp/doc/wireshark-dissector/plugin.rc.in [new file with mode: 0644]
src/libswift_udp/ext/seq_picker.cpp [new file with mode: 0644]
src/libswift_udp/ext/simple_selector.cpp [new file with mode: 0644]
src/libswift_udp/ext/vod_picker.cpp [new file with mode: 0644]
src/libswift_udp/getopt.c [new file with mode: 0644]
src/libswift_udp/getopt_long.c [new file with mode: 0644]
src/libswift_udp/getopt_win.h [new file with mode: 0644]
src/libswift_udp/hashtree.cpp [new file with mode: 0644]
src/libswift_udp/hashtree.h [new file with mode: 0644]
src/libswift_udp/httpgw.cpp [new file with mode: 0644]
src/libswift_udp/mfold/bash_profile [new file with mode: 0644]
src/libswift_udp/mfold/build.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/clean.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/cleanup.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/cleanup.node300.das2.ewi.tudelft.nl.sh [new file with mode: 0644]
src/libswift_udp/mfold/compile.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/das2.txt [new file with mode: 0644]
src/libswift_udp/mfold/do-harvest.sh [new file with mode: 0644]
src/libswift_udp/mfold/doall [new file with mode: 0755]
src/libswift_udp/mfold/docmd [new file with mode: 0755]
src/libswift_udp/mfold/dohrv [new file with mode: 0755]
src/libswift_udp/mfold/dotop [new file with mode: 0755]
src/libswift_udp/mfold/env.1mbit.sh [new file with mode: 0644]
src/libswift_udp/mfold/env.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/env.lossy.sh [new file with mode: 0644]
src/libswift_udp/mfold/env.messy.sh [new file with mode: 0644]
src/libswift_udp/mfold/hosts.txt [new file with mode: 0644]
src/libswift_udp/mfold/install.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/loggraphs [new file with mode: 0755]
src/libswift_udp/mfold/logparse [new file with mode: 0755]
src/libswift_udp/mfold/logreport [new file with mode: 0755]
src/libswift_udp/mfold/net.aussie.sh [new file with mode: 0644]
src/libswift_udp/mfold/net.lossy.sh [new file with mode: 0644]
src/libswift_udp/mfold/net.messy.sh [new file with mode: 0644]
src/libswift_udp/mfold/netclean.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/netcleanroot.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/netem.default.sh [new file with mode: 0755]
src/libswift_udp/mfold/netroot.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/ps.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/report.css [new file with mode: 0644]
src/libswift_udp/mfold/run.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/run.node300.das2.ewi.tudelft.nl.sh [new file with mode: 0644]
src/libswift_udp/mfold/run.seeder.sh [new file with mode: 0644]
src/libswift_udp/mfold/servers.txt [new file with mode: 0644]
src/libswift_udp/mfold/ssh-config [new file with mode: 0644]
src/libswift_udp/mfold/status.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/tcinfo.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/test.default.sh [new file with mode: 0644]
src/libswift_udp/mfold/vtt.txt [new file with mode: 0644]
src/libswift_udp/nat_test.cpp [new file with mode: 0644]
src/libswift_udp/nat_test_server.c [new file with mode: 0644]
src/libswift_udp/send_control.cpp [new file with mode: 0644]
src/libswift_udp/sendrecv.cpp [new file with mode: 0644]
src/libswift_udp/serialize.h [new file with mode: 0644]
src/libswift_udp/sha1.cpp [new file with mode: 0644]
src/libswift_udp/sha1.h [new file with mode: 0644]
src/libswift_udp/statsgw.cpp [new file with mode: 0644]
src/libswift_udp/swift.cpp [new file with mode: 0644]
src/libswift_udp/swift.h [new file with mode: 0644]
src/libswift_udp/tests/SConscript [new file with mode: 0644]
src/libswift_udp/tests/bin64test.cpp [new file with mode: 0644]
src/libswift_udp/tests/binstest2.cpp [new file with mode: 0755]
src/libswift_udp/tests/binstest3.cpp [new file with mode: 0644]
src/libswift_udp/tests/congctrltest.cpp [new file with mode: 0644]
src/libswift_udp/tests/connecttest.cpp [new file with mode: 0644]
src/libswift_udp/tests/dgramtest.cpp [new file with mode: 0644]
src/libswift_udp/tests/freemap.cpp [new file with mode: 0755]
src/libswift_udp/tests/hashtest.cpp [new file with mode: 0644]
src/libswift_udp/tests/ledbattest.cpp [new file with mode: 0644]
src/libswift_udp/tests/ledbattest2.cpp [new file with mode: 0644]
src/libswift_udp/tests/pex_test.sh [new file with mode: 0755]
src/libswift_udp/tests/test.bat [new file with mode: 0644]
src/libswift_udp/tests/test_file0.dat [new file with mode: 0644]
src/libswift_udp/tests/transfertest.cpp [new file with mode: 0644]
src/libswift_udp/transfer.cpp [new file with mode: 0644]

diff --git a/src/libswift_udp/BUGS b/src/libswift_udp/BUGS
new file mode 100644 (file)
index 0000000..887e828
--- /dev/null
@@ -0,0 +1,65 @@
+    * min_owd TINT_NEVER is logged
+
+    v hints, data for non-existing ranges
+    v opens multiple channels to the same address
+    v hints do not expire
+    v RTT calculations need improvement (test)
+    v google-log is unnecessary
+    * reduce template use (peer queue)
+    v hints do not expire
+    v survive 10% loss
+    v unlimited ping pong
+    v git sha-1
+    v check hints agains ack_out?_
+    v check data against ack_in
+    v channel suspend/wake. 3 cong modes state machine - ???
+    * release hints for a dormant channel
+    * minimize the number of template instantiations
+    v Channel thinks how much it HINTs a second,
+      picker thinks which HINTs are snubbed
+    * files <1sec download : how HINTs are sent?
+    v dead Channels are not killed => cannot open a new one
+        (have a channel already)
+    v peers don't cooperate
+    * RecoverProgress fails sometime
+    v leecher can't see file is done already
+    v why leecher waits 1sec?
+    * hint queue buildup
+    * file operations are not 64-bit ready
+        http://mail.python.org/pipermail/patches/2000-June/000848.html
+    * recovery: last packet
+    v no-HINT sending to a dead peer
+    * what if rtt>1sec
+    v unHINTed repeated sending
+    v 1259859412.out#8,9 connection breaks, #8 rtt 1000, #9 hint - 
+     mudachestvo, cwnd => send int 0.5sec
+     0_11_10_075_698 #9 sendctrl may send 0 < 0.000000 & 1732919509_-49_-45_-200_-111 (rtt 59661)
+     0_11_10_075_698 #9 +data (0,194)
+     0_11_10_575_703 #9 sendctrl loss detected
+     0_11_10_575_703 #9 Tdata (0,194)
+     0_11_10_575_703 #9 sendctrl may send 0 < 0.000000 & 1732919509_-49_-44_-700_-110 (rtt 59661)
+    v complete peer reconnects 1259967418.out.gz
+    * underhinting causes repetition causes interarr underest causes underhinting
+    * misterious initiating handshake bursts
+    v whether sending is limited by cwnd or app
+    * actually: whether packets are ACKed faster than sent
+    * uproot DATA NONE: complicates and deceives
+    v r735 goes to github; r741
+    * receiver is swapping => strange behavior
+    v on high losses cwnd goes to silly fractions => slows down recovery
+    v code the pingpong<->keepalive<->slowstart transition
+    v empty datagram hammering (see at linode)
+    * make a testkit!!!
+    * never back from keepalive syndrome (because of underhashing)
+    * HTTP daemon, combined select() loop
+    * range requests, priorities
+    v LEDBAT
+    * CUBIC
+    v misterious mass packet losses (!data)
+    
+    // Ric:
+    * check why the last HAVE msgs r not sent
+    * data is sent even if the client has stopped
+    * when initialized the piece picker might select hints from hint_out without knowing if the peer actually has it!
+    * IMPORTANT: trace(bin, range) reports bins out of range! Check if bug!! 
+
diff --git a/src/libswift_udp/ChangeLog b/src/libswift_udp/ChangeLog
new file mode 100644 (file)
index 0000000..ad12850
--- /dev/null
@@ -0,0 +1,7 @@
+0.003 - This is not a release as well - 18 Oct 2009
+
+       - but at least, it compiles now
+
+0.002 - This is not a release - 7 Oct 2009
+
+       - it does not even compile, committed for reading purposes only
diff --git a/src/libswift_udp/LICENSE b/src/libswift_udp/LICENSE
new file mode 100644 (file)
index 0000000..3b9c716
--- /dev/null
@@ -0,0 +1,694 @@
+------------------------------------------------------------------------------\r
+
+    swift content-delivery library.\r
+\r
+    The research leading to this library has received funding from the European\r
+    Community's Seventh Framework Programme in the P2P-Next project under grant\r
+    agreement no 216217.\r
+\r
+    All library modules are free software, unless stated otherwise; you can \r
+    redistribute them and/or modify them under the terms of the GNU Lesser \r
+    General Public License as published by the Free Software Foundation; in \r
+    particular, version 2.1 of the License.\r
+\r
+    This library is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+    Lesser General Public License for more details.\r
+\r
+    The following library modules are Copyright (c) 2008-2012, VTT Technical Research Centre of Finland; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Norut AS; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, DACC Systems AB; All rights reserved:\r
\r
+    The following library modules are Copyright (c) 2008-2012, Lancaster University; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Jo\9eef Stefan Institute; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, First Oversi Ltd.; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, TECHNISCHE UNIVERSITEIT DELFT; All rights reserved:\r
+      All files and directories found in the directory containing this LICENSE file.     \r
+\r
+    The following library modules are Copyright (c) 2008-2012, STMicroelectronics S.r.l.; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Kungliga Tekniska Högskolan (The Royal Institute of Technology); All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Markenfilm GmbH & Co. KG; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Radiotelevizija Slovenija Javni Zavvod Ljubljana; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Kendra Foundation; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Universitaet Klagenfurt; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, AG Projects; All rights reserved:\r
+      \r
+    The following library modules are Copyright (c) 2008-2012, The British Broadcasting Corporation; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Pioneer Digital Design Centre Limited; All rights reserved:\r
+  \r
+    The following library modules are Copyright (c) 2008-2012, INSTITUT FUER RUNDFUNKTECHNIK GMBH; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Fabchannel BV; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, University Politehnica Bucharest; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, EBU-UER; All rights reserved:\r
+\r
+    The following library modules are Copyright (c) 2008-2012, Università di Roma Sapienza; All rights reserved:\r
+\r
+\r
+    You should have received a copy of the GNU Lesser General Public\r
+    License along with this library; if not, write to the Free Software\r
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+\r
+    VTT Technical Research Centre of Finland, \r
+    Tekniikankatu 1, \r
+    FIN-33710 Tampere, \r
+    Finland\r
+\r
+    Norut AS,\r
+    Postboks 6434 \r
+    Forskningsparken, \r
+    9294 Tromsø,\r
+    Norway\r
+\r
+    DACC Systems AB\r
+    Glimmervägen 4, \r
+    SE18734, Täby,\r
+    Sweden\r
+\r
+    Lancaster University, \r
+    University House, \r
+    Bailrigg, Lancaster, LA1 4YW\r
+    United Kingdom\r
+\r
+    Jo\9eef Stefan Institute, \r
+    Jamova cesta 39, \r
+    1000 Ljubljana, \r
+    Slovenia\r
+\r
+    First Oversi Ltd.,\r
+    Rishon Lezion 1,\r
+    Petah Tikva 49723, \r
+    Israel\r
+\r
+    TECHNISCHE UNIVERSITEIT DELFT, \r
+    Faculty of Electrical Engineering, Mathematics and Computer Science, \r
+    Mekelweg 4, \r
+    2628 CD Delft, \r
+    The Netherlands\r
+\r
+    STMicroelectronics S.r.l., \r
+    via C.Olivetti 2, \r
+    I-20041 Agrate Brianza,\r
+    Italy\r
+\r
+    Kungliga Tekniska Högskolan (The Royal Institute of Technology), \r
+    KTH/ICT/ECS/TSLab\r
+    Electrum 229\r
+    164 40 Kista\r
+    Sweden\r
+\r
+    Markenfilm GmbH & Co. KG, \r
+    Schulauer Moorweg 25, \r
+    22880 Wedel, \r
+    Germany\r
+\r
+    Radiotelevizija Slovenija Javni Zavvod Ljubljana, \r
+    Kolodvorska 2, \r
+    SI-1000 Ljubljana,\r
+    Slovenia\r
+\r
+\r
+    Kendra Foundation, \r
+    Meadow Barn, Holne, \r
+    Newton Abbot, Devon, TQ13 7SP,\r
+    United Kingdom\r
+\r
+\r
+    Universitaet Klagenfurt, \r
+    Universitaetstrasse 65-67, \r
+    9020 Klagenfurt, \r
+    Austria\r
+\r
+    AG Projects, \r
+    Dr. Leijdsstraat 92,\r
+    2021RK Haarlem, \r
+    The Netherlands\r
+\r
+    The British Broadcasting Corporation,\r
+    Broadcasting House, Portland Place, \r
+    London, W1A 1AA \r
+    United Kingdom\r
+\r
+    Pioneer Digital Design Centre Limited, \r
+    Pioneer House, Hollybush Hill, Stoke Poges, \r
+    Slough, SL2 4QP\r
+    United Kingdom\r
+\r
+    INSTITUT FUER RUNDFUNKTECHNIK GMBH\r
+    Floriansmuehlstrasse 60,\r
+    80939 München, \r
+    Germany\r
+\r
+    Fabchannel BV, \r
+    Kleine-Gartmanplantsoen 21, \r
+    1017 RP Amsterdam, \r
+    The Netherlands\r
+\r
+    University Politehnica Bucharest, \r
+    313 Splaiul Independentei, \r
+    District 6, cod 060042, Bucharest,\r
+    Romania\r
+\r
+    EBU-UER, \r
+    L'Ancienne Route 17A, 1218\r
+    Grand Saconnex - Geneva, \r
+    Switzerland\r
+\r
+    Università di Roma Sapienza\r
+    Dipartimento di Informatica e Sistemistica (DIS),\r
+    Via Ariosto 25, \r
+    00185 Rome, \r
+    Italy\r
+\r
+\r
+------------------------------------------------------------------------------\r
+\r
+                  GNU LESSER GENERAL PUBLIC LICENSE\r
+                       Version 2.1, February 1999\r
+\r
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.\r
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts\r
+ as the successor of the GNU Library Public License, version 2, hence\r
+ the version number 2.1.]\r
+
+                            Preamble\r
+
+  The licenses for most software are designed to take away your\r
+freedom to share and change it.  By contrast, the GNU General Public\r
+Licenses are intended to guarantee your freedom to share and change\r
+free software--to make sure the software is free for all its users.\r
+
+  This license, the Lesser General Public License, applies to some\r
+specially designated software packages--typically libraries--of the\r
+Free Software Foundation and other authors who decide to use it.  You\r
+can use it too, but we suggest you first think carefully about whether\r
+this license or the ordinary General Public License is the better\r
+strategy to use in any particular case, based on the explanations below.\r
+
+  When we speak of free software, we are referring to freedom of use,\r
+not price.  Our General Public Licenses are designed to make sure that\r
+you have the freedom to distribute copies of free software (and charge\r
+for this service if you wish); that you receive source code or can get\r
+it if you want it; that you can change the software and use pieces of\r
+it in new free programs; and that you are informed that you can do\r
+these things.\r
+
+  To protect your rights, we need to make restrictions that forbid\r
+distributors to deny you these rights or to ask you to surrender these\r
+rights.  These restrictions translate to certain responsibilities for\r
+you if you distribute copies of the library or if you modify it.\r
+
+  For example, if you distribute copies of the library, whether gratis\r
+or for a fee, you must give the recipients all the rights that we gave\r
+you.  You must make sure that they, too, receive or can get the source\r
+code.  If you link other code with the library, you must provide\r
+complete object files to the recipients, so that they can relink them\r
+with the library after making changes to the library and recompiling\r
+it.  And you must show them these terms so they know their rights.\r
+
+  We protect your rights with a two-step method: (1) we copyright the\r
+library, and (2) we offer you this license, which gives you legal\r
+permission to copy, distribute and/or modify the library.\r
+
+  To protect each distributor, we want to make it very clear that\r
+there is no warranty for the free library.  Also, if the library is\r
+modified by someone else and passed on, the recipients should know\r
+that what they have is not the original version, so that the original\r
+author's reputation will not be affected by problems that might be\r
+introduced by others.\r
+
+
+  Finally, software patents pose a constant threat to the existence of\r
+any free program.  We wish to make sure that a company cannot\r
+effectively restrict the users of a free program by obtaining a\r
+restrictive license from a patent holder.  Therefore, we insist that\r
+any patent license obtained for a version of the library must be\r
+consistent with the full freedom of use specified in this license.\r
+
+  Most GNU software, including some libraries, is covered by the\r
+ordinary GNU General Public License.  This license, the GNU Lesser\r
+General Public License, applies to certain designated libraries, and\r
+is quite different from the ordinary General Public License.  We use\r
+this license for certain libraries in order to permit linking those\r
+libraries into non-free programs.\r
+
+  When a program is linked with a library, whether statically or using\r
+a shared library, the combination of the two is legally speaking a\r
+combined work, a derivative of the original library.  The ordinary\r
+General Public License therefore permits such linking only if the\r
+entire combination fits its criteria of freedom.  The Lesser General\r
+Public License permits more lax criteria for linking other code with\r
+the library.\r
+
+  We call this license the "Lesser" General Public License because it\r
+does Less to protect the user's freedom than the ordinary General\r
+Public License.  It also provides other free software developers Less\r
+of an advantage over competing non-free programs.  These disadvantages\r
+are the reason we use the ordinary General Public License for many\r
+libraries.  However, the Lesser license provides advantages in certain\r
+special circumstances.\r
+
+  For example, on rare occasions, there may be a special need to\r
+encourage the widest possible use of a certain library, so that it becomes\r
+a de-facto standard.  To achieve this, non-free programs must be\r
+allowed to use the library.  A more frequent case is that a free\r
+library does the same job as widely used non-free libraries.  In this\r
+case, there is little to gain by limiting the free library to free\r
+software only, so we use the Lesser General Public License.\r
+
+  In other cases, permission to use a particular library in non-free\r
+programs enables a greater number of people to use a large body of\r
+free software.  For example, permission to use the GNU C Library in\r
+non-free programs enables many more people to use the whole GNU\r
+operating system, as well as its variant, the GNU/Linux operating\r
+system.\r
+
+  Although the Lesser General Public License is Less protective of the\r
+users' freedom, it does ensure that the user of a program that is\r
+linked with the Library has the freedom and the wherewithal to run\r
+that program using a modified version of the Library.\r
+
+  The precise terms and conditions for copying, distribution and\r
+modification follow.  Pay close attention to the difference between a\r
+"work based on the library" and a "work that uses the library".  The\r
+former contains code derived from the library, whereas the latter must\r
+be combined with the library in order to run.\r
+
+
+                  GNU LESSER GENERAL PUBLIC LICENSE\r
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+
+  0. This License Agreement applies to any software library or other\r
+program which contains a notice placed by the copyright holder or\r
+other authorized party saying it may be distributed under the terms of\r
+this Lesser General Public License (also called "this License").\r
+Each licensee is addressed as "you".\r
+
+  A "library" means a collection of software functions and/or data\r
+prepared so as to be conveniently linked with application programs\r
+(which use some of those functions and data) to form executables.\r
+
+  The "Library", below, refers to any such software library or work\r
+which has been distributed under these terms.  A "work based on the\r
+Library" means either the Library or any derivative work under\r
+copyright law: that is to say, a work containing the Library or a\r
+portion of it, either verbatim or with modifications and/or translated\r
+straightforwardly into another language.  (Hereinafter, translation is\r
+included without limitation in the term "modification".)\r
+
+  "Source code" for a work means the preferred form of the work for\r
+making modifications to it.  For a library, complete source code means\r
+all the source code for all modules it contains, plus any associated\r
+interface definition files, plus the scripts used to control compilation\r
+and installation of the library.\r
+
+  Activities other than copying, distribution and modification are not\r
+covered by this License; they are outside its scope.  The act of\r
+running a program using the Library is not restricted, and output from\r
+such a program is covered only if its contents constitute a work based\r
+on the Library (independent of the use of the Library in a tool for\r
+writing it).  Whether that is true depends on what the Library does\r
+and what the program that uses the Library does.\r
+  \r
+  1. You may copy and distribute verbatim copies of the Library's\r
+complete source code as you receive it, in any medium, provided that\r
+you conspicuously and appropriately publish on each copy an\r
+appropriate copyright notice and disclaimer of warranty; keep intact\r
+all the notices that refer to this License and to the absence of any\r
+warranty; and distribute a copy of this License along with the\r
+Library.\r
+
+  You may charge a fee for the physical act of transferring a copy,\r
+and you may at your option offer warranty protection in exchange for a\r
+fee.\r
+
+
+  2. You may modify your copy or copies of the Library or any portion\r
+of it, thus forming a work based on the Library, and copy and\r
+distribute such modifications or work under the terms of Section 1\r
+above, provided that you also meet all of these conditions:\r
+
+    a) The modified work must itself be a software library.\r
+
+    b) You must cause the files modified to carry prominent notices\r
+    stating that you changed the files and the date of any change.\r
+
+    c) You must cause the whole of the work to be licensed at no\r
+    charge to all third parties under the terms of this License.\r
+
+    d) If a facility in the modified Library refers to a function or a\r
+    table of data to be supplied by an application program that uses\r
+    the facility, other than as an argument passed when the facility\r
+    is invoked, then you must make a good faith effort to ensure that,\r
+    in the event an application does not supply such function or\r
+    table, the facility still operates, and performs whatever part of\r
+    its purpose remains meaningful.\r
+
+    (For example, a function in a library to compute square roots has\r
+    a purpose that is entirely well-defined independent of the\r
+    application.  Therefore, Subsection 2d requires that any\r
+    application-supplied function or table used by this function must\r
+    be optional: if the application does not supply it, the square\r
+    root function must still compute square roots.)\r
+
+These requirements apply to the modified work as a whole.  If\r
+identifiable sections of that work are not derived from the Library,\r
+and can be reasonably considered independent and separate works in\r
+themselves, then this License, and its terms, do not apply to those\r
+sections when you distribute them as separate works.  But when you\r
+distribute the same sections as part of a whole which is a work based\r
+on the Library, the distribution of the whole must be on the terms of\r
+this License, whose permissions for other licensees extend to the\r
+entire whole, and thus to each and every part regardless of who wrote\r
+it.\r
+
+Thus, it is not the intent of this section to claim rights or contest\r
+your rights to work written entirely by you; rather, the intent is to\r
+exercise the right to control the distribution of derivative or\r
+collective works based on the Library.\r
+
+In addition, mere aggregation of another work not based on the Library\r
+with the Library (or with a work based on the Library) on a volume of\r
+a storage or distribution medium does not bring the other work under\r
+the scope of this License.\r
+\r
+  3. You may opt to apply the terms of the ordinary GNU General Public\r
+License instead of this License to a given copy of the Library.  To do\r
+this, you must alter all the notices that refer to this License, so\r
+that they refer to the ordinary GNU General Public License, version 2,\r
+instead of to this License.  (If a newer version than version 2 of the\r
+ordinary GNU General Public License has appeared, then you can specify\r
+that version instead if you wish.)  Do not make any other change in\r
+these notices.\r
+\r
+\r
+  Once this change is made in a given copy, it is irreversible for\r
+that copy, so the ordinary GNU General Public License applies to all\r
+subsequent copies and derivative works made from that copy.\r
+\r
+  This option is useful when you wish to copy part of the code of\r
+the Library into a program that is not a library.\r
+\r
+  4. You may copy and distribute the Library (or a portion or\r
+derivative of it, under Section 2) in object code or executable form\r
+under the terms of Sections 1 and 2 above provided that you accompany\r
+it with the complete corresponding machine-readable source code, which\r
+must be distributed under the terms of Sections 1 and 2 above on a\r
+medium customarily used for software interchange.\r
+\r
+  If distribution of object code is made by offering access to copy\r
+from a designated place, then offering equivalent access to copy the\r
+source code from the same place satisfies the requirement to\r
+distribute the source code, even though third parties are not\r
+compelled to copy the source along with the object code.\r
+\r
+  5. A program that contains no derivative of any portion of the\r
+Library, but is designed to work with the Library by being compiled or\r
+linked with it, is called a "work that uses the Library".  Such a\r
+work, in isolation, is not a derivative work of the Library, and\r
+therefore falls outside the scope of this License.\r
+\r
+  However, linking a "work that uses the Library" with the Library\r
+creates an executable that is a derivative of the Library (because it\r
+contains portions of the Library), rather than a "work that uses the\r
+library".  The executable is therefore covered by this License.\r
+Section 6 states terms for distribution of such executables.\r
+\r
+  When a "work that uses the Library" uses material from a header file\r
+that is part of the Library, the object code for the work may be a\r
+derivative work of the Library even though the source code is not.\r
+Whether this is true is especially significant if the work can be\r
+linked without the Library, or if the work is itself a library.  The\r
+threshold for this to be true is not precisely defined by law.\r
+\r
+  If such an object file uses only numerical parameters, data\r
+structure layouts and accessors, and small macros and small inline\r
+functions (ten lines or less in length), then the use of the object\r
+file is unrestricted, regardless of whether it is legally a derivative\r
+work.  (Executables containing this object code plus portions of the\r
+Library will still fall under Section 6.)\r
+\r
+  Otherwise, if the work is a derivative of the Library, you may\r
+distribute the object code for the work under the terms of Section 6.\r
+Any executables containing that work also fall under Section 6,\r
+whether or not they are linked directly with the Library itself.\r
+\r
+\r
+  6. As an exception to the Sections above, you may also combine or\r
+link a "work that uses the Library" with the Library to produce a\r
+work containing portions of the Library, and distribute that work\r
+under terms of your choice, provided that the terms permit\r
+modification of the work for the customer's own use and reverse\r
+engineering for debugging such modifications.\r
+\r
+  You must give prominent notice with each copy of the work that the\r
+Library is used in it and that the Library and its use are covered by\r
+this License.  You must supply a copy of this License.  If the work\r
+during execution displays copyright notices, you must include the\r
+copyright notice for the Library among them, as well as a reference\r
+directing the user to the copy of this License.  Also, you must do one\r
+of these things:\r
+\r
+    a) Accompany the work with the complete corresponding\r
+    machine-readable source code for the Library including whatever\r
+    changes were used in the work (which must be distributed under\r
+    Sections 1 and 2 above); and, if the work is an executable linked\r
+    with the Library, with the complete machine-readable "work that\r
+    uses the Library", as object code and/or source code, so that the\r
+    user can modify the Library and then relink to produce a modified\r
+    executable containing the modified Library.  (It is understood\r
+    that the user who changes the contents of definitions files in the\r
+    Library will not necessarily be able to recompile the application\r
+    to use the modified definitions.)\r
+\r
+    b) Use a suitable shared library mechanism for linking with the\r
+    Library.  A suitable mechanism is one that (1) uses at run time a\r
+    copy of the library already present on the user's computer system,\r
+    rather than copying library functions into the executable, and (2)\r
+    will operate properly with a modified version of the library, if\r
+    the user installs one, as long as the modified version is\r
+    interface-compatible with the version that the work was made with.\r
+\r
+    c) Accompany the work with a written offer, valid for at\r
+    least three years, to give the same user the materials\r
+    specified in Subsection 6a, above, for a charge no more\r
+    than the cost of performing this distribution.\r
+\r
+    d) If distribution of the work is made by offering access to copy\r
+    from a designated place, offer equivalent access to copy the above\r
+    specified materials from the same place.\r
+\r
+    e) Verify that the user has already received a copy of these\r
+    materials or that you have already sent this user a copy.\r
+\r
+  For an executable, the required form of the "work that uses the\r
+Library" must include any data and utility programs needed for\r
+reproducing the executable from it.  However, as a special exception,\r
+the materials to be distributed need not include anything that is\r
+normally distributed (in either source or binary form) with the major\r
+components (compiler, kernel, and so on) of the operating system on\r
+which the executable runs, unless that component itself accompanies\r
+the executable.\r
+\r
+  It may happen that this requirement contradicts the license\r
+restrictions of other proprietary libraries that do not normally\r
+accompany the operating system.  Such a contradiction means you cannot\r
+use both them and the Library together in an executable that you\r
+distribute.\r
+\r
+\r
+  7. You may place library facilities that are a work based on the\r
+Library side-by-side in a single library together with other library\r
+facilities not covered by this License, and distribute such a combined\r
+library, provided that the separate distribution of the work based on\r
+the Library and of the other library facilities is otherwise\r
+permitted, and provided that you do these two things:\r
+\r
+    a) Accompany the combined library with a copy of the same work\r
+    based on the Library, uncombined with any other library\r
+    facilities.  This must be distributed under the terms of the\r
+    Sections above.\r
+\r
+    b) Give prominent notice with the combined library of the fact\r
+    that part of it is a work based on the Library, and explaining\r
+    where to find the accompanying uncombined form of the same work.\r
+\r
+  8. You may not copy, modify, sublicense, link with, or distribute\r
+the Library except as expressly provided under this License.  Any\r
+attempt otherwise to copy, modify, sublicense, link with, or\r
+distribute the Library is void, and will automatically terminate your\r
+rights under this License.  However, parties who have received copies,\r
+or rights, from you under this License will not have their licenses\r
+terminated so long as such parties remain in full compliance.\r
+\r
+  9. You are not required to accept this License, since you have not\r
+signed it.  However, nothing else grants you permission to modify or\r
+distribute the Library or its derivative works.  These actions are\r
+prohibited by law if you do not accept this License.  Therefore, by\r
+modifying or distributing the Library (or any work based on the\r
+Library), you indicate your acceptance of this License to do so, and\r
+all its terms and conditions for copying, distributing or modifying\r
+the Library or works based on it.\r
+\r
+  10. Each time you redistribute the Library (or any work based on the\r
+Library), the recipient automatically receives a license from the\r
+original licensor to copy, distribute, link with or modify the Library\r
+subject to these terms and conditions.  You may not impose any further\r
+restrictions on the recipients' exercise of the rights granted herein.\r
+You are not responsible for enforcing compliance by third parties with\r
+this License.\r
+\r
+\r
+  11. If, as a consequence of a court judgment or allegation of patent\r
+infringement or for any other reason (not limited to patent issues),\r
+conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License.  If you cannot\r
+distribute so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you\r
+may not distribute the Library at all.  For example, if a patent\r
+license would not permit royalty-free redistribution of the Library by\r
+all those who receive copies directly or indirectly through you, then\r
+the only way you could satisfy both it and this License would be to\r
+refrain entirely from distribution of the Library.\r
+\r
+If any portion of this section is held invalid or unenforceable under any\r
+particular circumstance, the balance of the section is intended to apply,\r
+and the section as a whole is intended to apply in other circumstances.\r
+\r
+It is not the purpose of this section to induce you to infringe any\r
+patents or other property right claims or to contest validity of any\r
+such claims; this section has the sole purpose of protecting the\r
+integrity of the free software distribution system which is\r
+implemented by public license practices.  Many people have made\r
+generous contributions to the wide range of software distributed\r
+through that system in reliance on consistent application of that\r
+system; it is up to the author/donor to decide if he or she is willing\r
+to distribute software through any other system and a licensee cannot\r
+impose that choice.\r
+\r
+This section is intended to make thoroughly clear what is believed to\r
+be a consequence of the rest of this License.\r
+\r
+  12. If the distribution and/or use of the Library is restricted in\r
+certain countries either by patents or by copyrighted interfaces, the\r
+original copyright holder who places the Library under this License may add\r
+an explicit geographical distribution limitation excluding those countries,\r
+so that distribution is permitted only in or among countries not thus\r
+excluded.  In such case, this License incorporates the limitation as if\r
+written in the body of this License.\r
+\r
+  13. The Free Software Foundation may publish revised and/or new\r
+versions of the Lesser General Public License from time to time.\r
+Such new versions will be similar in spirit to the present version,\r
+but may differ in detail to address new problems or concerns.\r
+\r
+Each version is given a distinguishing version number.  If the Library\r
+specifies a version number of this License which applies to it and\r
+"any later version", you have the option of following the terms and\r
+conditions either of that version or of any later version published by\r
+the Free Software Foundation.  If the Library does not specify a\r
+license version number, you may choose any version ever published by\r
+the Free Software Foundation.\r
+\r
+\r
+  14. If you wish to incorporate parts of the Library into other free\r
+programs whose distribution conditions are incompatible with these,\r
+write to the author to ask for permission.  For software which is\r
+copyrighted by the Free Software Foundation, write to the Free\r
+Software Foundation; we sometimes make exceptions for this.  Our\r
+decision will be guided by the two goals of preserving the free status\r
+of all derivatives of our free software and of promoting the sharing\r
+and reuse of software generally.\r
+\r
+                            NO WARRANTY\r
+\r
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\r
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\r
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\r
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY\r
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\r
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\r
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\r
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r
+\r
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\r
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\r
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\r
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\r
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\r
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\r
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\r
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\r
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\r
+DAMAGES.\r
+\r
+                     END OF TERMS AND CONDITIONS\r
+\r
+\r
+           How to Apply These Terms to Your New Libraries\r
+\r
+  If you develop a new library, and you want it to be of the greatest\r
+possible use to the public, we recommend making it free software that\r
+everyone can redistribute and change.  You can do so by permitting\r
+redistribution under these terms (or, alternatively, under the terms of the\r
+ordinary General Public License).\r
+\r
+  To apply these terms, attach the following notices to the library.  It is\r
+safest to attach them to the start of each source file to most effectively\r
+convey the exclusion of warranty; and each file should have at least the\r
+"copyright" line and a pointer to where the full notice is found.\r
+\r
+    <one line to give the library's name and a brief idea of what it does.>\r
+    Copyright (C) <year>  <name of author>\r
+\r
+    This library is free software; you can redistribute it and/or\r
+    modify it under the terms of the GNU Lesser General Public\r
+    License as published by the Free Software Foundation; either\r
+    version 2.1 of the License, or (at your option) any later version.\r
+\r
+    This library is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+    Lesser General Public License for more details.\r
+\r
+    You should have received a copy of the GNU Lesser General Public\r
+    License along with this library; if not, write to the Free Software\r
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+You should also get your employer (if you work as a programmer) or your\r
+school, if any, to sign a "copyright disclaimer" for the library, if\r
+necessary.  Here is a sample; alter the names:\r
+\r
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the\r
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.\r
+\r
+  <signature of Ty Coon>, 1 April 1990\r
+  Ty Coon, President of Vice\r
+\r
+That's all there is to it!\r
+\r
+-------------------------------------------------------------------------------\r
+\r
diff --git a/src/libswift_udp/Makefile b/src/libswift_udp/Makefile
new file mode 100644 (file)
index 0000000..2ac2044
--- /dev/null
@@ -0,0 +1,15 @@
+LIBEVENT_HOME=/prod/pkgs/libevent-2.0.15-arno-http
+
+CPPFLAGS+=-O2 -I. -Wall -Wno-sign-compare -Wno-unused -g -I${LIBEVENT_HOME}/include -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+LDFLAGS+=-L${LIBEVENT_HOME}/lib -Wl,-rpath,${LIBEVENT_HOME}/lib -levent -lstdc++
+
+all: swift
+
+swift: swift.o sha1.o compat.o sendrecv.o send_control.o hashtree.o bin.o binmap.o binheap.o channel.o transfer.o httpgw.o statsgw.o cmdgw.o avgspeed.o availability.o
+#nat_test.o
+       g++ ${CPPFLAGS} -o swift *.o ${LDFLAGS}
+
+clean:
+       rm *.o swift 2>/dev/null
+
+.PHONY: all clean
diff --git a/src/libswift_udp/README b/src/libswift_udp/README
new file mode 100644 (file)
index 0000000..ddfd593
--- /dev/null
@@ -0,0 +1,35 @@
+swift: the multiparty transport protocol
+    (aka BitTorrent at the transport layer)
+    Differently from TCP, the protocol does not use the ordered data stream
+    abstraction. Effectively, it splits a file into 1KB packets and sends
+    them around. The secret sauce is Merkle hash trees and binmaps.
+    Requires libevent-2.0 or higher.
+
+see doc/index.html for marketing stuff, ideas and rants
+    doc/draft-ietf-ppsp-grishchenko-swift.txt for protocol draft spec
+    *.cpp for the actual code
+    swift.cpp is the main exec file; may run as e.g.
+    
+        ./swift -t node300.das2.ewi.tudelft.nl:20000 -h \
+        d1502706c46779d361a1d562a10da0a45c4c40e5 -f \
+        trailer.ogg
+        
+    ...to retrieve video and save it to a file.
+
+    Alternatively, you might play with the HTTP gateway, the preliminary
+    version. First, run the seeder-tracker: 
+
+        $ ./swift -f ~/Downloads/big_buck_bunny_480p_stereo.ogg -l 0.0.0.0:20000
+        Root hash: 7c462ad1d980ba44ab4b819e29004eb0bf6e6d5f
+
+    ...then you may try running the swift-HTTP gateway...
+
+        $ ./swift -t 127.0.0.1:20000 -g 0.0.0.0:8080 -w
+
+    ...and finally you may point your browser at the gateway...
+
+        http://127.0.0.1:8080/7c462ad1d980ba44ab4b819e29004eb0bf6e6d5f
+
+    If you use an HTML5 browser (Chrome preferred), you are likely to see
+    the bunny trailer at this point...
diff --git a/src/libswift_udp/SConstruct b/src/libswift_udp/SConstruct
new file mode 100644 (file)
index 0000000..ceca61f
--- /dev/null
@@ -0,0 +1,140 @@
+# Written by Victor Grishchenko, Arno Bakker \r
+# see LICENSE.txt for license information\r
+#\r
+# Requirements:\r
+#  - scons: Cross-platform build system    http://www.scons.org/\r
+#  - libevent2: Event driven network I/O   http://www.libevent.org/\r
+#    * Install in \build\libevent-2.0.14-stable\r
+# For debugging:\r
+#  - googletest: Google C++ Test Framework http://code.google.com/p/googletest/\r
+#       * Install in \build\gtest-1.4.0\r
+#\r
+\r
+\r
+import os\r
+import re\r
+import sys\r
+\r
+DEBUG = True\r
+\r
+TestDir='tests'\r
+\r
+target = 'swift'\r
+source = [ 'bin.cpp', 'binmap.cpp','binheap.cpp', 'sha1.cpp','hashtree.cpp',\r
+          'transfer.cpp', 'channel.cpp', 'sendrecv.cpp', 'send_control.cpp', \r
+          'compat.cpp','avgspeed.cpp', 'availability.cpp']\r
+\r
+\r
+env = Environment()\r
+if sys.platform == "win32":\r
+    #libevent2path = '\\build\\libevent-2.0.14-stable'\r
+    libevent2path = '\\build\\ttuki\\libevent-2.0.15-arno-http'\r
+\r
+    # "MSVC works out of the box". Sure.\r
+    # Make sure scons finds cl.exe, etc.\r
+    env.Append ( ENV = { 'PATH' : os.environ['PATH'] } )\r
+\r
+    # Make sure scons finds std MSVC include files\r
+    if not 'INCLUDE' in os.environ:\r
+        print "swift: Please run scons in a Visual Studio Command Prompt"\r
+        sys.exit(-1)\r
+        \r
+    include = os.environ['INCLUDE']\r
+    include += libevent2path+'\\include;'\r
+    include += libevent2path+'\\WIN32-Code;'\r
+    if DEBUG:\r
+        include += '\\build\\gtest-1.4.0\\include;'\r
+    \r
+    env.Append ( ENV = { 'INCLUDE' : include } )\r
+    \r
+    if 'CXXPATH' in os.environ:\r
+        cxxpath = os.environ['CXXPATH']\r
+    else:\r
+        cxxpath = ""\r
+    cxxpath += include\r
+    if DEBUG:\r
+        env.Append(CXXFLAGS="/Zi /MTd")\r
+        env.Append(LINKFLAGS="/DEBUG")\r
+    env.Append(CXXPATH=cxxpath)\r
+    env.Append(CPPPATH=cxxpath)\r
+\r
+    # getopt for win32\r
+    source += ['getopt.c','getopt_long.c']\r
\r
+     # Set libs to link to\r
+     # Advapi32.lib for CryptGenRandom in evutil_rand.obj\r
+    libs = ['ws2_32','libevent','Advapi32'] \r
+    if DEBUG:\r
+        libs += ['gtestd']\r
+        \r
+    # Update lib search path\r
+    libpath = os.environ['LIBPATH']\r
+    libpath += libevent2path+';'\r
+    if DEBUG:\r
+        libpath += '\\build\\gtest-1.4.0\\msvc\\gtest\\Debug;'\r
+\r
+    # Somehow linker can't find uuid.lib\r
+    libpath += 'C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib;'\r
+    \r
+    # TODO: Make the swift.exe a Windows program not a Console program\r
+    if not DEBUG:\r
+       env.Append(LINKFLAGS="/SUBSYSTEM:WINDOWS")\r
+    \r
+    APPSOURCE=['swift.cpp','httpgw.cpp','statsgw.cpp','cmdgw.cpp','getopt.c','getopt_long.c']\r
+    \r
+else:\r
+    libevent2path = '/arno/pkgs/libevent-2.0.15-arno-http'\r
+\r
+    # Enable the user defining external includes\r
+    if 'CPPPATH' in os.environ:\r
+        cpppath = os.environ['CPPPATH']\r
+    else:\r
+        cpppath = ""\r
+        print "To use external libs, set CPPPATH environment variable to list of colon-separated include dirs"\r
+    cpppath += libevent2path+'/include:'\r
+    env.Append(CPPPATH=".:"+cpppath)\r
+    #env.Append(LINKFLAGS="--static")\r
+\r
+    #if DEBUG:\r
+    #    env.Append(CXXFLAGS="-g")\r
+\r
+    # Large-file support always\r
+    env.Append(CXXFLAGS="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE")\r
+\r
+    # Set libs to link to\r
+    libs = ['stdc++','libevent','pthread']\r
+    if 'LIBPATH' in os.environ:\r
+          libpath = os.environ['LIBPATH']\r
+    else:\r
+        libpath = ""\r
+        print "To use external libs, set LIBPATH environment variable to list of colon-separated lib dirs"\r
+    libpath += libevent2path+'/lib:'\r
+\r
+    linkflags = '-Wl,-rpath,'+libevent2path+'/lib'\r
+    env.Append(LINKFLAGS=linkflags);\r
+\r
+\r
+    APPSOURCE=['swift.cpp','httpgw.cpp','statsgw.cpp','cmdgw.cpp']\r
+\r
+if DEBUG:\r
+    env.Append(CXXFLAGS="-DDEBUG")\r
+\r
+env.StaticLibrary (\r
+    target='libswift',\r
+    source = source,\r
+    LIBS=libs,\r
+    LIBPATH=libpath )\r
+\r
+env.Program(\r
+   target='swift',\r
+   source=APPSOURCE,\r
+   #CPPPATH=cpppath,\r
+   LIBS=[libs,'libswift'],\r
+   LIBPATH=libpath+':.')\r
+   \r
+Export("env")\r
+Export("libs")\r
+Export("libpath")\r
+Export("DEBUG")\r
+# Arno: uncomment to build tests\r
+SConscript('tests/SConscript')\r
diff --git a/src/libswift_udp/TODO b/src/libswift_udp/TODO
new file mode 100644 (file)
index 0000000..e1d8541
--- /dev/null
@@ -0,0 +1,76 @@
+    TRIAL TODO
+
+STATE MACHINE
+* imposed HINTs are terribly broken, resent for the data in flight 
+* check ACK/HAVE redundancy
+* HAVE overuses find_filtered
+* set priorities on ranges
+* small-progress update problem (aka peer nap)
+  guarantee size of updates < x% of data, on both ends
+* pex is affected by peer nap
+* how will tracker aggregate pexes?
+* SWIFT_MSGTYPE_RCVD
+* HAVE ALL / HAVE NONE
+* aggregate ACKS (schedule for +x ms)
+* channel close msg (hs 0)   # Arno: indeed, there appears to be no Channel garbage collection
+* connection rotation / pex / pex_del
+* misterious bug: Rdata (NONE)
+* ?amend MAX_REORDER depending on rtt_dev
+* Tdata repetitions bug
+
+PERFORMANCE
+* move to the.zett's binmaps
+* optimize redundant HASH messages
+* move to rolling HAVE queue
+* 32 bit time field
+* ?empty/full binmaps
+* initiate RTT with prev RTT to host:port
+* fractional cwnd
+
+CACHING/FILES
+* connection rotation
+* file rotation
+* real LRU/LFU
+* file/hash-file re-open in read-only mode
+* no cache recheck, failure-resistant
+* completion mark
+* unified events/callbacks
+* move to 64-bit IO
+* Transfer(fd) constructor
+* think of sliding window(s)
+* the ability to sniff file without downloading
+
+MANIFOLD
+* all-swarm performance stats
+* run chained setups (cmd line protocol subsetting)
+* implement: multiple swift instances per server
+* run thousand-daemon caching tests (use httpgw)
+* use a dedicated tracker
+* add NATs to the setup
+* recover mfold.libswift.org
+* integrate Windowses
+
+API
+* pluggable storage
+
+NAT
+* NAT type detection => need peer identifiers (x100 amplification)
+
+MFOLD
+* integrate multi-peer changes by Jori
+* do global swarm stats
+
+OTHER
+* httpgw or nginx?
+* Sha1Hash constructor ambiguity
+* don't #include .cpp
+* think of using HTTP (?) as a fallback
+* add header/footer, better abstract to the draft
+* Gertjan: separate peer from channel? cng ctrl per peer ?
+* packing hashes into a single datagram (tracking 1000s)
+* partial channels / lightweight channels
+
+THOUGHTS
+* 6 degrees of sep = 3-hop TorrentSmell
+* 60% immediately not connectable
+* support traffic
diff --git a/src/libswift_udp/availability.cpp b/src/libswift_udp/availability.cpp
new file mode 100644 (file)
index 0000000..a17c208
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *  availability.h
+ *  Tree keeping track of the  availability of each bin in a swarm
+ *
+ *  Created by Riccardo Petrocco
+ *  Copyright 2009-2012 Delft University of Technology. All rights reserved.
+ *
+ */
+#include "availability.h"
+
+using namespace swift;
+
+#define DEBUGAVAILABILITY      0
+
+
+uint8_t Availability::get(const bin_t bin)
+{
+       if (bin.is_none())
+               return 255;
+       else if (size_)
+               return avail_[bin.toUInt()];
+
+       return 0;
+}
+
+
+void Availability::setBin(bin_t bin)
+{
+       if (bin != bin_t::NONE)
+       {
+               bin_t beg = bin.base_left();
+               bin_t end = bin.base_right();
+
+               for (int i = beg.toUInt(); i<=end.toUInt(); i++)
+               {
+                       // for the moment keep a counter
+                       // TODO make it percentage
+                       avail_[i]++;
+               }
+       }
+
+}
+
+
+void Availability::removeBin(bin_t bin)
+{
+       bin_t beg = bin.base_left();
+       bin_t end = bin.base_right();
+
+       for (int i = beg.toUInt(); i<=end.toUInt(); i++)
+       {
+               avail_[i]--;
+       }
+}
+
+
+void Availability::setBinmap(binmap_t * binmap)
+{
+
+       if (binmap->is_filled())
+               for (int i=0; i<size_; i++)
+                       avail_[i]++;
+       else
+               if (!binmap->is_empty())
+               {
+                       //status();
+                       bin_t tmp_b;
+                       binmap_t tmp_bm;
+                       tmp_b = binmap_t::find_complement(tmp_bm, *binmap, 0);
+
+                       while (tmp_b != bin_t::NONE)
+                       {
+                               setBin(tmp_b);
+                               //binmap_t::copy(tmp_bm, *binmap, tmp_b);
+                               tmp_bm.set(tmp_b);
+                               tmp_b = binmap_t::find_complement(tmp_bm, *binmap, 0);
+                       }
+                       //status();
+
+               }
+
+       return;
+}
+
+void Availability::removeBinmap(binmap_t &binmap)
+{
+       if (binmap.is_filled())
+               for (int i=0; i<size_; i++)
+                       avail_[i]--;
+       else
+               if (!binmap.is_empty())
+               {
+                       bin_t tmp_b;
+                       binmap_t tmp_bm;
+                       tmp_b = binmap_t::find_complement(tmp_bm, binmap, 0);
+                       while (tmp_b != bin_t::NONE)
+                       {
+                               removeBin(tmp_b);
+                               tmp_bm.set(tmp_b);
+                               tmp_b = binmap_t::find_complement(tmp_bm, binmap, 0);
+                       }
+               }
+
+       return;
+}
+
+void Availability::set(uint32_t channel_id, binmap_t& binmap, bin_t target)
+{
+       if (DEBUGAVAILABILITY)
+       {
+               char bin_name_buf[32];
+               dprintf("%s #%u Availability -> setting %s (%llu)\n",tintstr(),channel_id,target.str(bin_name_buf),target.toUInt());
+       }
+
+       if (size_>0 && !binmap.is_filled(target))
+       {
+               bin_t beg = target.base_left();
+               bin_t end = target.base_right();
+
+               for (int i = beg.toUInt(); i<=end.toUInt(); i++)
+               {
+                       // for the moment keep a counter
+                       // TODO make it percentage
+                       if (!binmap.is_filled(bin_t(i)))
+                               avail_[i]++;
+                       //TODO avoid running into sub-trees that r filled
+               }
+       }
+       // keep track of the incoming have msgs
+       else
+       {
+
+               for (WaitingPeers::iterator vpci = waiting_peers_.begin(); vpci != waiting_peers_.end(); ++vpci)
+               {
+                   if (vpci->first == channel_id)
+                   {
+                       waiting_peers_.erase(vpci);
+                       break;
+               }
+               }
+
+       waiting_peers_.push_back(std::make_pair(channel_id, &binmap));
+       }
+}
+
+
+void Availability::remove(uint32_t channel_id, binmap_t& binmap)
+{
+       if (DEBUGAVAILABILITY)
+       {
+               dprintf("%s #%u Availability -> removing peer\n",tintstr(),channel_id);
+       }
+       if (size_<=0)
+       {
+               WaitingPeers::iterator vpci = waiting_peers_.begin();
+               for(; vpci != waiting_peers_.end(); ++vpci)
+               {
+                       if (vpci->first == channel_id)
+                       {
+                           waiting_peers_.erase(vpci);
+                               break;
+                       }
+               }
+       }
+
+       else
+               removeBinmap(binmap);
+       // remove the binmap from the availability
+
+       return;
+}
+
+
+void Availability::setSize(uint64_t size)
+{
+       if (size && !size_)
+       {
+               // TODO can be optimized (bithacks)
+               uint64_t r = 0;
+               uint64_t s = size;
+
+               // check if the binmap is not complete
+               if (s & (s-1))
+               {
+                       while (s >>= 1)
+                       {
+                               r++;
+                       }
+                       s = 1<<(r+1);
+               }
+               // consider higher layers
+               s += s-1;
+               size_ = s;
+               avail_ = new uint8_t[s]();
+
+               // Initialize with the binmaps we already received
+               for(WaitingPeers::iterator vpci = waiting_peers_.begin(); vpci != waiting_peers_.end(); ++vpci)
+               {
+                       setBinmap(vpci->second);
+               }
+
+
+               if (DEBUGAVAILABILITY)
+               {
+                       char bin_name_buf[32];
+                       dprintf("%s #1 Availability -> setting size in chunk %lu \t avail size %u\n",tintstr(), size, s);
+               }
+       }
+}
+
+bin_t Availability::getRarest(const bin_t range, int width)
+{
+       assert(range.toUInt()<size_);
+       bin_t curr = range;
+       bin_t::uint_t idx = range.toUInt();
+
+       while (curr.base_length()>width)
+       {
+               idx = curr.toUInt();
+               if ( avail_[curr.left().toUInt()] <= avail_[curr.right().toUInt()] )
+                       curr.to_left();
+               else
+                       curr.to_right();
+       }
+       return curr;
+}
+
+void Availability::status() const
+{
+    printf("availability:\n");
+
+    if (size_ > 0)
+    {
+               for (int i = 0; i < size_; i++)
+                       printf("%d", avail_[i]);
+    }
+
+    printf("\n");
+}
+
+
+
diff --git a/src/libswift_udp/availability.h b/src/libswift_udp/availability.h
new file mode 100644 (file)
index 0000000..2c7b31c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  availability.h
+ *  Tree keeping track of the availability of each bin in a swarm
+ *
+ *  Created by Riccardo Petrocco
+ *  Copyright 2009-2012 Delft University of Technology. All rights reserved.
+ *
+ */
+//#include "bin.h"
+//#include "binmap.h"
+#include "swift.h"
+#include <cassert>
+
+#ifndef AVAILABILITY_H
+#define AVAILABILITY_H
+
+namespace swift {
+
+typedef        std::vector< std::pair<uint32_t, binmap_t*> >   WaitingPeers;
+
+class Availability
+{
+    public:
+
+               /**
+            * Constructor
+            */
+           Availability(void) {        size_ = 0;          }
+
+
+           /**
+            * Constructor
+            */
+           explicit Availability(int size)
+           {
+               assert(size <= 0);
+               size_ = size;
+               avail_ = new uint8_t[size];
+           }
+
+        ~Availability(void)
+        {
+            if (size_)
+                delete [] avail_;
+        }
+
+           /** return the availability array */
+           uint8_t* get() { return avail_; }
+
+           /** returns the availability of a single bin */
+           uint8_t get(const bin_t bin);
+
+           /** set/update the availability */
+           void set(uint32_t channel_id, binmap_t& binmap, bin_t target);
+
+           /** removes the binmap of leaving peers */
+           void remove(uint32_t channel_id, binmap_t& binmap);
+
+           /** returns the size of the availability tree */
+           int size() { return size_; }
+
+           /** sets the size of the availability tree once we know the size of the file */
+           void setSize(uint64_t size);
+
+           /** sets a binmap */
+           void setBinmap(binmap_t *binmap);
+
+           /** get rarest bin, of specified width, within a range */
+           bin_t getRarest(const bin_t range, int width);
+
+           /** Echo the availability status to stdout */
+               void status() const;
+
+    protected:
+           uint8_t *avail_;
+           uint64_t    size_;
+           // a list of incoming have msgs, those are saved only it the file size is still unknown
+            // TODO fix... set it depending on the # of channels * something
+           WaitingPeers waiting_peers_;
+           //binmap_t *waiting_[20];
+
+
+
+           /** removes the binmap */
+           void removeBinmap(binmap_t& binmap);
+
+           /** removes the bin */
+           void removeBin(bin_t bin);
+
+           /** sets a bin */
+           void setBin(bin_t bin);
+
+};
+
+}
+
+#endif
diff --git a/src/libswift_udp/avgspeed.cpp b/src/libswift_udp/avgspeed.cpp
new file mode 100644 (file)
index 0000000..2bc8607
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  avgspeed.cpp
+ *  Class to compute moving average speed
+ *
+ *  Created by Arno Bakker
+ *  Copyright 2009 Delft University of Technology. All rights reserved.
+ *
+ */
+#include "avgspeed.h"
+
+using namespace swift;
+
+MovingAverageSpeed::MovingAverageSpeed(tint speed_interval, tint fudge)
+{
+    speed_interval_ = speed_interval;
+    fudge_ = fudge;
+       t_start_ = usec_time() - fudge_;
+       t_end_ = t_start_;
+       speed_ = 0.0;
+       resetstate_ = false;
+}
+
+
+void MovingAverageSpeed::AddPoint(uint64_t amount)
+{
+       // Arno, 2012-01-04: Resetting this measurement includes not adding
+       // points for a few seconds after the reset, to accomodate the case
+       // of going from high speed to low speed and content still coming in.
+       //
+       if (resetstate_) {
+               if ((t_start_ + speed_interval_/2) > usec_time()) {
+                       return;
+               }
+               resetstate_ = false;
+       }
+
+    tint t = usec_time();
+    speed_ = (speed_ * ((double)(t_end_ - t_start_)/((double)TINT_SEC)) + (double)amount) / ((t - t_start_)/((double)TINT_SEC) + 0.0001);
+    t_end_ = t;
+    if (t_start_ < t - speed_interval_)
+        t_start_ = t - speed_interval_;
+}
+
+
+double MovingAverageSpeed::GetSpeed()
+{
+    AddPoint(0);
+    return speed_;
+}
+
+
+double MovingAverageSpeed::GetSpeedNeutral()
+{
+    return speed_;
+}
+
+
+void MovingAverageSpeed::Reset()
+{
+       resetstate_ = true;
+       t_start_ = usec_time() - fudge_;
+       t_end_ = t_start_;
+       speed_ = 0.0;
+}
diff --git a/src/libswift_udp/avgspeed.h b/src/libswift_udp/avgspeed.h
new file mode 100644 (file)
index 0000000..b7d6f37
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  avgspeed.h
+ *  Class to compute moving average speed
+ *
+ *  Created by Arno Bakker
+ *  Copyright 2009 Delft University of Technology. All rights reserved.
+ *
+ */
+#include "compat.h"
+
+#ifndef AVGSPEED_H
+#define AVGSPEED_H
+
+namespace swift {
+
+
+class MovingAverageSpeed
+{
+    public: 
+               MovingAverageSpeed( tint speed_interval = 5 * TINT_SEC, tint fudge = TINT_SEC );
+               void AddPoint( uint64_t amount );
+        double GetSpeed();
+        double GetSpeedNeutral();
+        void Reset();
+    protected:
+        tint   speed_interval_;
+        tint   t_start_;
+        tint   t_end_;
+        double speed_;
+        tint   fudge_;
+        bool   resetstate_;
+};
+
+}
+
+#endif
diff --git a/src/libswift_udp/bin.cpp b/src/libswift_udp/bin.cpp
new file mode 100644 (file)
index 0000000..1878f3e
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  bin.cpp
+ *  swift
+ *
+ *  Created by Victor Grishchenko on 10/10/09.
+ *  Reimplemented by Alexander G. Pronchenkov on 05/05/10
+ *
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include "bin.h"
+#include <ostream>
+
+
+const bin_t bin_t::NONE(8 * sizeof(bin_t::uint_t), 0);
+const bin_t bin_t::ALL(8 * sizeof(bin_t::uint_t) - 1, 0);
+
+
+/* Methods */
+
+/**
+ * Gets the layer value of a bin
+ */
+int bin_t::layer(void) const
+{
+    if (is_none()) {
+        return -1;
+    }
+
+    int r = 0;
+
+#ifdef _MSC_VER
+#  pragma warning (push)
+#  pragma warning (disable:4146)
+#endif
+    register uint_t tail;
+    tail = v_ + 1;
+    tail = tail & (-tail);
+#ifdef _MSC_VER
+#  pragma warning (pop)
+#endif
+
+    if (tail > 0x80000000U) {
+        r = 32;
+        tail >>= 16;    // FIXME: hide warning
+        tail >>= 16;
+    }
+
+    // courtesy of Sean Eron Anderson
+    // http://graphics.stanford.edu/~seander/bithacks.html
+    static const char DeBRUIJN[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
+
+    return r + DeBRUIJN[ 0x1f & ((tail * 0x077CB531U) >> 27) ];
+}
+
+/* String operations */
+
+namespace {
+
+char* append(char* buf, int x)
+{
+    char* l = buf;
+    char* r = buf;
+
+    if (x < 0) {
+        *r++ = '-';
+        x = -x;
+    }
+
+    do {
+        *r++ = '0' + x % 10;
+        x /= 10;
+    } while (x);
+
+    char* e = r--;
+
+    while (l < r) {
+        const char t = *l;
+        *l++ = *r;
+        *r-- = t;
+    }
+
+    *e = '\0';
+
+    return e;
+}
+
+char* append(char* buf, bin_t::uint_t x)
+{
+    char* l = buf;
+    char* r = buf;
+
+    do {
+        *r++ = '0' + x % 10;
+        x /= 10;
+    } while (x);
+
+    char* e = r--;
+
+    while (l < r) {
+        const char t = *l;
+        *l++ = *r;
+        *r-- = t;
+    }
+
+    *e = '\0';
+
+    return e;
+}
+
+char* append(char* buf, const char* s)
+{
+    char* e = buf;
+
+    while (*s) {
+        *e++ = *s++;
+    }
+
+    *e = '\0';
+
+    return e;
+}
+
+char* append(char* buf, char c)
+{
+    char* e = buf;
+
+    *e++ = c;
+    *e = '\0';
+
+    return e;
+}
+
+} /* namespace */
+
+
+/**
+ * Get the standard-form of this bin, e.g. "(2,1)".
+ * (buffer should have enough of space)
+ */
+const char* bin_t::str(char* buf) const
+{
+    char* e = buf;
+
+    if (is_all()) {
+        e = append(e, "(ALL)");
+    } else if (is_none()) {
+        e = append(e, "(NONE)");
+    } else {
+        e = append(e, '(');
+        e = append(e, layer());
+        e = append(e, ',');
+        e = append(e, layer_offset());
+        e = append(e, ')');
+    }
+
+    return buf;
+}
+
+
+/**
+ * Output operator
+ */
+std::ostream & operator << (std::ostream & ostream, const bin_t & bin)
+{
+    char bin_name_buf[64];
+    return ostream << bin.str(bin_name_buf);
+}
diff --git a/src/libswift_udp/bin.h b/src/libswift_udp/bin.h
new file mode 100644 (file)
index 0000000..70a05c3
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ *  bin.h
+ *  swift
+ *
+ *  Created by Victor Grishchenko on 10/10/09.
+ *  Reimplemented by Alexander G. Pronchenkov on 05/05/10
+ *
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#ifndef __bin_h__
+#define __bin_h__
+
+#include <iosfwd>
+
+
+/**
+ * Numbering for (aligned) logarithmic bins.
+ *
+ * Each number stands for an interval
+ *   [layer_offset * 2^layer, (layer_offset + 1) * 2^layer).
+ *
+ * The following value is called as base_offset:
+ *   layer_offset * 2^layer -- is called
+ *
+ * Bin numbers in the tail111 encoding: meaningless bits in
+ * the tail are set to 0111...11, while the head denotes the offset.
+ * bin = 2 ^ (layer + 1) * layer_offset + 2 ^ layer - 1
+ *
+ * Thus, 1101 is the bin at layer 1, offset 3 (i.e. fourth).
+ */
+
+/**
+ *
+ *                  +-----------------00111-----------------+
+ *                  |                                       |
+ *        +-------00011-------+                   +-------01011-------+
+ *        |                   |                   |                   |
+ *   +--00001--+         +--00101--+         +--01001--+         +--01101--+
+ *   |         |         |         |         |         |         |         |
+ * 00000     00010     00100     00110     01000     01010     01100     1110
+ *
+ *
+ *
+ *               7
+ *           /       \
+ *       3              11
+ *     /   \           /  \
+ *   1       5       9     13
+ *  / \     / \     / \    / \
+ * 0   2   4   6   8  10  12 14
+ *
+ * Once we have peak hashes, this structure is more natural than bin-v1
+ *
+ */
+
+class bin_t {
+public:
+    /**
+     * Basic integer type
+     */
+    typedef unsigned long long uint_t;
+
+
+    /**
+     * Constants
+     */
+    static const bin_t NONE;
+    static const bin_t ALL;
+
+
+    /**
+     * Constructor
+     */
+    bin_t(void);
+
+
+    /**
+     * Constructor
+     */
+    explicit bin_t(uint_t val);
+
+
+    /**
+     * Constructor
+     */
+    bin_t(int layer, uint_t layer_offset);
+
+
+    /**
+     * Gets the bin value
+     */
+    uint_t toUInt(void) const;
+
+
+    /**
+     * Operator equal
+     */
+    bool operator == (const bin_t& bin) const;
+
+
+    /**
+     * Operator non-equal
+     */
+    bool operator != (const bin_t& bin) const;
+
+
+    /**
+     * Operator less than
+     */
+    bool operator < (const bin_t& bin) const;
+
+
+    /**
+     * Operator greater than
+     */
+    bool operator > (const bin_t& bin) const;
+
+
+    /**
+     * Operator less than or equal
+     */
+    bool operator <= (const bin_t& bin) const;
+
+
+    /**
+     * Operator greater than or equal
+     */
+    bool operator >= (const bin_t& bin) const;
+
+    /**
+     * Decompose the bin
+     */
+    void decompose(int* layer, uint_t* layer_offset) const;
+
+
+    /**
+     * Gets the beginning of the bin(ary interval)
+     */
+    uint_t base_offset(void) const;
+
+
+    /**
+     * Gets the length of the bin interval
+     */
+    uint_t base_length(void) const;
+
+
+    /**
+     * Gets the bin's layer, i.e. log2(base_length)
+     */
+    int layer(void) const;
+
+
+    /**
+     * Gets the bin layer bits
+     */
+    uint_t layer_bits(void) const;
+
+
+    /**
+     * Gets the bin layer offset
+     */
+    uint_t layer_offset(void) const;
+
+
+    /**
+     * Whether the bin is none
+     */
+    bool is_none(void) const;
+
+
+    /**
+     * Whether the bin is all
+     */
+    bool is_all(void) const;
+
+
+    /**
+     * Whether the bin is base (layer == 0)
+     */
+    bool is_base(void) const;
+
+
+    /**
+     * Checks whether is bin is a left child
+     */
+    bool is_left(void) const;
+
+
+    /**
+     * Checks whether is bin is a left child
+     */
+    bool is_right(void) const;
+
+
+    /**
+     * Sets this object to the parent
+     */
+    bin_t& to_parent(void);
+
+
+    /**
+     * Sets this object to the left child
+     */
+    bin_t& to_left(void);
+
+
+    /**
+     * Sets this object to the right child
+     */
+    bin_t& to_right(void);
+
+
+    /**
+     * Sets this object to the sibling
+     */
+    bin_t& to_sibling(void);
+
+
+    /**
+     * Sets this object to the leftmost base sub-bin
+     */
+    bin_t& to_base_left(void);
+
+
+    /**
+     * Sets this object to the rightmost base sub-bin
+     */
+    bin_t& to_base_right(void);
+
+
+    /**
+     * Sets this object to the permutated state
+     */
+    bin_t& to_twisted(uint_t mask);
+
+
+    /**
+     * Sets this object to a layer shifted state
+     */
+    bin_t& to_layer_shifted(int zlayer);
+
+
+    /**
+     * Gets the parent bin
+     */
+    bin_t parent(void) const;
+
+
+    /**
+     * Gets the left child
+     */
+    bin_t left(void) const;
+
+
+    /**
+     * Gets the right child
+     */
+    bin_t right(void) const;
+
+
+    /**
+     * Gets the sibling bin
+     */
+    bin_t sibling(void) const;
+
+
+    /**
+     * Gets the leftmost base sub-bin
+     */
+    bin_t base_left(void) const;
+
+
+    /**
+     * Gets the rightmost base sub-bin
+     */
+    bin_t base_right(void) const;
+
+
+    /**
+     * Performs a permutation
+     */
+    bin_t twisted(uint_t mask) const;
+
+
+    /**
+     * Gets the bin after a layer shifting
+     */
+    bin_t layer_shifted(int zlayer) const;
+
+
+    /**
+     * Checks for contains
+     */
+    bool contains(const bin_t& bin) const;
+
+
+    /**
+     * Get the standard-form of this bin, e.g. "(2,1)".
+     * (buffer should have enough of space)
+     */
+    const char* str(char* buf) const;
+
+
+private:
+
+    /** Bin value */
+    uint_t v_;
+};
+
+
+/**
+ * Output operator
+ */
+std::ostream & operator << (std::ostream & ostream, const bin_t & bin);
+
+
+/**
+ * Constructor
+ */
+inline bin_t::bin_t(void)
+{ }
+
+
+/**
+ * Constructor
+ */
+inline bin_t::bin_t(uint_t val)
+    : v_(val)
+{ }
+
+
+/**
+ * Constructor
+ */
+inline bin_t::bin_t(int layer, uint_t offset)
+{
+    if (static_cast<unsigned int>(layer) < 8 * sizeof(uint_t)) {
+        v_ = ((2 * offset + 1) << layer) - 1;
+    } else {
+        v_ = static_cast<uint_t>(-1); // Definition of the NONE bin
+    }
+}
+
+
+/**
+ * Gets the bin value
+ */
+inline bin_t::uint_t bin_t::toUInt(void) const
+{
+    return v_;
+}
+
+
+/**
+ * Operator equal
+ */
+inline bool bin_t::operator == (const bin_t& bin) const
+{
+    return v_ == bin.v_;
+}
+
+
+/**
+ * Operator non-equal
+ */
+inline bool bin_t::operator != (const bin_t& bin) const
+{
+    return v_ != bin.v_;
+}
+
+
+/**
+ * Operator less than
+ */
+inline bool bin_t::operator < (const bin_t& bin) const
+{
+    return v_ < bin.v_;
+}
+
+
+/**
+ * Operator great than
+ */
+inline bool bin_t::operator > (const bin_t& bin) const
+{
+    return v_ > bin.v_;
+}
+
+
+/**
+ * Operator less than or equal
+ */
+inline bool bin_t::operator <= (const bin_t& bin) const
+{
+    return v_ <= bin.v_;
+}
+
+
+/**
+ * Operator great than or equal
+ */
+inline bool bin_t::operator >= (const bin_t& bin) const
+{
+    return v_ >= bin.v_;
+}
+
+
+/**
+ * Decompose the bin
+ */
+inline void bin_t::decompose(int* layer, uint_t* layer_offset) const
+{
+    const int l = this->layer();
+    if (layer) {
+        *layer = l;
+    }
+    if (layer_offset) {
+        *layer_offset = v_ >> (l + 1);
+    }
+}
+
+
+/**
+ * Gets a beginning of the bin interval
+ */
+inline bin_t::uint_t bin_t::base_offset(void) const
+{
+    return (v_ & (v_ + 1)) >> 1;
+}
+
+
+/**
+ * Gets the length of the bin interval
+ */
+inline bin_t::uint_t bin_t::base_length(void) const
+{
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4146)
+#endif
+    const uint_t t = v_ + 1;
+    return t & -t;
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+}
+
+
+/**
+ * Gets the layer bits
+ */
+inline bin_t::uint_t bin_t::layer_bits(void) const
+{
+    return v_ ^ (v_ + 1);
+}
+
+
+/**
+ * Gets the offset value of a bin
+ */
+inline bin_t::uint_t bin_t::layer_offset(void) const
+{
+    return v_ >> (layer() + 1);
+}
+
+
+/**
+ * Does the bin is none
+ */
+inline bool bin_t::is_none(void) const
+{
+    return *this == NONE;
+}
+
+
+/**
+ * Does the bin is all
+ */
+inline bool bin_t::is_all(void) const
+{
+    return *this == ALL;
+}
+
+
+/**
+ * Checks is bin is base (layer == 0)
+ */
+inline bool bin_t::is_base(void) const
+{
+    return !(v_ & 1);
+}
+
+
+/**
+ * Checks is bin is a left child
+ */
+inline bool bin_t::is_left(void) const
+{
+    return !(v_ & (layer_bits() + 1));
+}
+
+
+/**
+ * Checks whether is bin is a left child
+ */
+inline bool bin_t::is_right(void) const
+{
+    return !is_left();
+}
+
+
+/**
+ * Sets this object to the parent
+ */
+inline bin_t& bin_t::to_parent(void)
+{
+    const uint_t lbs = layer_bits();
+    const uint_t nlbs = -2 - lbs; /* ~(lbs + 1) */
+
+    v_ = (v_ | lbs) & nlbs;
+
+    return *this;
+}
+
+
+/**
+ * Sets this object to the left child
+ */
+inline bin_t& bin_t::to_left(void)
+{
+    register uint_t t;
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4146)
+#endif
+    t = v_ + 1;
+    t &= -t;
+    t >>= 1;
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+//    if (t == 0) {
+//        return NONE;
+//    }
+
+    v_ ^= t;
+
+    return *this;
+}
+
+
+/**
+* Sets this object to the right child
+*/
+inline bin_t& bin_t::to_right(void)
+{
+    register uint_t t;
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4146)
+#endif
+    t = v_ + 1;
+    t &= -t;
+    t >>= 1;
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+//    if (t == 0) {
+//        return NONE;
+//    }
+
+    v_ += t;
+
+    return *this;
+}
+
+
+/**
+ * Sets this object to the sibling
+ */
+inline bin_t& bin_t::to_sibling(void)
+{
+    v_ ^= (v_ ^ (v_ + 1)) + 1;
+
+    return *this;
+}
+
+
+/**
+ * Sets this object to the leftmost base sub-bin
+ */
+inline bin_t& bin_t::to_base_left(void)
+{
+    if (!is_none()) {
+        v_ &= (v_ + 1);
+    }
+
+    return *this;
+}
+
+
+/**
+ * Sets this object to the rightmost base sub-bin
+ */
+inline bin_t& bin_t::to_base_right(void)
+{
+    if (!is_none()) {
+        v_ = (v_ | (v_ + 1)) - 1;
+    }
+
+    return *this;
+}
+
+
+/**
+ * Performs a permutation
+ */
+inline bin_t& bin_t::to_twisted(uint_t mask)
+{
+    v_ ^= ((mask << 1) & ~layer_bits());
+
+    return *this;
+}
+
+
+/**
+ * Sets this object to a layer shifted state
+ */
+inline bin_t& bin_t::to_layer_shifted(int zlayer)
+{
+    if (layer_bits() >> zlayer) {
+        v_ >>= zlayer;
+    } else {
+        v_ = (v_ >> zlayer) & ~static_cast<uint_t>(1);
+    }
+
+    return *this;
+}
+
+
+/**
+ * Gets the parent bin
+ */
+inline bin_t bin_t::parent(void) const
+{
+    const uint_t lbs = layer_bits();
+    const uint_t nlbs = -2 - lbs; /* ~(lbs + 1) */
+
+    return bin_t((v_ | lbs) & nlbs);
+}
+
+
+/**
+ * Gets the left child
+ */
+inline bin_t bin_t::left(void) const
+{
+    register uint_t t;
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4146)
+#endif
+    t = v_ + 1;
+    t &= -t;
+    t >>= 1;
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+//    if (t == 0) {
+//        return NONE;
+//    }
+
+    return bin_t(v_ ^ t);
+}
+
+
+/**
+ * Gets the right child
+ */
+inline bin_t bin_t::right(void) const
+{
+    register uint_t t;
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4146)
+#endif
+    t = v_ + 1;
+    t &= -t;
+    t >>= 1;
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+//    if (t == 0) {
+//        return NONE;
+//    }
+
+    return bin_t(v_ + t);
+}
+
+
+/**
+ * Gets the sibling bin
+ */
+inline bin_t bin_t::sibling(void) const
+{
+    return bin_t(v_ ^ (layer_bits() + 1));
+}
+
+
+/**
+ * Gets the leftmost base sub-bin
+ */
+inline bin_t bin_t::base_left(void) const
+{
+    if (is_none()) {
+        return NONE;
+    }
+
+    return bin_t(v_ & (v_ + 1));
+}
+
+
+/**
+ * Gets the rightmost base sub-bin
+ */
+inline bin_t bin_t::base_right(void) const
+{
+    if (is_none()) {
+        return NONE;
+    }
+
+    return bin_t((v_ | (v_ + 1)) - 1);
+}
+
+
+/**
+ * Performs a permutation
+ */
+inline bin_t bin_t::twisted(uint_t mask) const
+{
+    return bin_t( v_ ^ ((mask << 1) & ~layer_bits()) );
+}
+
+
+/**
+ * Gets the bin after a layer shifting
+ */
+inline bin_t bin_t::layer_shifted(int zlayer) const
+{
+    if (layer_bits() >> zlayer) {
+        return bin_t( v_  >> zlayer );
+    } else {
+        return bin_t( (v_ >> zlayer) & ~static_cast<uint_t>(1) );
+    }
+}
+
+
+/**
+ * Checks for contains
+ */
+inline bool bin_t::contains(const bin_t& bin) const
+{
+    if (is_none()) {
+        return false;
+    }
+
+    return (v_ & (v_ + 1)) <= bin.v_ && bin.v_ < (v_ | (v_ + 1));
+}
+
+
+#endif /*_bin_h__*/
diff --git a/src/libswift_udp/bin_utils.h b/src/libswift_udp/bin_utils.h
new file mode 100644 (file)
index 0000000..b6de28e
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef __bin_utils_h__
+#define __bin_utils_h__
+
+#include "bin.h"
+#include "compat.h"
+
+
+/**
+ * Generating a list of peak bins for corresponding length
+ */
+inline int gen_peaks(uint64_t length, bin_t * peaks) {
+    int pp = 0;
+    uint8_t layer = 0;
+
+    while (length) {
+        if (length & 1)
+            peaks[pp++] = bin_t(((2 * length - 1) << layer) - 1);
+        length >>= 1;
+        layer++;
+    }
+
+    for(int i = 0; i < (pp >> 1); ++i) {
+        bin_t memo = peaks[pp - 1 - i];
+        peaks[pp - 1 - i] = peaks[i];
+        peaks[i] = memo;
+    }
+
+    peaks[pp] = bin_t::NONE;
+    return pp;
+}
+
+
+/**
+ * Checking for that the bin value is fit to uint32_t
+ */
+inline bool bin_isUInt32(const bin_t & bin) {
+    if( bin.is_all() )
+        return true;
+    if( bin.is_none() )
+        return true;
+
+    const uint64_t v = bin.toUInt();
+
+    return static_cast<uint32_t>(v) == v && v != 0xffffffff && v != 0x7fffffff;
+}
+
+
+/**
+ * Convert the bin value to uint32_t
+ */
+inline uint32_t bin_toUInt32(const bin_t & bin) {
+    if( bin.is_all() )
+        return 0x7fffffff;
+    if( bin.is_none() )
+        return 0xffffffff;
+    return static_cast<uint32_t>(bin.toUInt());
+}
+
+
+/**
+ * Convert the bin value to uint64_t
+ */
+inline uint64_t bin_toUInt64(const bin_t & bin) {
+    return bin.toUInt();
+}
+
+
+/**
+ * Restore the bin from an uint32_t value
+ */
+inline bin_t bin_fromUInt32(uint32_t v) {
+    if( v == 0x7fffffff )
+        return bin_t::ALL;
+    if( v == 0xffffffff )
+        return bin_t::NONE;
+    return bin_t(static_cast<uint64_t>(v));
+}
+
+
+/**
+ * Restore the bin from an uint64_t value
+ */
+inline bin_t bin_fromUInt64(uint64_t v) {
+    return bin_t(static_cast<uint64_t>(v));
+}
+
+
+#endif /*_bin_utils_h__*/
diff --git a/src/libswift_udp/bingrep.cpp b/src/libswift_udp/bingrep.cpp
new file mode 100644 (file)
index 0000000..f5cd51c
--- /dev/null
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <string.h>
+#include "bin.h"
+
+int main (int argn, char** args) {
+    int lr;
+    unsigned long long of;
+    sscanf(args[1],"%i,%lli",&lr,&of);
+    bin_t target(lr,of);
+    char line[1024];
+    while (gets(line)) {
+        char* br = strchr(line,'(');
+        if (br && 2==sscanf(br,"(%i,%lli)",&lr,&of)) {
+            bin_t found(lr,of);
+            if ( target.contains(found) || found.contains(target))
+                printf("%s\n",line);
+        }
+    }
+    return 0;
+}
diff --git a/src/libswift_udp/binheap.cpp b/src/libswift_udp/binheap.cpp
new file mode 100644 (file)
index 0000000..58054d4
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  sbit.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 4/1/09.
+ *  Copyright 2009 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include <algorithm>
+#include <cstdlib>
+
+#include "binheap.h"
+
+
+binheap::binheap() {
+    size_ = 32;
+    heap_ = (bin_t*) malloc(size_*sizeof(bin_t));
+    filled_ = 0;
+}
+
+bool bincomp (const bin_t& a, const bin_t& b) {
+    register uint64_t ab = a.base_offset(), bb = b.base_offset();
+    if (ab==bb)
+        return a.layer_bits() < b.layer_bits();
+    else
+        return ab > bb;
+}
+
+bool bincomp_rev (const bin_t& a, const bin_t& b) {
+    register uint64_t ab = a.base_offset(), bb = b.base_offset();
+    if (ab==bb)
+        return a.layer_bits() > b.layer_bits();
+    else
+        return ab < bb;
+}
+
+bin_t binheap::pop() {
+    if (!filled_)
+        return bin_t::NONE;
+    bin_t ret = heap_[0];
+    std::pop_heap(heap_, heap_+filled_--,bincomp);
+    while (filled_ && ret.contains(heap_[0]))
+        std::pop_heap(heap_, heap_+filled_--,bincomp);
+    return ret;
+}
+
+void    binheap::extend() {
+    std::sort(heap_,heap_+filled_,bincomp_rev);
+    int solid = 0;
+    for(int i=1; i<filled_; i++)
+        if (!heap_[solid].contains(heap_[i]))
+            heap_[++solid] = heap_[i];
+    filled_ = solid+1;
+    if (2*filled_>size_) {
+        size_ <<= 1;
+        heap_ = (bin_t*) realloc(heap_,size_*sizeof(bin_t));
+    }
+}
+
+void    binheap::push(bin_t val) {
+    if (filled_==size_)
+        extend();
+    heap_[filled_++] = val;
+    std::push_heap(heap_, heap_+filled_,bincomp);
+}
+
+binheap::~binheap() {
+    free(heap_);
+}
+
diff --git a/src/libswift_udp/binheap.h b/src/libswift_udp/binheap.h
new file mode 100644 (file)
index 0000000..eaba92a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  sbit.cpp
+ *  binmap, a hybrid of bitmap and binary tree
+ *
+ *  Created by Victor Grishchenko on 3/28/09.
+ *  Copyright 2009 Delft University of Technology. All rights reserved.
+ *
+ */
+#ifndef BINS_H
+#define BINS_H
+
+#include "bin.h"
+#include "compat.h"
+
+class binheap {
+    bin_t       *heap_;
+    uint32_t    filled_;
+    uint32_t    size_;
+public:
+    binheap();
+    bin_t   pop();
+    void    push(bin_t);
+    bool    empty() const { return !filled_; }
+    void    extend();
+    ~binheap();
+};
+
+#endif
diff --git a/src/libswift_udp/binmap.cpp b/src/libswift_udp/binmap.cpp
new file mode 100644 (file)
index 0000000..feee239
--- /dev/null
@@ -0,0 +1,2126 @@
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+#include <iostream>
+
+
+#include "binmap.h"
+
+using namespace swift;
+
+namespace swift {
+
+inline size_t _max_(const size_t x, const size_t y)
+{
+    return x < y ? y : x;
+}
+
+typedef binmap_t::ref_t ref_t;
+typedef binmap_t::bitmap_t bitmap_t;
+
+/* Bitmap constants */
+const bitmap_t BITMAP_EMPTY  = static_cast<bitmap_t>(0);
+const bitmap_t BITMAP_FILLED = static_cast<bitmap_t>(-1);
+
+const bin_t::uint_t BITMAP_LAYER_BITS = 2 * 8 * sizeof(bitmap_t) - 1;
+
+const ref_t ROOT_REF = 0;
+
+#ifdef _MSC_VER
+#  pragma warning (push)
+#  pragma warning ( disable:4309 )
+#endif
+
+const bitmap_t BITMAP[] = {
+    static_cast<bitmap_t>(0x00000001), static_cast<bitmap_t>(0x00000003),
+    static_cast<bitmap_t>(0x00000002), static_cast<bitmap_t>(0x0000000f),
+    static_cast<bitmap_t>(0x00000004), static_cast<bitmap_t>(0x0000000c),
+    static_cast<bitmap_t>(0x00000008), static_cast<bitmap_t>(0x000000ff),
+    static_cast<bitmap_t>(0x00000010), static_cast<bitmap_t>(0x00000030),
+    static_cast<bitmap_t>(0x00000020), static_cast<bitmap_t>(0x000000f0),
+    static_cast<bitmap_t>(0x00000040), static_cast<bitmap_t>(0x000000c0),
+    static_cast<bitmap_t>(0x00000080), static_cast<bitmap_t>(0x0000ffff),
+    static_cast<bitmap_t>(0x00000100), static_cast<bitmap_t>(0x00000300),
+    static_cast<bitmap_t>(0x00000200), static_cast<bitmap_t>(0x00000f00),
+    static_cast<bitmap_t>(0x00000400), static_cast<bitmap_t>(0x00000c00),
+    static_cast<bitmap_t>(0x00000800), static_cast<bitmap_t>(0x0000ff00),
+    static_cast<bitmap_t>(0x00001000), static_cast<bitmap_t>(0x00003000),
+    static_cast<bitmap_t>(0x00002000), static_cast<bitmap_t>(0x0000f000),
+    static_cast<bitmap_t>(0x00004000), static_cast<bitmap_t>(0x0000c000),
+    static_cast<bitmap_t>(0x00008000), static_cast<bitmap_t>(0xffffffff),
+    static_cast<bitmap_t>(0x00010000), static_cast<bitmap_t>(0x00030000),
+    static_cast<bitmap_t>(0x00020000), static_cast<bitmap_t>(0x000f0000),
+    static_cast<bitmap_t>(0x00040000), static_cast<bitmap_t>(0x000c0000),
+    static_cast<bitmap_t>(0x00080000), static_cast<bitmap_t>(0x00ff0000),
+    static_cast<bitmap_t>(0x00100000), static_cast<bitmap_t>(0x00300000),
+    static_cast<bitmap_t>(0x00200000), static_cast<bitmap_t>(0x00f00000),
+    static_cast<bitmap_t>(0x00400000), static_cast<bitmap_t>(0x00c00000),
+    static_cast<bitmap_t>(0x00800000), static_cast<bitmap_t>(0xffff0000),
+    static_cast<bitmap_t>(0x01000000), static_cast<bitmap_t>(0x03000000),
+    static_cast<bitmap_t>(0x02000000), static_cast<bitmap_t>(0x0f000000),
+    static_cast<bitmap_t>(0x04000000), static_cast<bitmap_t>(0x0c000000),
+    static_cast<bitmap_t>(0x08000000), static_cast<bitmap_t>(0xff000000),
+    static_cast<bitmap_t>(0x10000000), static_cast<bitmap_t>(0x30000000),
+    static_cast<bitmap_t>(0x20000000), static_cast<bitmap_t>(0xf0000000),
+    static_cast<bitmap_t>(0x40000000), static_cast<bitmap_t>(0xc0000000),
+    static_cast<bitmap_t>(0x80000000), /* special */ static_cast<bitmap_t>(0xffffffff) /* special */
+};
+
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+
+/**
+ * Get the leftmost bin that corresponded to bitmap (the bin is filled in bitmap)
+ */
+bin_t::uint_t bitmap_to_bin(register bitmap_t b)
+{
+    static const unsigned char BITMAP_TO_BIN[] = {
+      0xff, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         8, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        10, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         9, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        12, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         8, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        10, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         9, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        14, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         8, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        10, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         9, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        13, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+         8, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        10, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 3,
+        11, 0, 2, 1, 4, 0, 2, 1, 6, 0, 2, 1, 5, 0, 2, 7
+    };
+
+    assert (sizeof(bitmap_t) <= 4);
+    assert (b != BITMAP_EMPTY);
+
+    unsigned char t;
+
+    t = BITMAP_TO_BIN[ b & 0xff ];
+    if (t < 16) {
+        if (t != 7) {
+            return static_cast<bin_t::uint_t>(t);
+        }
+
+        b += 1;
+        b &= -b;
+        if (0 == b) {
+            return BITMAP_LAYER_BITS / 2;
+        }
+        if (0 == (b & 0xffff)) {
+            return 15;
+        }
+        return 7;
+    }
+
+    b >>= 8;
+    t = BITMAP_TO_BIN[ b & 0xff ];
+    if (t <= 15) {
+        return 16 + t;
+    }
+
+    /* Recursion */
+    // return 32 + bitmap_to_bin( b >> 8 );
+
+    assert (sizeof(bitmap_t) == 4);
+
+    b >>= 8;
+    t = BITMAP_TO_BIN[ b & 0xff ];
+    if (t < 16) {
+        if (t != 7) {
+            return 32 + static_cast<bin_t::uint_t>(t);
+        }
+
+        b += 1;
+        b &= -b;
+        if (0 == (b & 0xffff)) {
+            return 47;
+        }
+        return 39;
+    }
+
+    b >>= 8;
+    return 48 + BITMAP_TO_BIN[ b & 0xff ];
+}
+
+
+/**
+ * Get the leftmost bin that corresponded to bitmap (the bin is filled in bitmap)
+ */
+bin_t bitmap_to_bin(const bin_t& bin, const bitmap_t bitmap)
+{
+    assert (bitmap != BITMAP_EMPTY);
+
+    if (bitmap == BITMAP_FILLED) {
+        return bin;
+    }
+
+    return bin_t(bin.base_left().toUInt() + bitmap_to_bin(bitmap));
+}
+
+} /* namespace */
+
+
+/* Methods */
+
+
+/**
+ * Constructor
+ */
+binmap_t::binmap_t()
+    : root_bin_(63)
+{
+    assert (sizeof(bitmap_t) <= 4);
+
+    cell_ = NULL;
+    cells_number_ = 0;
+    allocated_cells_number_ = 0;
+    free_top_ = ROOT_REF;
+
+    const ref_t root_ref = alloc_cell();
+
+    assert (root_ref == ROOT_REF && cells_number_ > 0);
+}
+
+
+/**
+ * Destructor
+ */
+binmap_t::~binmap_t()
+{
+    if (cell_) {
+        free(cell_);
+    }
+}
+
+
+/**
+ * Allocates one cell (dirty allocation)
+ */
+ref_t binmap_t::_alloc_cell()
+{
+    assert (allocated_cells_number_ < cells_number_);
+
+    /* Pop an element from the free cell list */
+    const ref_t ref = free_top_;
+    assert (cell_[ref].is_free_);
+
+    free_top_ = cell_[ ref ].free_next_;
+
+    assert (!(cell_[ ref ].is_free_ = false));   /* Reset flag in DEBUG */
+
+    ++allocated_cells_number_;
+
+    return ref;
+}
+
+
+/**
+ * Allocates one cell
+ */
+ref_t binmap_t::alloc_cell()
+{
+    if (!reserve_cells(1)) {
+        return ROOT_REF /* MEMORY ERROR or OVERFLOW ERROR */;
+    }
+
+    const ref_t ref = _alloc_cell();
+
+    /* Cleans cell */
+    memset(&cell_[ref], 0, sizeof(cell_[0]));
+
+    return ref;
+}
+
+
+/**
+ * Reserve cells allocation capacity
+ */
+bool binmap_t::reserve_cells(size_t count)
+{
+    if (cells_number_ - allocated_cells_number_ < count) {
+        /* Finding new sizeof of the buffer */
+        const size_t old_cells_number = cells_number_;
+        const size_t new_cells_number = _max_(16U, _max_(2 * old_cells_number, allocated_cells_number_ + count));
+
+        /* Check for reference capacity */
+        if (static_cast<ref_t>(new_cells_number) < old_cells_number) {
+            fprintf(stderr, "Warning: binmap_t::reserve_cells: REFERENCE LIMIT ERROR\n");
+            return false /* REFERENCE LIMIT ERROR */;
+        }
+
+        /* Check for integer overflow */
+        static const size_t MAX_NUMBER = (static_cast<size_t>(-1) / sizeof(cell_[0]));
+        if (MAX_NUMBER < new_cells_number) {
+            fprintf(stderr, "Warning: binmap_t::reserve_cells: INTEGER OVERFLOW\n");
+            return false /* INTEGER OVERFLOW */;
+        }
+
+        /* Reallocate memory */
+        cell_t* const cell = static_cast<cell_t*>(realloc(cell_, new_cells_number * sizeof(cell_[0])));
+        if (cell == NULL) {
+            fprintf(stderr, "Warning: binmap_t::reserve_cells: MEMORY ERROR\n");
+            return false /* MEMORY ERROR */;
+        }
+
+        cell_ = cell;
+        cells_number_ = new_cells_number;
+
+        /* Insert new cells to the free cell list */
+        const size_t stop_idx = old_cells_number - 1;
+        size_t idx = new_cells_number - 1;
+
+        cell_[ idx ].is_free_ = true;
+        cell_[ idx ].free_next_ = free_top_;
+
+        for (--idx; idx != stop_idx; --idx) {
+            cell_[ idx ].is_free_ = true;
+            cell_[ idx ].free_next_ = static_cast<ref_t>(idx + 1);
+        }
+
+        free_top_ = static_cast<ref_t>(old_cells_number);
+    }
+
+    return true;
+}
+
+
+/**
+ * Releases the cell
+ */
+void binmap_t::free_cell(ref_t ref)
+{
+    assert (ref > 0);
+    assert (!cell_[ref].is_free_);
+
+    if (cell_[ref].is_left_ref_) {
+        free_cell(cell_[ref].left_.ref_);
+    }
+    if (cell_[ref].is_right_ref_) {
+        free_cell(cell_[ref].right_.ref_);
+    }
+    assert ((cell_[ref].is_free_ = true)); /* Set flag in DEBUG */
+    cell_[ref].free_next_ = free_top_;
+
+    free_top_ = ref;
+
+    --allocated_cells_number_;
+}
+
+
+/**
+ * Extend root
+ */
+bool binmap_t::extend_root()
+{
+    assert (!root_bin_.is_all());
+
+    if (!cell_[ROOT_REF].is_left_ref_ && !cell_[ROOT_REF].is_right_ref_ && cell_[ROOT_REF].left_.bitmap_ == cell_[ROOT_REF].right_.bitmap_) {
+        /* Setup the root cell */
+        cell_[ROOT_REF].right_.bitmap_ = BITMAP_EMPTY;
+
+    } else {
+        /* Allocate new cell */
+        const ref_t ref = alloc_cell();
+        if (ref == ROOT_REF) {
+            return false /* ALLOC ERROR */;
+        }
+
+        /* Move old root to the cell */
+        cell_[ref] = cell_[ROOT_REF];
+
+        /* Setup new root */
+        cell_[ROOT_REF].is_left_ref_ = true;
+        cell_[ROOT_REF].is_right_ref_ = false;
+
+        cell_[ROOT_REF].left_.ref_ = ref;
+        cell_[ROOT_REF].right_.bitmap_ = BITMAP_EMPTY;
+    }
+
+    /* Reset bin */
+    root_bin_.to_parent();
+    return true;
+}
+
+
+/**
+ * Pack a trace of cells
+ */
+void binmap_t::pack_cells(ref_t* href)
+{
+    ref_t ref = *href--;
+    if (ref == ROOT_REF) {
+        return;
+    }
+
+    if (cell_[ref].is_left_ref_ || cell_[ref].is_right_ref_ ||
+        cell_[ref].left_.bitmap_ != cell_[ref].right_.bitmap_) {
+        return;
+    }
+
+    const bitmap_t bitmap = cell_[ref].left_.bitmap_;
+
+    do {
+        ref = *href--;
+
+        if (!cell_[ref].is_left_ref_) {
+            if (cell_[ref].left_.bitmap_ != bitmap) {
+                break;
+            }
+
+        } else if (!cell_[ref].is_right_ref_) {
+            if (cell_[ref].right_.bitmap_ != bitmap) {
+                break;
+            }
+
+        } else {
+            break;
+        }
+
+    } while (ref != ROOT_REF);
+
+    const ref_t par_ref = href[2];
+
+    if (cell_[ref].is_left_ref_ && cell_[ref].left_.ref_ == par_ref) {
+        cell_[ref].is_left_ref_ = false;
+        cell_[ref].left_.bitmap_ = bitmap;
+    } else {
+        cell_[ref].is_right_ref_ = false;
+        cell_[ref].right_.bitmap_ = bitmap;
+    }
+
+    free_cell(par_ref);
+}
+
+
+/**
+ * Whether binmap is empty
+ */
+bool binmap_t::is_empty() const
+{
+    const cell_t& cell = cell_[ROOT_REF];
+
+    return !cell.is_left_ref_ && !cell.is_right_ref_ &&
+           cell.left_.bitmap_ == BITMAP_EMPTY && cell.right_.bitmap_ == BITMAP_EMPTY;
+}
+
+
+/**
+ * Whether binmap is filled
+ */
+bool binmap_t::is_filled() const
+{
+    const cell_t& cell = cell_[ROOT_REF];
+
+    return root_bin_.is_all() && !cell.is_left_ref_ && !cell.is_right_ref_ &&
+           cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED;
+}
+
+
+/**
+ * Whether range/bin is empty
+ */
+bool binmap_t::is_empty(const bin_t& bin) const
+{
+    /* Process hi-layers case */
+    if (!root_bin_.contains(bin)) {
+        return !bin.contains(root_bin_) || is_empty();
+    }
+
+    /* Trace the bin */
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    trace(&cur_ref, &cur_bin, bin);
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Process common case */
+    const cell_t& cell = cell_[cur_ref];
+
+    if (bin.layer_bits() > BITMAP_LAYER_BITS) {
+        if (bin < cur_bin) {
+            return cell.left_.bitmap_ == BITMAP_EMPTY;
+        }
+        if (cur_bin < bin) {
+            return cell.right_.bitmap_ == BITMAP_EMPTY;
+        }
+        return !cell.is_left_ref_ && !cell.is_right_ref_ &&
+                cell.left_.bitmap_ == BITMAP_EMPTY && cell.right_.bitmap_ == BITMAP_EMPTY;
+    }
+
+    /* Process low-layers case */
+    assert (bin != cur_bin);
+
+    const bitmap_t bm1 = (bin < cur_bin) ? cell.left_.bitmap_ : cell.right_.bitmap_;
+    const bitmap_t bm2 = BITMAP[ BITMAP_LAYER_BITS & bin.toUInt() ];
+
+    return (bm1 & bm2) == BITMAP_EMPTY;
+}
+
+
+/**
+ * Whether range/bin is filled
+ */
+bool binmap_t::is_filled(const bin_t& bin) const
+{
+    /* Process hi-layers case */
+    if (!root_bin_.contains(bin)) {
+        return false;
+    }
+
+    /* Trace the bin */
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    trace(&cur_ref, &cur_bin, bin);
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Process common case */
+    const cell_t& cell = cell_[cur_ref];
+
+    if (bin.layer_bits() > BITMAP_LAYER_BITS) {
+        if (bin < cur_bin) {
+            return cell.left_.bitmap_ == BITMAP_FILLED;
+        }
+        if (cur_bin < bin) {
+            return cell.right_.bitmap_ == BITMAP_FILLED;
+        }
+        return !cell.is_left_ref_ && !cell.is_right_ref_ &&
+               cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED;
+    }
+
+    /* Process low-layers case */
+    assert (bin != cur_bin);
+
+    const bitmap_t bm1 = (bin < cur_bin) ? cell.left_.bitmap_ : cell.right_.bitmap_;
+    const bitmap_t bm2 = BITMAP[ BITMAP_LAYER_BITS & bin.toUInt() ];
+
+    return (bm1 & bm2) == bm2;
+}
+
+
+/**
+ * Return the topmost solid bin which covers the specified bin
+ */
+bin_t binmap_t::cover(const bin_t& bin) const
+{
+    /* Process hi-layers case */
+    if (!root_bin_.contains(bin)) {
+        if (!bin.contains(root_bin_)) {
+            return root_bin_.sibling();
+        }
+        if (is_empty()) {
+            return bin_t::ALL;
+        }
+        return bin_t::NONE;
+    }
+
+    /* Trace the bin */
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    trace(&cur_ref, &cur_bin, bin);
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Process common case */
+    const cell_t& cell = cell_[cur_ref];
+
+    if (bin.layer_bits() > BITMAP_LAYER_BITS) {
+        if (bin < cur_bin) {
+            if (cell.left_.bitmap_ == BITMAP_EMPTY || cell.left_.bitmap_ == BITMAP_FILLED) {
+                return cur_bin.left();
+            }
+            return bin_t::NONE;
+        }
+        if (cur_bin < bin) {
+            if (cell.right_.bitmap_ == BITMAP_EMPTY || cell.right_.bitmap_ == BITMAP_FILLED) {
+                return cur_bin.right();
+            }
+            return bin_t::NONE;
+        }
+        if (cell.is_left_ref_ || cell.is_right_ref_) {
+            return bin_t::NONE;
+        }
+        if (cell.left_.bitmap_ != cell.right_.bitmap_) {
+            return bin_t::NONE;
+        }
+        assert (cur_bin == root_bin_);
+        if (cell.left_.bitmap_ == BITMAP_EMPTY) {
+            return bin_t::ALL;
+        }
+        if (cell.left_.bitmap_ == BITMAP_FILLED) {
+            return cur_bin;
+        }
+        return bin_t::NONE;
+    }
+
+    /* Process low-layers case */
+    assert (bin != cur_bin);
+
+    bitmap_t bm1;
+    if (bin < cur_bin) {
+        bm1 = cell.left_.bitmap_;
+        cur_bin.to_left();
+    } else {
+        bm1 = cell.right_.bitmap_;
+        cur_bin.to_right();
+    }
+
+    if (bm1 == BITMAP_EMPTY) {
+        if (is_empty()) {
+            return bin_t::ALL;
+        }
+        return cur_bin;
+    }
+    if (bm1 == BITMAP_FILLED) {
+        if (is_filled()) {
+            return bin_t::ALL;
+        }
+        return cur_bin;
+    }
+
+    /* Trace the bitmap */
+    bin_t b = bin;
+    bitmap_t bm2 = BITMAP[ BITMAP_LAYER_BITS & b.toUInt() ];
+
+    if ((bm1 & bm2) == BITMAP_EMPTY) {
+        do {
+            cur_bin = b;
+            b.to_parent();
+            bm2 = BITMAP[ BITMAP_LAYER_BITS & b.toUInt() ];
+        } while ((bm1 & bm2) == BITMAP_EMPTY);
+
+        return cur_bin;
+
+    } else if ((bm1 & bm2) == bm2) {
+        do {
+            cur_bin = b;
+            b.to_parent();
+            bm2 = BITMAP[ BITMAP_LAYER_BITS & b.toUInt() ];
+        } while ((bm1 & bm2) == bm2);
+
+        return cur_bin;
+    }
+
+    return bin_t::NONE;
+}
+
+
+/**
+ * Find first empty bin
+ */
+bin_t binmap_t::find_empty() const
+{
+    /* Trace the bin */
+    bitmap_t bitmap = BITMAP_FILLED;
+
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    do {
+        /* Processing the root */
+        if (cell_[ROOT_REF].is_left_ref_) {
+            cur_ref = cell_[ROOT_REF].left_.ref_;
+            cur_bin = root_bin_.left();
+        } else if (cell_[ROOT_REF].left_.bitmap_ != BITMAP_FILLED) {
+            if (cell_[ ROOT_REF].left_.bitmap_ == BITMAP_EMPTY) {
+                if (!cell_[ ROOT_REF].is_right_ref_ && cell_[ ROOT_REF ].right_.bitmap_ == BITMAP_EMPTY) {
+                    return bin_t::ALL;
+                }
+                return root_bin_.left();
+            }
+            bitmap = cell_[ROOT_REF].left_.bitmap_;
+            cur_bin = root_bin_.left();
+            break;
+        } else if (cell_[ROOT_REF].is_right_ref_) {
+            cur_ref = cell_[ROOT_REF].right_.ref_;
+            cur_bin = root_bin_.right();
+        } else {
+            if (cell_[ROOT_REF].right_.bitmap_ == BITMAP_FILLED) {
+                if (root_bin_.is_all()) {
+                    return bin_t::NONE;
+                }
+                return root_bin_.sibling();
+            }
+            bitmap = cell_[ROOT_REF].right_.bitmap_;
+            cur_bin = root_bin_.right();
+            break;
+        }
+
+        /* Processing middle layers */
+        for ( ;;) {
+            if (cell_[cur_ref].is_left_ref_) {
+                cur_ref = cell_[cur_ref].left_.ref_;
+                cur_bin.to_left();
+            } else if (cell_[cur_ref].left_.bitmap_ != BITMAP_FILLED) {
+                bitmap = cell_[cur_ref].left_.bitmap_;
+                cur_bin.to_left();
+                break;
+            } else if (cell_[cur_ref].is_right_ref_) {
+                cur_ref = cell_[cur_ref].right_.ref_;
+                cur_bin.to_right();
+            } else {
+                assert (cell_[cur_ref].right_.bitmap_ != BITMAP_FILLED);
+                bitmap = cell_[cur_ref].right_.bitmap_;
+                cur_bin.to_right();
+                break;
+            }
+        }
+
+    } while (false);
+
+    /* Getting result */
+    assert (bitmap != BITMAP_FILLED);
+
+    return bitmap_to_bin(cur_bin, ~bitmap);
+}
+
+
+/**
+ * Find first filled bin
+ */
+bin_t binmap_t::find_filled() const
+{
+    /* Trace the bin */
+    bitmap_t bitmap = BITMAP_EMPTY;
+
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    do {
+        /* Processing the root */
+        if (cell_[ROOT_REF].is_left_ref_) {
+            cur_ref = cell_[ROOT_REF].left_.ref_;
+            cur_bin = root_bin_.left();
+        } else if (cell_[ROOT_REF].left_.bitmap_ != BITMAP_EMPTY) {
+            if (cell_[ ROOT_REF].left_.bitmap_ == BITMAP_FILLED) {
+                if (!cell_[ ROOT_REF].is_right_ref_ && cell_[ ROOT_REF ].right_.bitmap_ == BITMAP_FILLED) {
+                    return root_bin_;
+                }
+                return root_bin_.left();
+            }
+            bitmap = cell_[ROOT_REF].left_.bitmap_;
+            cur_bin = root_bin_.left();
+            break;
+        } else if (cell_[ROOT_REF].is_right_ref_) {
+            cur_ref = cell_[ROOT_REF].right_.ref_;
+            cur_bin = root_bin_.right();
+        } else {
+            if (cell_[ROOT_REF].right_.bitmap_ == BITMAP_EMPTY) {
+                return bin_t::NONE;
+            }
+            bitmap = cell_[ROOT_REF].right_.bitmap_;
+            cur_bin = root_bin_.right();
+            break;
+        }
+
+        /* Processing middle layers */
+        for ( ;;) {
+            if (cell_[cur_ref].is_left_ref_) {
+                cur_ref = cell_[cur_ref].left_.ref_;
+                cur_bin.to_left();
+            } else if (cell_[cur_ref].left_.bitmap_ != BITMAP_EMPTY) {
+                bitmap = cell_[cur_ref].left_.bitmap_;
+                cur_bin.to_left();
+                break;
+            } else if (cell_[cur_ref].is_right_ref_) {
+                cur_ref = cell_[cur_ref].right_.ref_;
+                cur_bin.to_right();
+            } else {
+                assert (cell_[cur_ref].right_.bitmap_ != BITMAP_EMPTY);
+                bitmap = cell_[cur_ref].right_.bitmap_;
+                cur_bin.to_right();
+                break;
+            }
+        }
+
+    } while (false);
+
+    /* Getting result */
+    assert (bitmap != BITMAP_EMPTY);
+
+    return bitmap_to_bin(cur_bin, bitmap);
+}
+
+
+#define LR_LEFT   (0x00)
+#define RL_RIGHT  (0x01)
+#define RL_LEFT   (0x02)
+#define LR_RIGHT  (0x03)
+
+
+#define SSTACK()                                    \
+    int _top_ = 0;                                  \
+    bin_t _bin_[64];                                \
+    ref_t _sref_[64];                               \
+    char _dir_[64];
+
+#define DSTACK()                                    \
+    int _top_ = 0;                                  \
+    bin_t _bin_[64];                                \
+    ref_t _dref_[64];                               \
+    char _dir_[64];
+
+#define SDSTACK()                                   \
+    int _top_ = 0;                                  \
+    bin_t _bin_[64];                                \
+    ref_t _sref_[64];                               \
+    ref_t _dref_[64];                               \
+    char _dir_[64];
+
+
+#define SPUSH(b, sr, twist)                         \
+    do {                                            \
+        _bin_[_top_] = b;                           \
+        _sref_[_top_] = sr;                         \
+        _dir_[_top_] = (0 != (twist & (b.base_length() >> 1))); \
+        ++_top_;                                    \
+    } while (false)
+
+#define DPUSH(b, dr, twist)                         \
+    do {                                            \
+        _bin_[_top_] = b;                           \
+        _dref_[_top_] = dr;                         \
+        _dir_[_top_] = (0 != (twist & (b.base_length() >> 1))); \
+        ++_top_;                                    \
+    } while (false)
+
+#define SDPUSH(b, sr, dr, twist)                    \
+    do {                                            \
+        _bin_[_top_] = b;                           \
+        _sref_[_top_] = sr;                         \
+        _dref_[_top_] = dr;                         \
+        _dir_[_top_] = (0 != (twist & (b.base_length() >> 1))); \
+        ++_top_;                                    \
+    } while (false)
+
+
+#define SPOP()                                      \
+    assert (_top_ < 65);                            \
+    --_top_;                                        \
+    const bin_t b = _bin_[_top_];                   \
+    const cell_t& sc = source.cell_[_sref_[_top_]]; \
+    const bool is_left = !(_dir_[_top_] & 0x01);    \
+    if (0 == (_dir_[_top_] & 0x02)) {               \
+        _dir_[_top_++] ^= 0x03;                     \
+    }
+
+#define DPOP()                                      \
+    assert (_top_ < 65);                            \
+    --_top_;                                        \
+    const bin_t b = _bin_[_top_];                   \
+    const cell_t& dc = destination.cell_[_dref_[_top_]]; \
+    const bool is_left = !(_dir_[_top_] & 0x01);    \
+    if (0 == (_dir_[_top_] & 0x02)) {               \
+        _dir_[_top_++] ^= 0x03;                     \
+    }
+
+#define SDPOP()                                     \
+    assert (_top_ < 65);                            \
+    --_top_;                                        \
+    const bin_t b = _bin_[_top_];                   \
+    const cell_t& sc = source.cell_[_sref_[_top_]]; \
+    const cell_t& dc = destination.cell_[_dref_[_top_]]; \
+    const bool is_left = !(_dir_[_top_] & 0x01);    \
+    if (0 == (_dir_[_top_] & 0x02)) {               \
+        _dir_[_top_++] ^= 0x03;                     \
+    }
+
+
+/**
+ * Find first additional bin in source
+ *
+ * @param destination
+ *             the destination binmap
+ * @param source
+ *             the source binmap
+ */
+bin_t binmap_t::find_complement(const binmap_t& destination, const binmap_t& source, const bin_t::uint_t twist)
+{
+    return find_complement(destination, source, bin_t::ALL, twist);
+
+    if (destination.is_empty()) {
+        const cell_t& cell = source.cell_[ROOT_REF];
+        if (!cell.is_left_ref_ && !cell.is_right_ref_ && cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED) {
+            return source.root_bin_;
+        }
+        return _find_complement(source.root_bin_, BITMAP_EMPTY, ROOT_REF, source, twist);
+    }
+
+    if (destination.root_bin_.contains(source.root_bin_)) {
+        ref_t dref;
+        bin_t dbin;
+
+        destination.trace(&dref, &dbin, source.root_bin_);
+
+        if (dbin == source.root_bin_) {
+            return binmap_t::_find_complement(dbin, dref, destination, ROOT_REF, source, twist);
+        }
+
+        assert (source.root_bin_ < dbin);
+
+        if (destination.cell_[dref].left_.bitmap_ != BITMAP_FILLED) {
+            if (destination.cell_[dref].left_.bitmap_ == BITMAP_EMPTY) {
+                const cell_t& cell = source.cell_[ROOT_REF];
+                if (!cell.is_left_ref_ && !cell.is_right_ref_ && cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED) {
+                    return source.root_bin_;
+                }
+            }
+            return binmap_t::_find_complement(source.root_bin_, destination.cell_[dref].left_.bitmap_, ROOT_REF, source, twist);
+        }
+
+        return bin_t::NONE;
+
+    } else {
+        SSTACK();
+
+        /* Initialization */
+        SPUSH(source.root_bin_, ROOT_REF, twist);
+
+        /* Main loop */
+        do {
+            SPOP();
+
+            if (is_left) {
+                if (b.left() == destination.root_bin_) {
+                    if (sc.is_left_ref_) {
+                        const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.ref_, source, twist);
+                        if (!res.is_none()) {
+                            return res;
+                        }
+                    } else if (sc.left_.bitmap_ != BITMAP_EMPTY) {
+                        const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.bitmap_, twist);
+                        if (!res.is_none()) {
+                            return res;
+                        }
+                    }
+                    continue;
+                }
+
+                if (sc.is_left_ref_) {
+                    SPUSH(b.left(), sc.left_.ref_, twist);
+                    continue;
+
+                } else if (sc.left_.bitmap_ != BITMAP_EMPTY) {
+                    if (0 == (twist & (b.left().base_length() - 1) & ~(destination.root_bin_.base_length() - 1))) {
+                        const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.bitmap_, twist);
+                        if (!res.is_none()) {
+                            return res;
+                        }
+                        return binmap_t::_find_complement(destination.root_bin_.sibling(), BITMAP_EMPTY, sc.left_.bitmap_, twist);
+
+                    } else if (sc.left_.bitmap_ != BITMAP_FILLED) {
+                        return binmap_t::_find_complement(b.left(), BITMAP_EMPTY, sc.left_.bitmap_, twist);
+
+                    } else {
+                        bin_t::uint_t s = twist & (b.left().base_length() - 1);
+                        /* Sorry for the following hardcode hack: Flow the highest bit of s */
+                        s |= s >> 1; s |= s >> 2;
+                        s |= s >> 4; s |= s >> 8;
+                        s |= s >> 16;
+                        s |= (s >> 16) >> 16;   // FIXME: hide warning
+                        return bin_t(s + 1 + (s >> 1)); /* bin_t(s >> 1).sibling(); */
+                    }
+                }
+
+            } else {
+                if (sc.is_right_ref_) {
+                    return binmap_t::_find_complement(b.right(), BITMAP_EMPTY, sc.right_.ref_, source, twist);
+                } else if (sc.right_.bitmap_ != BITMAP_EMPTY) {
+                    return binmap_t::_find_complement(b.right(), BITMAP_EMPTY, sc.right_.bitmap_, twist);
+                }
+                continue;
+            }
+        } while (_top_ > 0);
+
+        return bin_t::NONE;
+    }
+}
+
+
+bin_t binmap_t::find_complement(const binmap_t& destination, const binmap_t& source, bin_t range, const bin_t::uint_t twist)
+{
+    ref_t sref = ROOT_REF;
+    bitmap_t sbitmap = BITMAP_EMPTY;
+    bool is_sref = true;
+
+    if (range.contains(source.root_bin_)) {
+        range = source.root_bin_;
+        is_sref = true;
+        sref = ROOT_REF;
+
+    } else if (source.root_bin_.contains(range)) {
+        bin_t sbin;
+        source.trace(&sref, &sbin, range);
+
+        if (range == sbin) {
+            is_sref = true;
+        } else {
+            is_sref = false;
+
+            if (range < sbin) {
+                sbitmap = source.cell_[sref].left_.bitmap_;
+            } else {
+                sbitmap = source.cell_[sref].right_.bitmap_;
+            }
+
+            sbitmap &= BITMAP[ BITMAP_LAYER_BITS & range.toUInt() ];
+
+            if (sbitmap == BITMAP_EMPTY) {
+                return bin_t::NONE;
+            }
+        }
+
+    } else {
+        return bin_t::NONE;
+    }
+
+    assert (is_sref || sbitmap != BITMAP_EMPTY);
+
+    if (destination.is_empty()) {
+        if (is_sref) {
+            const cell_t& cell = source.cell_[sref];
+            if (!cell.is_left_ref_ && !cell.is_right_ref_ && cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED) {
+                return range;
+            } else {
+                return _find_complement(range, BITMAP_EMPTY, sref, source, twist);
+            }
+        } else {
+            return _find_complement(range, BITMAP_EMPTY, sbitmap, twist);
+        }
+    }
+
+    if (destination.root_bin_.contains(range)) {
+        ref_t dref;
+        bin_t dbin;
+        destination.trace(&dref, &dbin, range);
+
+        if (range == dbin) {
+            if (is_sref) {
+                return _find_complement(range, dref, destination, sref, source, twist);
+            } else {
+                return _find_complement(range, dref, destination, sbitmap, twist);
+            }
+
+        } else {
+            bitmap_t dbitmap;
+
+            if (range < dbin) {
+                dbitmap = destination.cell_[dref].left_.bitmap_;
+            } else {
+                dbitmap = destination.cell_[dref].right_.bitmap_;
+            }
+
+            if (dbitmap == BITMAP_FILLED) {
+                return bin_t::NONE;
+
+            } else if (is_sref) {
+                if (dbitmap == BITMAP_EMPTY) {
+                    const cell_t& cell = source.cell_[sref];
+                    if (!cell.is_left_ref_ && !cell.is_right_ref_ && cell.left_.bitmap_ == BITMAP_FILLED && cell.right_.bitmap_ == BITMAP_FILLED) {
+                        return range;
+                    }
+                }
+
+                return _find_complement(range, dbitmap, sref, source, twist);
+
+            } else {
+                if ((sbitmap & ~dbitmap) != BITMAP_EMPTY) {
+                    return _find_complement(range, dbitmap, sbitmap, twist);
+                } else {
+                    return bin_t::NONE;
+                }
+            }
+        }
+
+    } else if (!range.contains(destination.root_bin_)) {
+        if (is_sref) {
+            return _find_complement(range, BITMAP_EMPTY, sref, source, twist);
+        } else {
+            return _find_complement(range, BITMAP_EMPTY, sbitmap, twist);
+        }
+
+    } else { // range.contains(destination.m_root_bin)
+        if (is_sref) {
+            SSTACK();
+
+            SPUSH(range, sref, twist);
+
+            do {
+                SPOP();
+
+                if (is_left) {
+                    if (b.left() == destination.root_bin_) {
+                        if (sc.is_left_ref_) {
+                            const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.ref_, source, twist);
+                            if (!res.is_none()) {
+                                return res;
+                            }
+                        } else if (sc.left_.bitmap_ != BITMAP_EMPTY) {
+                            const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.bitmap_, twist);
+                            if (!res.is_none()) {
+                                return res;
+                            }
+                        }
+                        continue;
+                    }
+
+                    if (sc.is_left_ref_) {
+                        SPUSH(b.left(), sc.left_.ref_, twist);
+                        continue;
+
+                    } else if (sc.left_.bitmap_ != BITMAP_EMPTY) {
+                        if (0 == (twist & (b.left().base_length() - 1) & ~(destination.root_bin_.base_length() - 1))) {
+                            const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sc.left_.bitmap_, twist);
+                            if (!res.is_none()) {
+                                return res;
+                            }
+                            return binmap_t::_find_complement(destination.root_bin_.sibling(), BITMAP_EMPTY, sc.left_.bitmap_, twist);
+
+                        } else if (sc.left_.bitmap_ != BITMAP_FILLED) {
+                            return binmap_t::_find_complement(b.left(), BITMAP_EMPTY, sc.left_.bitmap_, twist);
+
+                        } else {
+                            bin_t::uint_t s = twist & (b.left().base_length() - 1);
+                            /* Sorry for the following hardcode hack: Flow the highest bit of s */
+                            s |= s >> 1; s |= s >> 2;
+                            s |= s >> 4; s |= s >> 8;
+                            s |= s >> 16;
+                            s |= (s >> 16) >> 16;   // FIXME: hide warning
+                            return bin_t(s + 1 + (s >> 1)); /* bin_t(s >> 1).sibling(); */
+                        }
+                    }
+
+                } else {
+                    if (sc.is_right_ref_) {
+                        return binmap_t::_find_complement(b.right(), BITMAP_EMPTY, sc.right_.ref_, source, twist);
+                    } else if (sc.right_.bitmap_ != BITMAP_EMPTY) {
+                        return binmap_t::_find_complement(b.right(), BITMAP_EMPTY, sc.right_.bitmap_, twist);
+                    }
+                    continue;
+                }
+            } while (_top_ > 0);
+
+            return bin_t::NONE;
+
+        } else {
+            if (0 == (twist & (range.base_length() - 1) & ~(destination.root_bin_.base_length() - 1))) {
+                const bin_t res = binmap_t::_find_complement(destination.root_bin_, ROOT_REF, destination, sbitmap, twist);
+                if (!res.is_none()) {
+                    return res;
+                }
+                return binmap_t::_find_complement(destination.root_bin_.sibling(), BITMAP_EMPTY, sbitmap, twist);
+
+            } else if (sbitmap != BITMAP_FILLED) {
+                return binmap_t::_find_complement(range, BITMAP_EMPTY, sbitmap, twist);
+
+            } else {
+                bin_t::uint_t s = twist & (range.base_length() - 1);
+                /* Sorry for the following hardcode hack: Flow the highest bit of s */
+                s |= s >> 1; s |= s >> 2;
+                s |= s >> 4; s |= s >> 8;
+                s |= s >> 16;
+                s |= (s >> 16) >> 16;   // FIXME: hide warning
+                return bin_t(s + 1 + (s >> 1)); /* bin_t(s >> 1).sibling(); */
+            }
+        }
+    }
+}
+
+
+bin_t binmap_t::_find_complement(const bin_t& bin, const ref_t dref, const binmap_t& destination, const ref_t sref, const binmap_t& source, const bin_t::uint_t twist)
+{
+    /* Initialization */
+    SDSTACK();
+    SDPUSH(bin, sref, dref, twist);
+
+    /* Main loop */
+    do {
+        SDPOP();
+
+        if (is_left) {
+            if (sc.is_left_ref_) {
+                if (dc.is_left_ref_) {
+                    SDPUSH(b.left(), sc.left_.ref_, dc.left_.ref_, twist);
+                    continue;
+
+                } else if (dc.left_.bitmap_ != BITMAP_FILLED) {
+                    const bin_t res = binmap_t::_find_complement(b.left(), dc.left_.bitmap_, sc.left_.ref_, source, twist);
+                    if (!res.is_none()) {
+                        return res;
+                    }
+                    continue;
+                }
+
+            } else if (sc.left_.bitmap_ != BITMAP_EMPTY) {
+                if (dc.is_left_ref_) {
+                    const bin_t res = binmap_t::_find_complement(b.left(), dc.left_.ref_, destination, sc.left_.bitmap_, twist);
+                    if (!res.is_none()) {
+                        return res;
+                    }
+                    continue;
+
+                } else if ((sc.left_.bitmap_ & ~dc.left_.bitmap_) != BITMAP_EMPTY) {
+                    return binmap_t::_find_complement(b.left(), dc.left_.bitmap_, sc.left_.bitmap_, twist);
+                }
+            }
+
+        } else {
+            if (sc.is_right_ref_) {
+                if (dc.is_right_ref_) {
+                    SDPUSH(b.right(), sc.right_.ref_, dc.right_.ref_, twist);
+                    continue;
+
+                } else if (dc.right_.bitmap_ != BITMAP_FILLED) {
+                    const bin_t res = binmap_t::_find_complement(b.right(), dc.right_.bitmap_, sc.right_.ref_, source, twist);
+                    if (!res.is_none()) {
+                        return res;
+                    }
+                    continue;
+                }
+
+            } else if (sc.right_.bitmap_ != BITMAP_EMPTY) {
+                if (dc.is_right_ref_) {
+                    const bin_t res = binmap_t::_find_complement(b.right(), dc.right_.ref_, destination, sc.right_.bitmap_, twist);
+                    if (!res.is_none()) {
+                        return res;
+                    }
+                    continue;
+
+                } else if ((sc.right_.bitmap_ & ~dc.right_.bitmap_) != BITMAP_EMPTY) {
+                    return binmap_t::_find_complement(b.right(), dc.right_.bitmap_, sc.right_.bitmap_, twist);
+                }
+            }
+        }
+    } while (_top_ > 0);
+
+    return bin_t::NONE;
+}
+
+
+bin_t binmap_t::_find_complement(const bin_t& bin, const bitmap_t dbitmap, const ref_t sref, const binmap_t& source, const bin_t::uint_t twist)
+{
+    assert (dbitmap != BITMAP_EMPTY || sref != ROOT_REF ||
+            source.cell_[ROOT_REF].is_left_ref_ ||
+            source.cell_[ROOT_REF].is_right_ref_ ||
+            source.cell_[ROOT_REF].left_.bitmap_ != BITMAP_FILLED ||
+            source.cell_[ROOT_REF].right_.bitmap_ != BITMAP_FILLED);
+
+    /* Initialization */
+    SSTACK();
+    SPUSH(bin, sref, twist);
+
+    /* Main loop */
+    do {
+        SPOP();
+
+        if (is_left) {
+            if (sc.is_left_ref_) {
+                SPUSH(b.left(), sc.left_.ref_, twist);
+                continue;
+            } else if ((sc.left_.bitmap_ & ~dbitmap) != BITMAP_EMPTY) {
+                return binmap_t::_find_complement(b.left(), dbitmap, sc.left_.bitmap_, twist);
+            }
+
+        } else {
+            if (sc.is_right_ref_) {
+                SPUSH(b.right(), sc.right_.ref_, twist);
+                continue;
+            } else if ((sc.right_.bitmap_ & ~dbitmap) != BITMAP_EMPTY) {
+                return binmap_t::_find_complement(b.right(), dbitmap, sc.right_.bitmap_, twist);
+            }
+        }
+    } while (_top_ > 0);
+
+    return bin_t::NONE;
+}
+
+
+bin_t binmap_t::_find_complement(const bin_t& bin, const ref_t dref, const binmap_t& destination, const bitmap_t sbitmap, const bin_t::uint_t twist)
+{
+    /* Initialization */
+    DSTACK();
+    DPUSH(bin, dref, twist);
+
+    /* Main loop */
+    do {
+        DPOP();
+
+        if (is_left) {
+            if (dc.is_left_ref_) {
+                DPUSH(b.left(), dc.left_.ref_, twist);
+                continue;
+
+            } else if ((sbitmap & ~dc.left_.bitmap_) != BITMAP_EMPTY) {
+                return binmap_t::_find_complement(b.left(), dc.left_.bitmap_, sbitmap, twist);
+            }
+
+        } else {
+            if (dc.is_right_ref_) {
+                DPUSH(b.right(), dc.right_.ref_, twist);
+                continue;
+
+            } else if ((sbitmap & ~dc.right_.bitmap_) != BITMAP_EMPTY) {
+                return binmap_t::_find_complement(b.right(), dc.right_.bitmap_, sbitmap, twist);
+            }
+        }
+    } while (_top_ > 0);
+
+    return bin_t::NONE;
+}
+
+
+bin_t binmap_t::_find_complement(const bin_t& bin, const bitmap_t dbitmap, const bitmap_t sbitmap, bin_t::uint_t twist)
+{
+    bitmap_t bitmap = sbitmap & ~dbitmap;
+
+    assert (bitmap != BITMAP_EMPTY);
+
+    if (bitmap == BITMAP_FILLED) {
+        return bin;
+    }
+
+    twist &= bin.base_length() - 1;
+
+    if (sizeof(bitmap_t) == 2) {
+        if (twist & 1) {
+            bitmap = ((bitmap & 0x5555) << 1)  | ((bitmap & 0xAAAA) >> 1);
+        }
+        if (twist & 2) {
+            bitmap = ((bitmap & 0x3333) << 2)  | ((bitmap & 0xCCCC) >> 2);
+        }
+        if (twist & 4) {
+            bitmap = ((bitmap & 0x0f0f) << 4)  | ((bitmap & 0xf0f0) >> 4);
+        }
+        if (twist & 8) {
+            bitmap = ((bitmap & 0x00ff) << 8)  | ((bitmap & 0xff00) >> 8);
+        }
+
+        // Arno, 2012-03-21: Do workaround (see below) here as well?
+
+        return bin_t(bin.base_left().twisted(twist & ~0x0f).toUInt() + bitmap_to_bin(bitmap)).to_twisted(twist & 0x0f);
+
+    } else {
+        if (twist & 1) {
+            bitmap = ((bitmap & 0x55555555) << 1)  | ((bitmap & 0xAAAAAAAA) >> 1);
+        }
+        if (twist & 2) {
+            bitmap = ((bitmap & 0x33333333) << 2)  | ((bitmap & 0xCCCCCCCC) >> 2);
+        }
+        if (twist & 4) {
+            bitmap = ((bitmap & 0x0f0f0f0f) << 4)  | ((bitmap & 0xf0f0f0f0) >> 4);
+        }
+        if (twist & 8) {
+            bitmap = ((bitmap & 0x00ff00ff) << 8)  | ((bitmap & 0xff00ff00) >> 8);
+        }
+        if (twist & 16) {
+            bitmap = ((bitmap & 0x0000ffff) << 16)  | ((bitmap & 0xffff0000) >> 16);
+        }
+
+        bin_t diff = bin_t(bin.base_left().twisted(twist & ~0x1f).toUInt() + bitmap_to_bin(bitmap)).to_twisted(twist & 0x1f);
+
+        // Arno, 2012-03-21: Sanity check, if it fails, attempt workaround
+        if (!bin.contains(diff))
+        {
+               // Bug: Proposed bin is outside of specified range. The bug appears
+               // to be that the code assumes that the range parameter (called bin
+               // here) is aligned on a 32-bit boundary. I.e. the width of a
+               // half_t. Hence when the code does range + bitmap_to_bin(x)
+               // to find the base-layer offset of the bit on which the source
+               // and dest bitmaps differ, the result may be too high.
+               //
+               // What I do here is to round the rangestart to 32 bits, and
+               // then add bitmap_to_bin(bitmap), divided by two as that function
+               // returns the bit in a "bin number" format (=bit * 2).
+               //
+               // In other words, the "bin" parameter should tell us at what
+               // base offset of the 32-bit dbitmap and sbitmap is. At the moment
+               // it doesn't always, because "bin" is not rounded to 32-bit.
+               //
+               // see tests/binstest3.cpp
+
+               bin_t::uint_t rangestart = bin.base_left().twisted(twist & ~0x1f).layer_offset();
+               bin_t::uint_t b2b = bitmap_to_bin(bitmap);
+               bin_t::uint_t absoff = ((int)(rangestart/32))*32 + b2b/2;
+
+               diff = bin_t(0,absoff);
+               diff = diff.to_twisted(twist & 0x1f);
+
+               //char binstr[32];
+               //fprintf(stderr,"__fc solution %s\n", diff.str(binstr) );
+        }
+        return diff;
+    }
+}
+
+
+/**
+ * Sets bins
+ *
+ * @param bin
+ *             the bin
+ */
+void binmap_t::set(const bin_t& bin)
+{
+    if (bin.is_none()) {
+        return;
+    }
+
+    if (bin.layer_bits() > BITMAP_LAYER_BITS) {
+        _set__high_layer_bitmap(bin, BITMAP_FILLED);
+    } else {
+        _set__low_layer_bitmap(bin, BITMAP_FILLED);
+    }
+}
+
+
+/**
+ * Resets bins
+ *
+ * @param bin
+ *             the bin
+ */
+void binmap_t::reset(const bin_t& bin)
+{
+    if (bin.is_none()) {
+        return;
+    }
+
+    if (bin.layer_bits() > BITMAP_LAYER_BITS) {
+        _set__high_layer_bitmap(bin, BITMAP_EMPTY);
+    } else {
+        _set__low_layer_bitmap(bin, BITMAP_EMPTY);
+    }
+}
+
+
+/**
+ * Empty all bins
+ */
+void binmap_t::clear()
+{
+    cell_t& cell = cell_[ROOT_REF];
+
+    if (cell.is_left_ref_) {
+        free_cell(cell.left_.ref_);
+    }
+    if (cell.is_right_ref_) {
+        free_cell(cell.right_.ref_);
+    }
+
+    cell.is_left_ref_ = false;
+    cell.is_right_ref_ = false;
+    cell.left_.bitmap_ = BITMAP_EMPTY;
+    cell.right_.bitmap_ = BITMAP_EMPTY;
+}
+
+
+/**
+ * Fill the binmap. Creates a new filled binmap. Size is given by the source root
+ */
+void binmap_t::fill(const binmap_t& source)
+{
+    root_bin_ = source.root_bin_;
+    /* Extends root if needed */
+    while (!root_bin_.contains(source.root_bin_)) {
+        if (!extend_root()) {
+            return /* ALLOC ERROR */;
+        }
+    }
+    set(source.root_bin_);
+
+    cell_t& cell = cell_[ROOT_REF];
+
+    cell.is_left_ref_ = false;
+    cell.is_right_ref_ = false;
+    cell.left_.bitmap_ = BITMAP_FILLED;
+    cell.right_.bitmap_ = BITMAP_FILLED;
+}
+
+
+
+/**
+ * Get number of allocated cells
+ */
+size_t binmap_t::cells_number() const
+{
+    return allocated_cells_number_;
+}
+
+
+/**
+ * Get total size of the binmap
+ */
+size_t binmap_t::total_size() const
+{
+    return sizeof(*this) + sizeof(cell_[0]) * cells_number_;
+}
+
+
+
+/**
+ * Echo the binmap status to stdout
+ */
+void binmap_t::status() const
+{
+    printf("bitmap:\n");
+    for (int i = 0; i < 16; ++i) {
+        for (int j = 0; j < 64; ++j) {
+            printf("%d", is_filled(bin_t(i * 64 + j)));
+        }
+        printf("\n");
+    }
+
+    printf("size: %u bytes\n", static_cast<unsigned int>(total_size()));
+    printf("cells number: %u (of %u)\n", static_cast<unsigned int>(allocated_cells_number_), static_cast<unsigned int>(cells_number_));
+    printf("root bin: %llu\n", static_cast<unsigned long long>(root_bin_.toUInt()));
+}
+
+
+/** Trace the bin */
+inline void binmap_t::trace(ref_t* ref, bin_t* bin, const bin_t& target) const
+{
+    assert (root_bin_.contains(target));
+
+    ref_t cur_ref = ROOT_REF;
+    bin_t cur_bin = root_bin_;
+
+    while (target != cur_bin) {
+        if (target < cur_bin) {
+            if (cell_[cur_ref].is_left_ref_) {
+                cur_ref = cell_[cur_ref].left_.ref_;
+                cur_bin.to_left();
+            } else {
+                break;
+            }
+        } else {
+            if (cell_[cur_ref].is_right_ref_) {
+                cur_ref = cell_[cur_ref].right_.ref_;
+                cur_bin.to_right();
+            } else {
+                break;
+            }
+        }
+    }
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    if (ref) {
+        *ref = cur_ref;
+    }
+    if (bin) {
+        *bin = cur_bin;
+    }
+}
+
+
+/** Trace the bin */
+inline void binmap_t::trace(ref_t* ref, bin_t* bin, ref_t** history, const bin_t& target) const
+{
+    assert (history);
+    assert (root_bin_.contains(target));
+
+    ref_t* href = *history;
+    ref_t cur_ref = ROOT_REF;
+    bin_t cur_bin = root_bin_;
+
+    *href++ = ROOT_REF;
+    while (target != cur_bin) {
+        if (target < cur_bin) {
+            if (cell_[cur_ref].is_left_ref_) {
+                cur_ref = cell_[cur_ref].left_.ref_;
+                cur_bin.to_left();
+            } else {
+                break;
+            }
+        } else {
+            if (cell_[cur_ref].is_right_ref_) {
+                cur_ref = cell_[cur_ref].right_.ref_;
+                cur_bin.to_right();
+            } else {
+                break;
+            }
+        }
+
+        *href++ = cur_ref;
+    }
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    if (ref) {
+        *ref = cur_ref;
+    }
+    if (bin) {
+        *bin = cur_bin;
+    }
+
+    *history = href;
+}
+
+
+/**
+ * Copy a binmap to another
+ */
+void binmap_t::copy(binmap_t& destination, const binmap_t& source)
+{
+    destination.root_bin_ = source.root_bin_;
+    binmap_t::copy(destination, ROOT_REF, source, ROOT_REF);
+}
+
+
+/**
+ * Copy a range from one binmap to another binmap
+ */
+void binmap_t::copy(binmap_t& destination, const binmap_t& source, const bin_t& range)
+{
+    ref_t int_ref;
+    bin_t int_bin;
+
+    if (range.contains(destination.root_bin_)) {
+        if (source.root_bin_.contains(range)) {
+            source.trace(&int_ref, &int_bin, range);
+            destination.root_bin_ = range;
+            binmap_t::copy(destination, ROOT_REF, source, int_ref);
+        } else if (range.contains(source.root_bin_)) {
+            destination.root_bin_ = source.root_bin_;
+            binmap_t::copy(destination, ROOT_REF, source, ROOT_REF);
+        } else {
+            destination.reset(range);
+        }
+
+    } else {
+        if (source.root_bin_.contains(range)) {
+            source.trace(&int_ref, &int_bin, range);
+
+            const cell_t& cell = source.cell_[int_ref];
+
+            if (range.layer_bits() <= BITMAP_LAYER_BITS) {
+                if (range < int_bin) {
+                    destination._set__low_layer_bitmap(range, cell.left_.bitmap_);
+                } else {
+                    destination._set__low_layer_bitmap(range, cell.right_.bitmap_);
+                }
+
+            } else {
+                if (range == int_bin) {
+                    if (cell.is_left_ref_ || cell.is_right_ref_ || cell.left_.bitmap_ != cell.right_.bitmap_) {
+                        binmap_t::_copy__range(destination, source, int_ref, range);
+                    } else {
+                        destination._set__high_layer_bitmap(range, cell.left_.bitmap_);
+                    }
+                } else if (range < int_bin) {
+                    destination._set__high_layer_bitmap(range, cell.left_.bitmap_);
+                } else {
+                    destination._set__high_layer_bitmap(range, cell.right_.bitmap_);
+                }
+            }
+
+        } else if (range.contains(source.root_bin_)) {
+            destination.reset(range);   // Probably it could be optimized
+
+            const cell_t& cell = source.cell_[ ROOT_REF ];
+
+            if (cell.is_left_ref_ || cell.is_right_ref_ || cell.left_.bitmap_ != cell.right_.bitmap_) {
+                binmap_t::_copy__range(destination, source, ROOT_REF, source.root_bin_);
+            } else {
+                destination._set__high_layer_bitmap(source.root_bin_, cell.left_.bitmap_);
+            }
+
+        } else {
+            destination.reset(range);
+        }
+    }
+}
+
+
+inline void binmap_t::_set__low_layer_bitmap(const bin_t& bin, const bitmap_t _bitmap)
+{
+    assert (bin.layer_bits() <= BITMAP_LAYER_BITS);
+
+    const bitmap_t bin_bitmap = BITMAP[ bin.toUInt() & BITMAP_LAYER_BITS ];
+    const bitmap_t bitmap = _bitmap & bin_bitmap;
+
+    /* Extends root if needed */
+    if (!root_bin_.contains(bin)) {
+        /* Trivial case */
+        if (bitmap == BITMAP_EMPTY) {
+            return;
+        }
+        do {
+            if (!extend_root()) {
+                return /* ALLOC ERROR */;
+            }
+        } while (!root_bin_.contains(bin));
+    }
+
+    /* Get the pre-range */
+    const bin_t pre_bin( (bin.toUInt() & (~(BITMAP_LAYER_BITS + 1))) | BITMAP_LAYER_BITS );
+
+    /* The trace the bin with history */
+    ref_t _href[64];
+    ref_t* href = _href;
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    /* Process first stage -- do not touch existed tree */
+    trace(&cur_ref, &cur_bin, &href, pre_bin);
+
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Checking that we need to do anything */
+    bitmap_t bm = BITMAP_EMPTY;
+    {
+        cell_t& cell = cell_[cur_ref];
+
+        if (bin < cur_bin) {
+            assert (!cell.is_left_ref_);
+            bm = cell.left_.bitmap_;
+            if ((bm & bin_bitmap) == bitmap) {
+                return;
+            }
+            if (cur_bin == pre_bin) {
+                cell.left_.bitmap_ = (cell.left_.bitmap_ & ~bin_bitmap) | bitmap;
+                pack_cells(href - 1);
+                return;
+            }
+        } else {
+            assert (!cell.is_right_ref_);
+            bm = cell.right_.bitmap_;
+            if ((bm & bin_bitmap) == bitmap) {
+                return;
+            }
+            if (cur_bin == pre_bin) {
+                cell.right_.bitmap_ = (cell.right_.bitmap_ & ~bin_bitmap) | bitmap;
+                pack_cells(href - 1);
+                return;
+            }
+        }
+    }
+
+    /* Reserving proper number of cells */
+    if (!reserve_cells( cur_bin.layer() - pre_bin.layer() )) {
+        return /* MEMORY ERROR or OVERFLOW ERROR */;
+    }
+
+    /* Continue to trace */
+    do {
+        const ref_t ref = _alloc_cell();
+
+        cell_[ref].is_left_ref_ = false;
+        cell_[ref].is_right_ref_ = false;
+        cell_[ref].left_.bitmap_ = bm;
+        cell_[ref].right_.bitmap_ = bm;
+
+        if (pre_bin < cur_bin) {
+            cell_[cur_ref].is_left_ref_ = true;
+            cell_[cur_ref].left_.ref_ = ref;
+            cur_bin.to_left();
+        } else {
+            cell_[cur_ref].is_right_ref_ = true;
+            cell_[cur_ref].right_.ref_ = ref;
+            cur_bin.to_right();
+        }
+
+        cur_ref = ref;
+    } while (cur_bin != pre_bin);
+
+    assert (cur_bin == pre_bin);
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Complete setting */
+    if (bin < cur_bin) {
+        cell_[cur_ref].left_.bitmap_ = (cell_[cur_ref].left_.bitmap_ & ~bin_bitmap) | bitmap;
+    } else {
+        cell_[cur_ref].right_.bitmap_ = (cell_[cur_ref].right_.bitmap_ & ~bin_bitmap) | bitmap;
+    }
+}
+
+
+inline void binmap_t::_set__high_layer_bitmap(const bin_t& bin, const bitmap_t bitmap)
+{
+    assert (bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* First trivial case */
+    if (bin.contains(root_bin_)) {
+        cell_t& cell = cell_[ROOT_REF];
+        if (cell.is_left_ref_) {
+            free_cell(cell.left_.ref_);
+        }
+        if (cell.is_right_ref_) {
+            free_cell(cell.right_.ref_);
+        }
+
+        root_bin_ = bin;
+        cell.is_left_ref_ = false;
+        cell.is_right_ref_ = false;
+        cell.left_.bitmap_ = bitmap;
+        cell.right_.bitmap_ = bitmap;
+
+        return;
+    }
+
+    /* Get the pre-range */
+    bin_t pre_bin = bin.parent();
+
+    /* Extends root if needed */
+    if (!root_bin_.contains(pre_bin)) {
+        /* Second trivial case */
+        if (bitmap == BITMAP_EMPTY) {
+            return;
+        }
+
+        do {
+            if (!extend_root()) {
+                return /* ALLOC ERROR */;
+            }
+        } while (!root_bin_.contains(pre_bin));
+    }
+
+    /* The trace the bin with history */
+    ref_t _href[64];
+    ref_t* href = _href;
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    /* Process first stage -- do not touch existed tree */
+    trace(&cur_ref, &cur_bin, &href, pre_bin);
+
+    /* Checking that we need to do anything */
+    bitmap_t bm = BITMAP_EMPTY;
+    {
+        cell_t& cell = cell_[cur_ref];
+        if (bin < cur_bin) {
+            if (cell.is_left_ref_) {
+                /* assert (cur_bin == pre_bin); */
+                cell.is_left_ref_ = false;
+                free_cell(cell.left_.ref_);
+            } else {
+                bm = cell.left_.bitmap_;
+                if (bm == bitmap) {
+                    return;
+                }
+            }
+            if (cur_bin == pre_bin) {
+                cell.left_.bitmap_ = bitmap;
+                pack_cells(href - 1);
+                return;
+            }
+        } else {
+            if (cell.is_right_ref_) {
+                /* assert (cur_bin == pre_bin); */
+                cell.is_right_ref_ = false;
+                free_cell(cell.right_.ref_);
+            } else {
+                bm = cell.right_.bitmap_;
+                if (bm == bitmap) {
+                    return;
+                }
+            }
+            if (cur_bin == pre_bin) {
+                cell.right_.bitmap_ = bitmap;
+                pack_cells(href - 1);
+                return;
+            }
+        }
+    }
+
+    /* Reserving proper number of cells */
+    if (!reserve_cells( cur_bin.layer() - pre_bin.layer() )) {
+        return /* MEMORY ERROR or OVERFLOW ERROR */;
+    }
+
+    /* Continue to trace */
+    do {
+        const ref_t ref = _alloc_cell();
+
+        cell_[ref].is_left_ref_ = false;
+        cell_[ref].is_right_ref_ = false;
+        cell_[ref].left_.bitmap_ = bm;
+        cell_[ref].right_.bitmap_ = bm;
+
+        if (pre_bin < cur_bin) {
+            cell_[cur_ref].is_left_ref_ = true;
+            cell_[cur_ref].left_.ref_ = ref;
+            cur_bin.to_left();
+        } else {
+            cell_[cur_ref].is_right_ref_ = true;
+            cell_[cur_ref].right_.ref_ = ref;
+            cur_bin.to_right();
+        }
+
+        cur_ref = ref;
+    } while (cur_bin != pre_bin);
+
+    assert (cur_bin == pre_bin);
+    assert (cur_bin.layer_bits() > BITMAP_LAYER_BITS);
+
+    /* Complete setting */
+    if (bin < cur_bin) {
+        cell_[cur_ref].left_.bitmap_ = bitmap;
+    } else {
+        cell_[cur_ref].right_.bitmap_ = bitmap;
+    }
+}
+
+
+void binmap_t::_copy__range(binmap_t& destination, const binmap_t& source, const ref_t sref, const bin_t sbin)
+{
+    assert (sbin.layer_bits() > BITMAP_LAYER_BITS);
+
+    assert (sref == ROOT_REF ||
+            source.cell_[ sref ].is_left_ref_ || source.cell_[ sref ].is_right_ref_ ||
+            source.cell_[ sref ].left_.bitmap_ != source.cell_[ sref ].right_.bitmap_
+    );
+
+    /* Extends root if needed */
+    while (!destination.root_bin_.contains(sbin)) {
+        if (!destination.extend_root()) {
+            return /* ALLOC ERROR */;
+        }
+    }
+
+    /* The trace the bin */
+    ref_t cur_ref;
+    bin_t cur_bin;
+
+    /* Process first stage -- do not touch existed tree */
+    destination.trace(&cur_ref, &cur_bin, sbin);
+
+    /* Continue unpacking if needed */
+    if (cur_bin != sbin) {
+        bitmap_t bm = BITMAP_EMPTY;
+
+        if (sbin < cur_bin) {
+            bm = destination.cell_[cur_ref].left_.bitmap_;
+        } else {
+            bm = destination.cell_[cur_ref].right_.bitmap_;
+        }
+
+        /* Reserving proper number of cells */
+        if (!destination.reserve_cells( cur_bin.layer() - sbin.layer() )) {
+            return /* MEMORY ERROR or OVERFLOW ERROR */;
+        }
+
+        /* Continue to trace */
+        do {
+            const ref_t ref = destination._alloc_cell();
+
+            destination.cell_[ref].is_left_ref_ = false;
+            destination.cell_[ref].is_right_ref_ = false;
+            destination.cell_[ref].left_.bitmap_ = bm;
+            destination.cell_[ref].right_.bitmap_ = bm;
+
+            if (sbin < cur_bin) {
+                destination.cell_[cur_ref].is_left_ref_ = true;
+                destination.cell_[cur_ref].left_.ref_ = ref;
+                cur_bin.to_left();
+            } else {
+                destination.cell_[cur_ref].is_right_ref_ = true;
+                destination.cell_[cur_ref].right_.ref_ = ref;
+                cur_bin.to_right();
+            }
+
+            cur_ref = ref;
+        } while (cur_bin != sbin);
+    }
+
+    /* Make copying */
+    copy(destination, cur_ref, source, sref);
+}
+
+
+/**
+ * Clone binmap cells to another binmap
+ */
+void binmap_t::copy(binmap_t& destination, const ref_t dref, const binmap_t& source, const ref_t sref)
+{
+    assert (dref == ROOT_REF ||
+            source.cell_[ sref ].is_left_ref_ || source.cell_[ sref ].is_right_ref_ ||
+            source.cell_[ sref ].left_.bitmap_ != source.cell_[ sref ].right_.bitmap_
+    );
+
+    size_t sref_size = 0;
+    size_t dref_size = 0;
+
+    ref_t sstack[128];
+    ref_t dstack[128];
+    size_t top = 0;
+
+    /* Get size of the source subtree */
+    sstack[top++] = sref;
+    do {
+        assert (top < sizeof(sstack) / sizeof(sstack[0]));
+
+        ++sref_size;
+
+        const cell_t& scell = source.cell_[ sstack[--top] ];
+        if (scell.is_left_ref_) {
+            sstack[top++] = scell.left_.ref_;
+        }
+        if (scell.is_right_ref_) {
+            sstack[top++] = scell.right_.ref_;
+        }
+
+    } while (top > 0);
+
+    /* Get size of the destination subtree */
+    dstack[top++] = dref;
+    do {
+        assert (top < sizeof(dstack) / sizeof(dstack[0]));
+
+        ++dref_size;
+
+        const cell_t& dcell = destination.cell_[ dstack[--top] ];
+        if (dcell.is_left_ref_) {
+            dstack[top++] = dcell.left_.ref_;
+        }
+        if (dcell.is_right_ref_) {
+            dstack[top++] = dcell.right_.ref_;
+        }
+
+    } while (top > 0);
+
+    /* Reserving proper number of cells */
+    if (dref_size < sref_size) {
+        if (!destination.reserve_cells( sref_size - dref_size)) {
+            return /* MEMORY ERROR or OVERFLOW ERROR */;
+        }
+    }
+
+    /* Release the destination subtree */
+    if (destination.cell_[dref].is_left_ref_) {
+        destination.free_cell(destination.cell_[dref].left_.ref_);
+    }
+    if (destination.cell_[dref].is_right_ref_) {
+        destination.free_cell(destination.cell_[dref].right_.ref_);
+    }
+
+    /* Make cloning */
+    sstack[top] = sref;
+    dstack[top] = dref;
+    ++top;
+
+    do {
+        --top;
+        const cell_t& scell = source.cell_[ sstack[top] ];
+        cell_t& dcell = destination.cell_[ dstack[top] ];
+
+        /* Processing left ref */
+        if (scell.is_left_ref_) {
+            dcell.is_left_ref_ = true;
+            dcell.left_.ref_ = destination._alloc_cell();
+
+            sstack[top] = scell.left_.ref_;
+            dstack[top] = dcell.left_.ref_;
+            ++top;
+        } else {
+            dcell.is_left_ref_ = false;
+            dcell.left_.bitmap_ = scell.left_.bitmap_;
+        }
+
+        /* Processing right ref */
+        if (scell.is_right_ref_) {
+            dcell.is_right_ref_ = true;
+            dcell.right_.ref_ = destination._alloc_cell();
+
+            sstack[top] = scell.right_.ref_;
+            dstack[top] = dcell.right_.ref_;
+            ++top;
+        } else {
+            dcell.is_right_ref_ = false;
+            dcell.right_.bitmap_ = scell.right_.bitmap_;
+        }
+    } while (top > 0);
+}
+
+int binmap_t::write_cell(FILE *fp,cell_t c)
+{
+       fprintf_retiffail(fp,"leftb %d\n", c.left_.bitmap_);
+       fprintf_retiffail(fp,"rightb %d\n", c.right_.bitmap_);
+       fprintf_retiffail(fp,"is_left %d\n", c.is_left_ref_ ? 1 : 0 );
+       fprintf_retiffail(fp,"is_right %d\n", c.is_right_ref_ ? 1 : 0 );
+       fprintf_retiffail(fp,"is_free %d\n", c.is_free_ ? 1 : 0 );
+       return 0;
+}
+
+
+int binmap_t::read_cell(FILE *fp,cell_t *c)
+{
+       bitmap_t left,right;
+       int is_left,is_right,is_free;
+       fscanf_retiffail(fp,"leftb %d\n", &left);
+       fscanf_retiffail(fp,"rightb %d\n", &right);
+       fscanf_retiffail(fp,"is_left %d\n", &is_left );
+       fscanf_retiffail(fp,"is_right %d\n", &is_right );
+       fscanf_retiffail(fp,"is_free %d\n", &is_free );
+
+       //fprintf(stderr,"binmapread_cell: l%ld r%ld %d %d %d\n", left, right, is_left, is_right, is_free );
+
+       c->left_.bitmap_ = left;
+       c->right_.bitmap_ = right;
+       c->is_left_ref_ = (bool)is_left;
+       c->is_right_ref_ = (bool)is_right;
+       c->is_free_ = (bool)is_free;
+
+       return 0;
+}
+
+// Arno, 2011-10-20: Persistent storage
+int binmap_t::serialize(FILE *fp)
+{
+        fprintf_retiffail(fp,"root bin %lli\n",root_bin_.toUInt() );
+        fprintf_retiffail(fp,"free top %i\n",free_top_ );
+        fprintf_retiffail(fp,"alloc cells " PRISIZET"\n", allocated_cells_number_);
+        fprintf_retiffail(fp,"cells num " PRISIZET"\n", cells_number_);
+        for (size_t i=0; i<cells_number_; i++)
+        {
+               if (write_cell(fp,cell_[i]) < 0)
+                       return -1;
+        }
+        return 0;
+}
+
+
+
+
+int binmap_t::deserialize(FILE *fp)
+{
+        bin_t::uint_t rootbinval;
+        ref_t freetop;
+        size_t alloccells,cells;
+        fscanf_retiffail(fp,"root bin %lli\n", &rootbinval );
+        fscanf_retiffail(fp,"free top %i\n", &freetop );
+        fscanf_retiffail(fp,"alloc cells " PRISIZET"\n", &alloccells);
+        fscanf_retiffail(fp,"cells num " PRISIZET"\n", &cells);
+
+        //fprintf(stderr,"Filling BINMAP %p\n", this );
+        //fprintf(stderr,"Rootbin %lli freetop %li alloc %li num %li\n", rootbinval, freetop, alloccells, cells );
+
+        root_bin_ = bin_t(rootbinval);
+        free_top_ = freetop;
+        allocated_cells_number_ = alloccells;
+        cells_number_ = cells;
+        if (cell_ != NULL) {
+                free(cell_);
+        }
+        cell_ = (cell_t *)new cell_t[cells];
+        size_t i=0;
+        for (i=0; i<cells; i++)
+        {
+               /* Cleans cell */
+               memset(&cell_[i], 0, sizeof(cell_[0]));
+               if (read_cell(fp,&cell_[i]) < 0)
+                       return -1;
+        }
+        return 0;
+}
diff --git a/src/libswift_udp/binmap.h b/src/libswift_udp/binmap.h
new file mode 100644 (file)
index 0000000..168f21f
--- /dev/null
@@ -0,0 +1,251 @@
+#ifndef __binmap_h__
+#define __binmap_h__
+
+#include <cstddef>
+#include "bin.h"
+#include "compat.h"
+#include "serialize.h"
+
+namespace swift {
+
+/**
+ * Binmap class
+ */
+class binmap_t : Serializable {
+public:
+    /** Type of bitmap */
+    typedef int32_t bitmap_t;
+    /** Type of reference */
+    typedef uint32_t ref_t;
+
+
+    /**
+     * Constructor
+     */
+    binmap_t();
+
+
+    /**
+     * Destructor
+     */
+    ~binmap_t();
+
+
+    /**
+     * Set the bin
+     */
+    void set(const bin_t& bin);
+
+
+    /**
+     * Reset the bin
+     */
+    void reset(const bin_t& bin);
+
+
+    /**
+     * Empty all bins
+     */
+    void clear();
+
+
+    /**
+     * Ric: Fill all bins, size is given by the source's root
+     */
+    void fill(const binmap_t& source);
+
+
+    /**
+     * Whether binmap is empty
+     */
+    bool is_empty() const;
+
+
+    /**
+     * Whether binmap is filled
+     */
+    bool is_filled() const;
+
+
+    /**
+     * Whether range/bin is empty
+     */
+    bool is_empty(const bin_t& bin) const;
+
+
+    /**
+     * Whether range/bin is filled
+     */
+    bool is_filled(const bin_t& bin) const;
+
+
+    /**
+     * Return the topmost solid bin which covers the specified bin
+     */
+    bin_t cover(const bin_t& bin) const;
+
+
+    /**
+     * Find first empty bin
+     */
+    bin_t find_empty() const;
+
+
+    /**
+     * Find first filled bin
+     */
+    bin_t find_filled() const;
+
+
+    /**
+     * Get number of allocated cells
+     */
+    size_t cells_number() const;
+
+
+    /**
+     * Get total size of the binmap (Arno: =number of bytes it occupies in memory)
+     */
+    size_t total_size() const;
+
+
+    /**
+     * Echo the binmap status to stdout
+     */
+    void status() const;
+
+
+    /**
+     * Find first additional bin in source
+     */
+    static bin_t find_complement(const binmap_t& destination, const binmap_t& source, const bin_t::uint_t twist);
+
+
+    /**
+     * Find first additional bin of the source inside specified range
+     */
+    static bin_t find_complement(const binmap_t& destination, const binmap_t& source, bin_t range, const bin_t::uint_t twist);
+
+
+    /**
+     * Copy one binmap to another
+     */
+    static void copy(binmap_t& destination, const binmap_t& source);
+
+
+    /**
+     * Copy a range from one binmap to another binmap
+     */
+    static void copy(binmap_t& destination, const binmap_t& source, const bin_t& range);
+
+
+    // Arno, 2011-10-20: Persistent storage
+    int serialize(FILE *fp);
+    int deserialize(FILE *fp);
+private:
+    #pragma pack(push, 1)
+
+    /**
+     * Structure of cell halves
+     */
+    typedef struct {
+        union {
+            bitmap_t bitmap_;
+            ref_t ref_;
+        };
+    } half_t;
+
+    /**
+     * Structure of cells
+     */
+    typedef union {
+        struct {
+            half_t left_;
+            half_t right_;
+            bool is_left_ref_ : 1;
+            bool is_right_ref_ : 1;
+            bool is_free_ : 1;
+        };
+        ref_t free_next_;
+    } cell_t;
+
+    #pragma pack(pop)
+
+private:
+
+    /** Allocates one cell (dirty allocation) */
+    ref_t _alloc_cell();
+
+    /** Allocates one cell */
+    ref_t alloc_cell();
+
+    /** Reserve cells allocation capacity */
+    bool reserve_cells(size_t count);
+
+    /** Releases the cell */
+    void free_cell(ref_t cell);
+
+    /** Extend root */
+    bool extend_root();
+
+    /** Pack a trace of cells */
+    void pack_cells(ref_t* cells);
+
+
+    /** Pointer to the list of blocks */
+    cell_t* cell_;
+
+    /** Number of available cells */
+    size_t cells_number_;
+
+    /** Number of allocated cells */
+    size_t allocated_cells_number_;
+
+    /** Front of the free cell list */
+    ref_t free_top_;
+
+    /** The root bin */
+    bin_t root_bin_;
+
+
+    /** Trace the bin */
+    void trace(ref_t* ref, bin_t* bin, const bin_t& target) const;
+
+    /** Trace the bin */
+    void trace(ref_t* ref, bin_t* bin, ref_t** history, const bin_t& target) const;
+
+
+    /** Sets low layer bitmap */
+    void _set__low_layer_bitmap(const bin_t& bin, const bitmap_t bitmap);
+
+    /** Sets high layer bitmap */
+    void _set__high_layer_bitmap(const bin_t& bin, const bitmap_t bitmap);
+
+
+    /** Clone binmap cells to another binmap */
+    static void copy(binmap_t& destination, const ref_t dref, const binmap_t& source, const ref_t sref);
+
+    static void _copy__range(binmap_t& destination, const binmap_t& source, const ref_t sref, const bin_t sbin);
+
+
+    /** Find first additional bin in source */
+    static bin_t _find_complement(const bin_t& bin, const ref_t dref, const binmap_t& destination, const ref_t sref, const binmap_t& source, const bin_t::uint_t twist);
+    static bin_t _find_complement(const bin_t& bin, const bitmap_t dbitmap, const ref_t sref, const binmap_t& source, const bin_t::uint_t twist);
+    static bin_t _find_complement(const bin_t& bin, const ref_t dref, const binmap_t& destination, const bitmap_t sbitmap, const bin_t::uint_t twist);
+    static bin_t _find_complement(const bin_t& bin, const bitmap_t dbitmap, const bitmap_t sbitmap, const bin_t::uint_t twist);
+
+
+    /* Disabled */
+    binmap_t& operator = (const binmap_t&);
+
+    /* Disabled */
+    binmap_t(const binmap_t&);
+
+    // Arno, 2011-10-20: Persistent storage
+    int write_cell(FILE *fp,cell_t c);
+    int read_cell(FILE *fp,cell_t *c);
+};
+
+} // namespace end
+
+#endif /*_binmap_h__*/
diff --git a/src/libswift_udp/channel.cpp b/src/libswift_udp/channel.cpp
new file mode 100644 (file)
index 0000000..35f4435
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ *  channel.cpp
+ *  class representing a virtual connection to a peer. In addition,
+ *  it contains generic functions for socket management (see sock_open
+ *  class variable)
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include <cassert>
+#include "compat.h"
+//#include <glog/logging.h>
+#include "swift.h"
+
+using namespace std;
+using namespace swift;
+
+/*
+ * Class variables
+ */
+
+swift::tint now_t::now = Channel::Time();
+tint Channel::start = now_t::now;
+tint Channel::epoch = now_t::now/360000000LL*360000000LL; // make logs mergeable
+uint64_t Channel::global_dgrams_up=0, Channel::global_dgrams_down=0,
+         Channel::global_raw_bytes_up=0, Channel::global_raw_bytes_down=0,
+         Channel::global_bytes_up=0, Channel::global_bytes_down=0;
+sckrwecb_t Channel::sock_open[] = {};
+int Channel::sock_count = 0;
+swift::tint Channel::last_tick = 0;
+int Channel::MAX_REORDERING = 4;
+bool Channel::SELF_CONN_OK = false;
+swift::tint Channel::TIMEOUT = TINT_SEC*60;
+std::vector<Channel*> Channel::channels(1);
+Address Channel::tracker;
+//tbheap Channel::send_queue;
+FILE* Channel::debug_file = NULL;
+#include "ext/simple_selector.cpp"
+PeerSelector* Channel::peer_selector = new SimpleSelector();
+tint Channel::MIN_PEX_REQUEST_INTERVAL = TINT_SEC;
+
+
+/*
+ * Instance methods
+ */
+
+Channel::Channel    (FileTransfer* transfer, int socket, Address peer_addr) :
+       // Arno, 2011-10-03: Reordered to avoid g++ Wall warning
+       peer_(peer_addr), socket_(socket==INVALID_SOCKET?default_socket():socket), // FIXME
+    transfer_(transfer), peer_channel_id_(0), own_id_mentioned_(false),
+    data_in_(TINT_NEVER,bin_t::NONE), data_in_dbl_(bin_t::NONE),
+    data_out_cap_(bin_t::ALL),hint_out_size_(0),
+    // Gertjan fix 996e21e8abfc7d88db3f3f8158f2a2c4fc8a8d3f
+    // "Changed PEX rate limiting to per channel limiting"
+    last_pex_request_time_(0), next_pex_request_time_(0),
+    pex_request_outstanding_(false), useless_pex_count_(0),
+    pex_requested_(false),  // Ric: init var that wasn't initialiazed
+    //
+    rtt_avg_(TINT_SEC), dev_avg_(0), dip_avg_(TINT_SEC),
+    last_send_time_(0), last_recv_time_(0), last_data_out_time_(0), last_data_in_time_(0),
+    last_loss_time_(0), next_send_time_(0), cwnd_(1), cwnd_count1_(0), send_interval_(TINT_SEC),
+    send_control_(PING_PONG_CONTROL), sent_since_recv_(0),
+    lastrecvwaskeepalive_(false), lastsendwaskeepalive_(false), // Arno: nap bug fix
+    ack_rcvd_recent_(0),
+    ack_not_rcvd_recent_(0), owd_min_bin_(0), owd_min_bin_start_(NOW),
+    owd_cur_bin_(0), dgrams_sent_(0), dgrams_rcvd_(0),
+    raw_bytes_up_(0), raw_bytes_down_(0), bytes_up_(0), bytes_down_(0),
+    scheduled4close_(false)
+{
+    if (peer_==Address())
+        peer_ = tracker;
+    this->id_ = channels.size();
+    channels.push_back(this);
+    transfer_->hs_in_.push_back(bin_t(id_));
+    for(int i=0; i<4; i++) {
+        owd_min_bins_[i] = TINT_NEVER;
+        owd_current_[i] = TINT_NEVER;
+    }
+    evsend_ptr_ = new struct event;
+    evtimer_assign(evsend_ptr_,evbase,&Channel::LibeventSendCallback,this);
+    evtimer_add(evsend_ptr_,tint2tv(next_send_time_));
+
+    // RATELIMIT
+       transfer->mychannels_.insert(this);
+
+       dprintf("%s #%u init channel %s\n",tintstr(),id_,peer_.str());
+       //fprintf(stderr,"new Channel %d %s\n", id_, peer_.str() );
+}
+
+
+Channel::~Channel () {
+       dprintf("%s #%u dealloc channel\n",tintstr(),id_);
+    channels[id_] = NULL;
+    ClearEvents();
+
+    // RATELIMIT
+    if (transfer_ != NULL)
+       transfer_->mychannels_.erase(this);
+}
+
+
+void Channel::ClearEvents()
+{
+    if (evsend_ptr_ != NULL) {
+       if (evtimer_pending(evsend_ptr_,NULL))
+               evtimer_del(evsend_ptr_);
+       delete evsend_ptr_;
+       evsend_ptr_ = NULL;
+    }
+}
+
+
+
+
+bool Channel::IsComplete() {
+       // Check if peak hash bins are filled.
+       if (file().peak_count() == 0)
+               return false;
+
+    for(int i=0; i<file().peak_count(); i++) {
+        bin_t peak = file().peak(i);
+        if (!ack_in_.is_filled(peak))
+            return false;
+    }
+       return true;
+}
+
+
+
+uint16_t Channel::GetMyPort() {
+       struct sockaddr_in mysin = {};
+       socklen_t mysinlen = sizeof(mysin);
+       if (getsockname(socket_, (struct sockaddr *)&mysin, &mysinlen) < 0)
+       {
+               print_error("error on getsockname");
+               return 0;
+       }
+       else
+               return ntohs(mysin.sin_port);
+}
+
+bool Channel::IsDiffSenderOrDuplicate(Address addr, uint32_t chid)
+{
+    if (peer() != addr)
+    {
+       // Got message from different address than I send to
+       //
+               if (!own_id_mentioned_ && addr.is_private()) {
+                       // Arno, 2012-02-27: Got HANDSHAKE reply from IANA private address,
+                       // check for duplicate connections:
+                       //
+                       // When two peers A and B are behind the same firewall, they will get
+                       // extB, resp. extA addresses from the tracker. They will both
+                       // connect to their counterpart but because the incoming packet
+                       // will be from the intNAT address the duplicates are not
+                       // recognized.
+                       //
+                       // Solution: when the second datagram comes in (HANDSHAKE reply),
+                       // see if you have had a first datagram from the same addr
+                       // (HANDSHAKE). If so, close the channel if his port number is
+                       // larger than yours (such that one channel remains).
+                       //
+                       recv_peer_ = addr;
+
+                       Channel *c = transfer().FindChannel(addr,this);
+                       if (c != NULL) {
+                               // I already initiated a connection to this peer,
+                               // this new incoming message would establish a duplicate.
+                               // One must break the connection, decide using port
+                               // number:
+                               dprintf("%s #%u found duplicate channel to %s\n",
+                                               tintstr(),chid,addr.str());
+
+                               if (addr.port() > GetMyPort()) {
+                                       //Schedule4Close();
+                                       dprintf("%s #%u closing duplicate channel to %s\n",
+                                                       tintstr(),chid,addr.str());
+                                       return true;
+                               }
+                       }
+               }
+               else
+               {
+                       // Received HANDSHAKE reply from other address than I sent
+                       // HANDSHAKE to, and the address is not an IANA private
+                       // address (=no NAT in play), so close.
+                       //Schedule4Close();
+                       dprintf("%s #%u invalid peer address %s!=%s\n",
+                                       tintstr(),chid,peer().str(),addr.str());
+                       return true;
+               }
+    }
+       return false;
+}
+
+
+
+
+
+
+
+/*
+ * Class methods
+ */
+tint Channel::Time () {
+    //HiResTimeOfDay* tod = HiResTimeOfDay::Instance();
+    //tint ret = tod->getTimeUSec();
+    //DLOG(INFO)<<"now is "<<ret;
+    return now_t::now = usec_time();
+}
+
+// SOCKMGMT
+evutil_socket_t Channel::Bind (Address address, sckrwecb_t callbacks) {
+    struct sockaddr_in addr = address;
+    evutil_socket_t fd;
+    int len = sizeof(struct sockaddr_in), sndbuf=1<<20, rcvbuf=1<<20;
+    #define dbnd_ensure(x) { if (!(x)) { \
+        print_error("binding fails"); close_socket(fd); return INVALID_SOCKET; } }
+    dbnd_ensure ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0 );
+    dbnd_ensure( make_socket_nonblocking(fd) );  // FIXME may remove this
+    int enable = true;
+    dbnd_ensure ( setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
+                             (setsockoptptr_t)&sndbuf, sizeof(int)) == 0 );
+    dbnd_ensure ( setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
+                             (setsockoptptr_t)&rcvbuf, sizeof(int)) == 0 );
+    //setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (setsockoptptr_t)&enable, sizeof(int));
+    dbnd_ensure ( ::bind(fd, (sockaddr*)&addr, len) == 0 );
+
+    callbacks.sock = fd;
+    sock_open[sock_count++] = callbacks;
+    return fd;
+}
+
+Address Channel::BoundAddress(evutil_socket_t sock) {
+
+    struct sockaddr_in myaddr;
+    socklen_t mylen = sizeof(myaddr);
+    int ret = getsockname(sock,(sockaddr*)&myaddr,&mylen);
+    if (ret >= 0) {
+               return Address(myaddr);
+    }
+       else {
+               return Address();
+       }
+}
+
+
+Address swift::BoundAddress(evutil_socket_t sock) {
+       return Channel::BoundAddress(sock);
+}
+
+
+int Channel::SendTo (evutil_socket_t sock, const Address& addr, struct evbuffer *evb) {
+
+    int length = evbuffer_get_length(evb);
+    int r = sendto(sock,(const char *)evbuffer_pullup(evb, length),length,0,
+                   (struct sockaddr*)&(addr.addr),sizeof(struct sockaddr_in));
+    if (r<0) {
+        print_error("can't send");
+        evbuffer_drain(evb, length); // Arno: behaviour is to pretend the packet got lost
+    }
+    else
+       evbuffer_drain(evb,r);
+    global_dgrams_up++;
+    global_raw_bytes_up+=length;
+    Time();
+    return r;
+}
+
+int Channel::RecvFrom (evutil_socket_t sock, Address& addr, struct evbuffer *evb) {
+    socklen_t addrlen = sizeof(struct sockaddr_in);
+    struct evbuffer_iovec vec;
+    if (evbuffer_reserve_space(evb, SWIFT_MAX_RECV_DGRAM_SIZE, &vec, 1) < 0) {
+       print_error("error on evbuffer_reserve_space");
+       return 0;
+    }
+    int length = recvfrom (sock, (char *)vec.iov_base, SWIFT_MAX_RECV_DGRAM_SIZE, 0,
+                          (struct sockaddr*)&(addr.addr), &addrlen);
+    if (length<0) {
+        length = 0;
+
+        // Linux and Windows report "ICMP port unreachable" if the dest port could
+        // not be reached:
+        //    http://support.microsoft.com/kb/260018
+        //    http://www.faqs.org/faqs/unix-faq/socket/
+#ifdef _WIN32
+        if (WSAGetLastError() == 10054) // Sometimes errno == 2 ?!
+#else
+               if (errno == ECONNREFUSED)
+#endif
+               {
+               CloseChannelByAddress(addr);
+               }
+        else
+               print_error("error on recv");
+    }
+    vec.iov_len = length;
+    if (evbuffer_commit_space(evb, &vec, 1) < 0)  {
+        length = 0;
+        print_error("error on evbuffer_commit_space");
+    }
+    global_dgrams_down++;
+    global_raw_bytes_down+=length;
+    Time();
+    return length;
+}
+
+
+void Channel::CloseSocket(evutil_socket_t sock) {
+    for(int i=0; i<sock_count; i++)
+        if (sock_open[i].sock==sock)
+            sock_open[i] = sock_open[--sock_count];
+    if (!close_socket(sock))
+        print_error("on closing a socket");
+}
+
+void Channel::Shutdown () {
+    while (sock_count--)
+        CloseSocket(sock_open[sock_count].sock);
+}
+
+void     swift::SetTracker(const Address& tracker) {
+    Channel::tracker = tracker;
+}
+
+int Channel::DecodeID(int scrambled) {
+    return scrambled ^ (int)start;
+}
+int Channel::EncodeID(int unscrambled) {
+    return unscrambled ^ (int)start;
+}
+
+/*
+ * class Address implementation
+ */
+
+void Address::set_ipv4 (const char* ip_str) {
+    struct hostent *h = gethostbyname(ip_str);
+    if (h == NULL) {
+        print_error("cannot lookup address");
+        return;
+    } else {
+        addr.sin_addr.s_addr = *(u_long *) h->h_addr_list[0];
+    }
+}
+
+
+Address::Address(const char* ip_port) {
+    clear();
+    if (strlen(ip_port)>=1024)
+        return;
+    char ipp[1024];
+    strncpy(ipp,ip_port,1024);
+    char* semi = strchr(ipp,':');
+    if (semi) {
+        *semi = 0;
+        set_ipv4(ipp);
+        set_port(semi+1);
+    } else {
+        if (strchr(ipp, '.')) {
+            set_ipv4(ipp);
+            set_port((uint16_t)0);
+        } else {
+            set_ipv4((uint32_t)INADDR_ANY);
+            set_port(ipp);
+        }
+    }
+}
+
+
+uint32_t Address::LOCALHOST = INADDR_LOOPBACK;
+
+
+/*
+ * Utility methods 1
+ */
+
+
+const char* swift::tintstr (tint time) {
+    if (time==0)
+        time = now_t::now;
+    static char ret_str[4][32]; // wow
+    static int i;
+    i = (i+1) & 3;
+    if (time==TINT_NEVER)
+        return "NEVER";
+    time -= Channel::epoch;
+    assert(time>=0);
+    int hours = time/TINT_HOUR;
+    time %= TINT_HOUR;
+    int mins = time/TINT_MIN;
+    time %= TINT_MIN;
+    int secs = time/TINT_SEC;
+    time %= TINT_SEC;
+    int msecs = time/TINT_MSEC;
+    time %= TINT_MSEC;
+    int usecs = time/TINT_uSEC;
+    sprintf(ret_str[i],"%i_%02i_%02i_%03i_%03i",hours,mins,secs,msecs,usecs);
+    return ret_str[i];
+}
+
+
+std::string swift::sock2str (struct sockaddr_in addr) {
+    char ipch[32];
+#ifdef _WIN32
+    //Vista only: InetNtop(AF_INET,&(addr.sin_addr),ipch,32);
+    // IPv4 only:
+    struct in_addr inaddr;
+    memcpy(&inaddr, &(addr.sin_addr), sizeof(inaddr));
+    strncpy(ipch, inet_ntoa(inaddr),32);
+#else
+    inet_ntop(AF_INET,&(addr.sin_addr),ipch,32);
+#endif
+    sprintf(ipch+strlen(ipch),":%i",ntohs(addr.sin_port));
+    return std::string(ipch);
+}
+
+
+/*
+ * Swift top-level API implementation
+ */
+
+int     swift::Listen (Address addr) {
+    sckrwecb_t cb;
+    cb.may_read = &Channel::LibeventReceiveCallback;
+    cb.sock = Channel::Bind(addr,cb);
+    // swift UDP receive
+    event_assign(&Channel::evrecv, Channel::evbase, cb.sock, EV_READ,
+                cb.may_read, NULL);
+    event_add(&Channel::evrecv, NULL);
+    return cb.sock;
+}
+
+void    swift::Shutdown (int sock_des) {
+    Channel::Shutdown();
+}
+
+int      swift::Open (const char* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size) {
+    FileTransfer* ft = new FileTransfer(filename, hash, force_check_diskvshash, check_netwvshash, chunk_size);
+    if (ft && ft->file().file_descriptor()) {
+
+        /*if (FileTransfer::files.size()<fdes)  // FIXME duplication
+            FileTransfer::files.resize(fdes);
+        FileTransfer::files[fdes] = ft;*/
+
+        // initiate tracker connections
+       // SWIFTPROC
+       ft->SetTracker(tracker);
+       ft->ConnectToTracker();
+
+       return ft->file().file_descriptor();
+    } else {
+        if (ft)
+            delete ft;
+        return -1;
+    }
+}
+
+
+void    swift::Close (int fd) {
+    if (fd<FileTransfer::files.size() && FileTransfer::files[fd])
+        delete FileTransfer::files[fd];
+}
+
+
+void    swift::AddPeer (Address address, const Sha1Hash& root) {
+    Channel::peer_selector->AddPeer(address,root);
+}
+
+
+uint64_t  swift::Size (int fdes) {
+    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
+        return FileTransfer::files[fdes]->file().size();
+    else
+        return 0;
+}
+
+
+bool  swift::IsComplete (int fdes) {
+    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
+        return FileTransfer::files[fdes]->file().is_complete();
+    else
+        return 0;
+}
+
+
+uint64_t  swift::Complete (int fdes) {
+    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
+        return FileTransfer::files[fdes]->file().complete();
+    else
+        return 0;
+}
+
+
+uint64_t  swift::SeqComplete (int fdes) {
+    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
+        return FileTransfer::files[fdes]->file().seq_complete();
+    else
+        return 0;
+}
+
+
+const Sha1Hash& swift::RootMerkleHash (int file) {
+    FileTransfer* trans = FileTransfer::file(file);
+    if (!trans)
+        return Sha1Hash::ZERO;
+    return trans->file().root_hash();
+}
+
+
+/** Returns the number of bytes in a chunk for this transmission */
+uint32_t         swift::ChunkSize(int fdes)
+{
+    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
+        return FileTransfer::files[fdes]->file().chunk_size();
+    else
+        return 0;
+}
+
+
+// CHECKPOINT
+void swift::Checkpoint(int transfer) {
+       // Save transfer's binmap for zero-hashcheck restart
+       FileTransfer *ft = FileTransfer::file(transfer);
+       if (ft == NULL)
+               return;
+
+       std::string binmap_filename = ft->file().filename();
+       binmap_filename.append(".mbinmap");
+       //fprintf(stderr,"swift: checkpointing %s at %lli\n", binmap_filename.c_str(), Complete(transfer));
+       FILE *fp = fopen(binmap_filename.c_str(),"wb");
+       if (!fp) {
+               print_error("cannot open mbinmap for writing");
+               return;
+       }
+       if (ft->file().serialize(fp) < 0)
+               print_error("writing to mbinmap");
+       fclose(fp);
+}
+
+
+
+/*
+ * Utility methods 2
+ */
+
+int swift::evbuffer_add_string(struct evbuffer *evb, std::string str) {
+    return evbuffer_add(evb, str.c_str(), str.size());
+}
+
+int swift::evbuffer_add_8(struct evbuffer *evb, uint8_t b) {
+    return evbuffer_add(evb, &b, 1);
+}
+
+int swift::evbuffer_add_16be(struct evbuffer *evb, uint16_t w) {
+    uint16_t wbe = htons(w);
+    return evbuffer_add(evb, &wbe, 2);
+}
+
+int swift::evbuffer_add_32be(struct evbuffer *evb, uint32_t i) {
+    uint32_t ibe = htonl(i);
+    return evbuffer_add(evb, &ibe, 4);
+}
+
+int swift::evbuffer_add_64be(struct evbuffer *evb, uint64_t l) {
+    uint32_t lbe[2];
+    lbe[0] = htonl((uint32_t)(l>>32));
+    lbe[1] = htonl((uint32_t)(l&0xffffffff));
+    return evbuffer_add(evb, lbe, 8);
+}
+
+int swift::evbuffer_add_hash(struct evbuffer *evb, const Sha1Hash& hash)  {
+    return evbuffer_add(evb, hash.bits, Sha1Hash::SIZE);
+}
+
+uint8_t swift::evbuffer_remove_8(struct evbuffer *evb) {
+    uint8_t b;
+    if (evbuffer_remove(evb, &b, 1) < 1)
+       return 0;
+    return b;
+}
+
+uint16_t swift::evbuffer_remove_16be(struct evbuffer *evb) {
+    uint16_t wbe;
+    if (evbuffer_remove(evb, &wbe, 2) < 2)
+       return 0;
+    return ntohs(wbe);
+}
+
+uint32_t swift::evbuffer_remove_32be(struct evbuffer *evb) {
+    uint32_t ibe;
+    if (evbuffer_remove(evb, &ibe, 4) < 4)
+       return 0;
+    return ntohl(ibe);
+}
+
+uint64_t swift::evbuffer_remove_64be(struct evbuffer *evb) {
+    uint32_t lbe[2];
+    if (evbuffer_remove(evb, lbe, 8) < 8)
+       return 0;
+    uint64_t l = ntohl(lbe[0]);
+    l<<=32;
+    l |= ntohl(lbe[1]);
+    return l;
+}
+
+Sha1Hash swift::evbuffer_remove_hash(struct evbuffer* evb)  {
+    char bits[Sha1Hash::SIZE];
+    if (evbuffer_remove(evb, bits, Sha1Hash::SIZE) < Sha1Hash::SIZE)
+       return Sha1Hash::ZERO;
+    return Sha1Hash(false, bits);
+}
+
diff --git a/src/libswift_udp/cmdgw.cpp b/src/libswift_udp/cmdgw.cpp
new file mode 100644 (file)
index 0000000..841bd25
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ *  cmdgw.cpp
+ *  command gateway for controling swift engine via a TCP connection
+ *
+ *  Created by Arno Bakker
+ *  Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <math.h>
+#include <iostream>
+#include <sstream>
+
+#include "swift.h"
+#include "compat.h"
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include <event2/listener.h>
+
+
+using namespace swift;
+
+// Send PLAY after receiving 2^layer * chunksize bytes
+#define CMDGW_MAX_PREBUF_BYTES         (256*1024)
+
+
+// Status of the swarm download
+#define DLSTATUS_HASHCHECKING  2
+#define DLSTATUS_DOWNLOADING  3
+#define DLSTATUS_SEEDING 4
+
+#define MAX_CMD_MESSAGE 1024
+
+#define ERROR_NO_ERROR         0
+#define ERROR_UNKNOWN_CMD      -1
+#define ERROR_MISS_ARG         -2
+#define ERROR_BAD_ARG          -3
+
+#define CMDGW_MAX_CLIENT 1024   // Arno: == maximum number of swarms per proc
+
+struct cmd_gw_t {
+    int      id;
+    evutil_socket_t   cmdsock;
+    int                 transfer; // swift FD
+    char       *contentfilename; // basename of content file
+    bool       moreinfo;                 // whether to report detailed stats (see SETMOREINFO cmd)
+    tint       startt;                   // ARNOSMPTODO: debug speed measurements, remove
+} cmd_requests[CMDGW_MAX_CLIENT];
+
+
+int cmd_gw_reqs_open = 0;
+int cmd_gw_reqs_count = 0;
+
+struct evconnlistener *cmd_evlistener = NULL;
+struct evbuffer *cmd_evbuffer = NULL; // Data received on cmd socket : WARNING: one for all cmd sockets
+Address cmd_gw_httpaddr;                 // HTTP gateway address for PLAY cmd
+
+
+bool cmd_gw_debug=false;
+
+
+// Fwd defs
+void CmdGwDataCameInCallback(struct bufferevent *bev, void *ctx);
+bool CmdGwReadLine(evutil_socket_t cmdsock);
+void CmdGwNewRequestCallback(evutil_socket_t cmdsock, char *line);
+
+
+
+void CmdGwFreeRequest(cmd_gw_t* req)
+{
+    if (req->contentfilename != NULL)
+        free(req->contentfilename);
+    // Arno, 2012-02-06: Reset, in particular moreinfo flag.
+    memset(req,'\0',sizeof(cmd_gw_t));
+}
+
+
+void CmdGwCloseConnection(evutil_socket_t sock)
+{
+       // Close cmd connection and stop all associated downloads.
+       // Doesn't remove .mhash state or content
+
+       bool scanning = true;
+       while (scanning)
+       {
+               scanning = false;
+           for(int i=0; i<cmd_gw_reqs_open; i++)
+           {
+               cmd_gw_t* req = &cmd_requests[i];
+               if (req->cmdsock==sock)
+               {
+                dprintf("%s @%i stopping-on-close transfer %i\n",tintstr(),req->id,req->transfer);
+                swift::Close(req->transfer);
+
+                // Remove from list and reiterate over it
+                CmdGwFreeRequest(req);
+                       *req = cmd_requests[--cmd_gw_reqs_open];
+                       scanning = true;
+                       break;
+               }
+           }
+       }
+}
+
+
+cmd_gw_t* CmdGwFindRequestByTransfer (int transfer)
+{
+    for(int i=0; i<cmd_gw_reqs_open; i++)
+        if (cmd_requests[i].transfer==transfer)
+            return cmd_requests+i;
+    return NULL;
+}
+
+cmd_gw_t* CmdGwFindRequestByRootHash(Sha1Hash &want_hash)
+{
+       FileTransfer *ft = NULL;
+    for(int i=0; i<cmd_gw_reqs_open; i++) {
+       cmd_gw_t* req = &cmd_requests[i];
+       ft = FileTransfer::file(req->transfer);
+       Sha1Hash got_hash = ft->root_hash();
+        if (want_hash == got_hash)
+               return req;
+    }
+    return NULL;
+}
+
+
+void CmdGwGotCHECKPOINT(Sha1Hash &want_hash)
+{
+       // Checkpoint the specified download
+       fprintf(stderr,"cmd: GotCHECKPOINT: %s\n",want_hash.hex().c_str());
+
+       cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
+       if (req == NULL)
+       return;
+    FileTransfer *ft = FileTransfer::file(req->transfer);
+
+       std::string binmap_filename = ft->file().filename();
+       binmap_filename.append(".mbinmap");
+       fprintf(stderr,"cmdgw: GotCHECKPOINT: checkpointing to %s\n", binmap_filename.c_str() );
+       FILE *fp = fopen(binmap_filename.c_str(),"wb");
+       if (!fp) {
+               print_error("cannot open mbinmap for writing");
+               return;
+       }
+       if (ft->file().serialize(fp) < 0)
+               print_error("writing to mbinmap");
+       fclose(fp);
+}
+
+
+void CmdGwGotREMOVE(Sha1Hash &want_hash, bool removestate, bool removecontent)
+{
+       // Remove the specified download
+       fprintf(stderr,"cmd: GotREMOVE: %s %d %d\n",want_hash.hex().c_str(),removestate,removecontent);
+
+       cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
+       if (req == NULL)
+       return;
+    FileTransfer *ft = FileTransfer::file(req->transfer);
+
+       fprintf(stderr, "%s @%i remove transfer %i\n",tintstr(),req->id,req->transfer);
+       dprintf("%s @%i remove transfer %i\n",tintstr(),req->id,req->transfer);
+       swift::Close(req->transfer);
+
+       // Delete content + .mhash from filesystem, if desired
+       if (removecontent)
+               remove(req->contentfilename);
+
+       if (removestate)
+       {
+               char *mhashfilename = (char *)malloc(strlen(req->contentfilename)+strlen(".mhash")+1);
+               strcpy(mhashfilename,req->contentfilename);
+               strcat(mhashfilename,".mhash");
+
+               remove(mhashfilename);
+               free(mhashfilename);
+
+               // Arno, 2012-01-10: .mbinmap gots to go too.
+               char *mbinmapfilename = (char *)malloc(strlen(req->contentfilename)+strlen(".mbinmap")+1);
+               strcpy(mbinmapfilename,req->contentfilename);
+               strcat(mbinmapfilename,".mbinmap");
+
+               remove(mbinmapfilename);
+               free(mbinmapfilename);
+       }
+
+       CmdGwFreeRequest(req);
+       *req = cmd_requests[--cmd_gw_reqs_open];
+}
+
+
+void CmdGwGotMAXSPEED(Sha1Hash &want_hash, data_direction_t ddir, double speed)
+{
+       // Set maximum speed on the specified download
+       fprintf(stderr,"cmd: GotMAXSPEED: %s %d %lf\n",want_hash.hex().c_str(),ddir,speed);
+
+       cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
+       if (req == NULL)
+       return;
+    FileTransfer *ft = FileTransfer::file(req->transfer);
+       ft->SetMaxSpeed(ddir,speed);
+}
+
+
+void CmdGwGotSETMOREINFO(Sha1Hash &want_hash, bool enable)
+{
+       cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
+       if (req == NULL)
+       return;
+       req->moreinfo = enable;
+}
+
+
+void CmdGwSendINFOHashChecking(cmd_gw_t* req, Sha1Hash root_hash)
+{
+       // Send INFO DLSTATUS_HASHCHECKING message.
+
+    char cmd[MAX_CMD_MESSAGE];
+       sprintf(cmd,"INFO %s %d %lli/%lli %lf %lf %u %u\r\n",root_hash.hex().c_str(),DLSTATUS_HASHCHECKING,(uint64_t)0,(uint64_t)0,0.0,0.0,0,0);
+
+    //fprintf(stderr,"cmd: SendINFO: %s", cmd);
+    send(req->cmdsock,cmd,strlen(cmd),0);
+}
+
+
+void CmdGwSendINFO(cmd_gw_t* req, int dlstatus)
+{
+       // Send INFO message.
+       if (cmd_gw_debug)
+               fprintf(stderr,"cmd: SendINFO: %d %d\n", req->transfer, dlstatus );
+
+       FileTransfer *ft = FileTransfer::file(req->transfer);
+       if (ft == NULL)
+               // Download was removed or closed somehow.
+               return;
+
+    Sha1Hash root_hash = ft->root_hash();
+
+    char cmd[MAX_CMD_MESSAGE];
+    uint64_t size = swift::Size(req->transfer);
+    uint64_t complete = swift::Complete(req->transfer);
+    if (size == complete)
+       dlstatus = DLSTATUS_SEEDING;
+
+    uint32_t numleech = ft->GetNumLeechers();
+    uint32_t numseeds = ft->GetNumSeeders();
+    sprintf(cmd,"INFO %s %d %lli/%lli %lf %lf %u %u\r\n",root_hash.hex().c_str(),dlstatus,complete,size,ft->GetCurrentSpeed(DDIR_DOWNLOAD),ft->GetCurrentSpeed(DDIR_UPLOAD),numleech,numseeds);
+
+    //fprintf(stderr,"cmd: SendINFO: %s", cmd);
+    send(req->cmdsock,cmd,strlen(cmd),0);
+
+    // MORESTATS
+    if (req->moreinfo) {
+       // Send detailed ul/dl stats in JSON format.
+
+       std::ostringstream oss;
+       oss.setf(std::ios::fixed,std::ios::floatfield);
+       oss.precision(5);
+        std::set<Channel *>::iterator iter;
+        std::set<Channel *> peerchans = ft->GetChannels();
+
+        oss << "MOREINFO" << " " << root_hash.hex() << " ";
+
+        double tss = (double)Channel::Time() / 1000000.0L;
+        oss << "{\"timestamp\":\"" << tss << "\", ";
+        oss << "\"channels\":";
+        oss << "[";
+        for (iter=peerchans.begin(); iter!=peerchans.end(); iter++) {
+               Channel *c = *iter;
+               if (c != NULL) {
+                       if (iter!=peerchans.begin())
+                               oss << ", ";
+                       oss << "{";
+                       oss << "\"ip\": \"" << c->peer().ipv4str() << "\", ";
+                       oss << "\"port\": " << c->peer().port() << ", ";
+                       oss << "\"raw_bytes_up\": " << c->raw_bytes_up() << ", ";
+                       oss << "\"raw_bytes_down\": " << c->raw_bytes_down() << ", ";
+                       oss << "\"bytes_up\": " << c->bytes_up() << ", ";
+                       oss << "\"bytes_down\": " << c->bytes_down() << " ";
+                       oss << "}";
+               }
+        }
+        oss << "], ";
+        oss << "\"raw_bytes_up\": " << Channel::global_raw_bytes_up << ", ";
+        oss << "\"raw_bytes_down\": " << Channel::global_raw_bytes_down << ", ";
+        oss << "\"bytes_up\": " << Channel::global_bytes_up << ", ";
+        oss << "\"bytes_down\": " << Channel::global_bytes_down << " ";
+        oss << "}";
+
+        oss << "\r\n";
+
+        std::stringbuf *pbuf=oss.rdbuf();
+        size_t slen = strlen(pbuf->str().c_str());
+        send(req->cmdsock,pbuf->str().c_str(),slen,0);
+    }
+}
+
+
+void CmdGwSendPLAY(int transfer)
+{
+       // Send PLAY message to user
+       if (cmd_gw_debug)
+               fprintf(stderr,"cmd: SendPLAY: %d\n", transfer );
+
+    cmd_gw_t* req = CmdGwFindRequestByTransfer(transfer);
+    Sha1Hash root_hash = FileTransfer::file(transfer)->root_hash();
+
+    char cmd[MAX_CMD_MESSAGE];
+    // Slightly diff format: roothash as ID after CMD
+    sprintf(cmd,"PLAY %s http://%s/%s\r\n",root_hash.hex().c_str(),cmd_gw_httpaddr.str(),root_hash.hex().c_str());
+
+    fprintf(stderr,"cmd: SendPlay: %s", cmd);
+
+    send(req->cmdsock,cmd,strlen(cmd),0);
+}
+
+
+void CmdGwSwiftFirstProgressCallback (int transfer, bin_t bin)
+{
+       // First CMDGW_MAX_PREBUF_BYTES bytes received via swift,
+       // tell user to PLAY
+       // ARNOSMPTODO: bitrate-dependent prebuffering?
+       if (cmd_gw_debug)
+               fprintf(stderr,"cmd: SwiftFirstProgress: %d\n", transfer );
+
+       swift::RemoveProgressCallback(transfer,&CmdGwSwiftFirstProgressCallback);
+
+       CmdGwSendPLAY(transfer);
+}
+
+
+void CmdGwSwiftErrorCallback (evutil_socket_t cmdsock)
+{
+       // Error on swift socket callback
+
+       const char *response = "ERROR Swift Engine Problem\r\n";
+       send(cmdsock,response,strlen(response),0);
+
+       //swift::close_socket(sock);
+}
+
+
+
+void CmdGwUpdateDLStateCallback(cmd_gw_t* req)
+{
+       // Periodic callback, tell user INFO
+       CmdGwSendINFO(req,DLSTATUS_DOWNLOADING);
+
+       // Update speed measurements such that they decrease when DL/UL stops
+       FileTransfer *ft = FileTransfer::file(req->transfer);
+       ft->OnRecvData(0);
+       ft->OnSendData(0);
+
+       if (false)
+       {
+               // DEBUG download speed rate limit
+               double dlspeed = ft->GetCurrentSpeed(DDIR_DOWNLOAD);
+#ifdef WIN32
+               double dt = max(0.000001,(double)(usec_time() - req->startt)/TINT_SEC);
+#else
+               double dt = std::max(0.000001,(double)(usec_time() - req->startt)/TINT_SEC);
+#endif
+               double exspeed = (double)(swift::Complete(req->transfer)) / dt;
+               fprintf(stderr,"cmd: UpdateDLStateCallback: SPEED %lf == %lf\n", dlspeed, exspeed );
+       }
+}
+
+
+void CmdGwUpdateDLStatesCallback()
+{
+       // Called by swift main approximately every second
+       // Loop over all swarms
+    for(int i=0; i<cmd_gw_reqs_open; i++)
+    {
+       cmd_gw_t* req = &cmd_requests[i];
+       CmdGwUpdateDLStateCallback(req);
+    }
+}
+
+
+
+void CmdGwDataCameInCallback(struct bufferevent *bev, void *ctx)
+{
+       // Turn TCP stream into lines deliniated by \r\n
+       evutil_socket_t cmdsock = bufferevent_getfd(bev);
+       if (cmd_gw_debug)
+               fprintf(stderr,"CmdGwDataCameIn: ENTER %d\n", cmdsock );
+
+       struct evbuffer *inputevbuf = bufferevent_get_input(bev);
+        int ret = evbuffer_add_buffer(cmd_evbuffer,inputevbuf);
+       if (ret == -1) {
+               CmdGwCloseConnection(cmdsock);
+               return;
+       }
+
+
+       while (CmdGwReadLine(cmdsock))
+               ;
+}
+
+bool CmdGwReadLine(evutil_socket_t cmdsock)
+{
+       // Parse cmd_evbuffer for lines, and call NewRequest when found
+
+       size_t rd=0;
+    char *cmd = evbuffer_readln(cmd_evbuffer,&rd, EVBUFFER_EOL_CRLF_STRICT);
+    if (cmd != NULL)
+    {
+       CmdGwNewRequestCallback(cmdsock,cmd);
+       free(cmd);
+       return true;
+    }
+    else
+       return false;
+}
+
+int CmdGwHandleCommand(evutil_socket_t cmdsock, char *copyline);
+
+void CmdGwNewRequestCallback(evutil_socket_t cmdsock, char *line)
+{
+       // New command received from user
+
+    // CMD request line
+       char *copyline = (char *)malloc(strlen(line)+1);
+       strcpy(copyline,line);
+
+       int ret = CmdGwHandleCommand(cmdsock,copyline);
+       if (ret < 0) {
+               dprintf("cmd: Error parsing command %s\n", line );
+               char *cmd = NULL;
+               if (ret == ERROR_UNKNOWN_CMD)
+                       cmd = "ERROR unknown command\r\n";
+               else if (ret == ERROR_MISS_ARG)
+                       cmd = "ERROR missing parameter\r\n";
+               else
+                       cmd = "ERROR bad parameter\r\n";
+               send(cmdsock,cmd,strlen(cmd),0);
+        CmdGwCloseConnection(cmdsock);
+       }
+
+    free(copyline);
+}
+
+
+int CmdGwHandleCommand(evutil_socket_t cmdsock, char *copyline)
+{
+       char *method=NULL,*paramstr = NULL;
+       char * token = strchr(copyline,' '); // split into CMD PARAM
+       if (token != NULL) {
+               *token = '\0';
+               paramstr = token+1;
+       }
+       else
+               paramstr = "";
+
+       method = copyline;
+
+    fprintf(stderr,"cmd: GOT %s %s\n", method, paramstr);
+
+    char *savetok = NULL;
+    if (!strcmp(method,"START"))
+    {
+       // New START request
+        cmd_gw_t* req = cmd_requests + cmd_gw_reqs_open++;
+        req->id = ++cmd_gw_reqs_count;
+        req->cmdsock = cmdsock;
+
+        //fprintf(stderr,"cmd: START: new request %i\n",req->id);
+
+       char *url = paramstr;
+        // parse URL
+               // tswift://tracker/roothash-as-hex$chunksize@duration-in-secs
+        char *trackerstr=NULL,*hashstr=NULL,*durationstr=NULL,*chunksizestr=NULL;
+
+        bool haschunksize = (bool)(strchr(paramstr,'$') != NULL);
+        bool hasduration = (bool)(strchr(paramstr,'@') != NULL); // FAXME: user@ in tracker URL
+
+        token = strtok_r(url,"/",&savetok); // tswift://
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        token = strtok_r(NULL,"/",&savetok);      // tracker:port
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        trackerstr = token;
+
+        if (haschunksize && hasduration) {
+               token = strtok_r(NULL,"$",&savetok);       // roothash
+               if (token == NULL)
+                       return ERROR_BAD_ARG;
+               hashstr = token;
+
+            token = strtok_r(NULL,"@",&savetok);                       // chunksize
+            if (token == NULL)
+                return ERROR_BAD_ARG;
+            chunksizestr = token;
+
+               token = strtok_r(NULL,"",&savetok);             // duration
+               if (token == NULL)
+                       return ERROR_BAD_ARG;
+               durationstr = token;
+        }
+        else if (haschunksize) {
+               token = strtok_r(NULL,"$",&savetok);       // roothash
+               if (token == NULL)
+                       return ERROR_BAD_ARG;
+               hashstr = token;
+
+            token = strtok_r(NULL,"",&savetok);                        // chunksize
+            if (token == NULL)
+                       return ERROR_BAD_ARG;
+            chunksizestr = token;
+        }
+        else if (hasduration) {
+               token = strtok_r(NULL,"@",&savetok);       // roothash
+               if (token == NULL)
+                       return ERROR_BAD_ARG;
+               hashstr = token;
+
+            token = strtok_r(NULL,"",&savetok);                        // duration
+            if (token == NULL)
+                       return ERROR_BAD_ARG;
+            durationstr = token;
+        }
+        else {
+               token = strtok_r(NULL,"",&savetok);       // roothash
+               if (token == NULL)
+                       return ERROR_BAD_ARG;
+               hashstr = token;
+        }
+
+        dprintf("cmd: START: parsed tracker %s hash %s dur %s cs %s\n",trackerstr,hashstr,durationstr,chunksizestr);
+
+        if (strlen(hashstr)!=40) {
+               dprintf("cmd: START: roothash too short %i\n", strlen(hashstr) );
+            return ERROR_BAD_ARG;
+        }
+        uint32_t chunksize=SWIFT_DEFAULT_CHUNK_SIZE;
+        if (haschunksize) {
+               int n = sscanf(chunksizestr,"%i",&chunksize);
+               if (n != 1)
+                       return ERROR_BAD_ARG;
+        }
+        int duration=0;
+        if (hasduration) {
+               int n = sscanf(durationstr,"%i",&duration);
+               if (n != 1)
+                       return ERROR_BAD_ARG;
+        }
+
+        dprintf("cmd: START: %s with tracker %s chunksize %i duration %i\n",hashstr,trackerstr,chunksize,duration);
+
+        // FAXME: return duration in HTTPGW
+
+        Address trackaddr;
+               trackaddr = Address(trackerstr);
+               if (trackaddr==Address())
+               {
+                       dprintf("cmd: START: tracker address must be hostname:port, ip:port or just port\n");
+               return ERROR_BAD_ARG;
+               }
+               // SetTracker(trackaddr); == set default tracker
+
+        // initiate transmission
+        Sha1Hash root_hash = Sha1Hash(true,hashstr);
+
+        // Send INFO DLSTATUS_HASHCHECKING
+               CmdGwSendINFOHashChecking(req,root_hash);
+
+               // ARNOSMPTODO: disable/interleave hashchecking at startup
+        int transfer = swift::Find(root_hash);
+        if (transfer==-1)
+            transfer = swift::Open(hashstr,root_hash,trackaddr,false,true,chunksize);
+
+        // RATELIMIT
+        //FileTransfer::file(transfer)->SetMaxSpeed(DDIR_DOWNLOAD,512*1024);
+
+        req->transfer = transfer;
+        req->startt = usec_time();
+
+        // See HashTree::HashTree
+        req->contentfilename = (char *)malloc(strlen(hashstr)+1);
+        strcpy(req->contentfilename,hashstr);
+
+        if (cmd_gw_debug)
+               fprintf(stderr,"cmd: Already on disk is %lli/%lli\n", swift::Complete(transfer), swift::Size(transfer));
+
+        // Wait for prebuffering and then send PLAY to user
+       // ARNOSMPTODO: OUTOFORDER: breaks with out-of-order download
+        if (swift::Size(transfer) >= CMDGW_MAX_PREBUF_BYTES)
+        {
+            CmdGwSwiftFirstProgressCallback(transfer,bin_t(0,0));
+            CmdGwSendINFO(req, DLSTATUS_DOWNLOADING);
+        }
+        else
+        {
+               int progresslayer = bytes2layer(CMDGW_MAX_PREBUF_BYTES,swift::ChunkSize(transfer));
+            swift::AddProgressCallback(transfer,&CmdGwSwiftFirstProgressCallback,progresslayer);
+        }
+    }
+    else if (!strcmp(method,"REMOVE"))
+    {
+       // REMOVE roothash removestate removecontent\r\n
+       bool removestate = false, removecontent = false;
+
+        token = strtok_r(paramstr," ",&savetok); //
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        char *hashstr = token;
+        token = strtok_r(NULL," ",&savetok);      // removestate
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        removestate = !strcmp(token,"1");
+        token = strtok_r(NULL,"",&savetok);       // removecontent
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        removecontent = !strcmp(token,"1");
+
+       Sha1Hash root_hash = Sha1Hash(true,hashstr);
+       CmdGwGotREMOVE(root_hash,removestate,removecontent);
+    }
+    else if (!strcmp(method,"MAXSPEED"))
+    {
+       // MAXSPEED roothash direction speed-float-kb/s\r\n
+       data_direction_t ddir;
+       double speed;
+
+        token = strtok_r(paramstr," ",&savetok); //
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        char *hashstr = token;
+        token = strtok_r(NULL," ",&savetok);      // direction
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        ddir = !strcmp(token,"DOWNLOAD") ? DDIR_DOWNLOAD : DDIR_UPLOAD;
+        token = strtok_r(NULL,"",&savetok);       // speed
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        int n = sscanf(token,"%lf",&speed);
+        if (n == 0) {
+               dprintf("cmd: MAXSPEED: speed is not a float\n");
+                       return ERROR_MISS_ARG;
+        }
+       Sha1Hash root_hash = Sha1Hash(true,hashstr);
+       CmdGwGotMAXSPEED(root_hash,ddir,speed*1024.0);
+    }
+    else if (!strcmp(method,"CHECKPOINT"))
+    {
+       // CHECKPOINT roothash\r\n
+       Sha1Hash root_hash = Sha1Hash(true,paramstr);
+       CmdGwGotCHECKPOINT(root_hash);
+    }
+    else if (!strcmp(method,"SETMOREINFO"))
+    {
+       // GETMOREINFO roothash toggle\r\n
+        token = strtok_r(paramstr," ",&savetok); //
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        char *hashstr = token;
+        token = strtok_r(NULL," ",&savetok);      // direction
+        if (token == NULL)
+               return ERROR_MISS_ARG;
+        bool enable = (bool)!strcmp(token,"1");
+       Sha1Hash root_hash = Sha1Hash(true,hashstr);
+       CmdGwGotSETMOREINFO(root_hash,enable);
+    }
+    else if (!strcmp(method,"SHUTDOWN"))
+    {
+       CmdGwCloseConnection(cmdsock);
+       // Tell libevent to stop processing events
+       event_base_loopexit(Channel::evbase, NULL);
+    }
+    else
+    {
+       return ERROR_UNKNOWN_CMD;
+    }
+
+    return ERROR_NO_ERROR;
+}
+
+
+
+void CmdGwEventCameInCallback(struct bufferevent *bev, short events, void *ctx)
+{
+       if (events & BEV_EVENT_ERROR)
+               print_error("cmdgw: Error from bufferevent");
+    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
+    {
+       // Called when error on cmd connection
+       evutil_socket_t cmdsock = bufferevent_getfd(bev);
+       CmdGwCloseConnection(cmdsock);
+               bufferevent_free(bev);
+    }
+}
+
+
+void CmdGwNewConnectionCallback(struct evconnlistener *listener,
+    evutil_socket_t fd, struct sockaddr *address, int socklen,
+    void *ctx)
+{
+       // New TCP connection on cmd listen socket
+
+       fprintf(stderr,"cmd: Got new cmd connection %i\n",fd);
+    dprintf("DBG cmd: Got new cmd connection %i\n",fd);
+
+       struct event_base *base = evconnlistener_get_base(listener);
+       struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
+
+       bufferevent_setcb(bev, CmdGwDataCameInCallback, NULL, CmdGwEventCameInCallback, NULL);
+       bufferevent_enable(bev, EV_READ|EV_WRITE);
+
+
+       // One buffer for all cmd connections, reset
+       if (cmd_evbuffer != NULL)
+               evbuffer_free(cmd_evbuffer);
+    cmd_evbuffer = evbuffer_new();
+}
+
+
+void CmdGwListenErrorCallback(struct evconnlistener *listener, void *ctx)
+{
+       // libevent got error on cmd listener
+    struct event_base *base = evconnlistener_get_base(listener);
+    int err = EVUTIL_SOCKET_ERROR();
+    char errmsg[1024];
+    sprintf(errmsg, "cmdgw: Got a fatal error %d (%s) on the listener.\n", err, evutil_socket_error_to_string(err));
+
+    print_error(errmsg);
+    dprintf("%s @0 closed cmd gateway\n",tintstr());
+
+       evconnlistener_free(cmd_evlistener);
+}
+
+
+bool InstallCmdGateway (struct event_base *evbase,Address cmdaddr,Address httpaddr)
+{
+       // Allocate libevent listener for cmd connections
+       // From http://www.wangafu.net/~nickm/libevent-book/Ref8_listener.html
+
+    fprintf(stderr,"cmdgw: Creating new listener on addr %s\n", cmdaddr.str() );
+  
+    const struct sockaddr_in sin = (sockaddr_in)cmdaddr;
+
+    cmd_evlistener = evconnlistener_new_bind(evbase, CmdGwNewConnectionCallback, NULL,
+        LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
+        (const struct sockaddr *)&sin, sizeof(sin));
+    if (!cmd_evlistener) {
+            print_error("Couldn't create listener");
+            return false;
+    }
+    evconnlistener_set_error_cb(cmd_evlistener, CmdGwListenErrorCallback);
+
+    cmd_gw_httpaddr = httpaddr;
+
+    cmd_evbuffer = evbuffer_new();
+
+    return true;
+}
+
diff --git a/src/libswift_udp/compat.cpp b/src/libswift_udp/compat.cpp
new file mode 100644 (file)
index 0000000..04e3f6f
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ *  compat.cpp
+ *  swift
+ *
+ *  Created by Arno Bakker, Victor Grishchenko
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include "compat.h"
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#ifdef _WIN32
+#include <tchar.h>
+#include <io.h>
+#include <sys/timeb.h>
+#include <vector>
+#include <stdexcept>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+namespace swift {
+
+#ifdef _WIN32
+static HANDLE map_handles[1024];
+#endif
+
+int64_t file_size (int fd) {
+
+#ifdef WIN32
+       struct _stat32i64 st;
+    _fstat32i64(fd, &st);
+#else
+    struct stat st;
+    fstat(fd, &st);
+#endif
+    return st.st_size;
+}
+
+int     file_seek (int fd, int64_t offset) {
+#ifndef _WIN32
+    return lseek(fd,offset,SEEK_SET);
+#else
+    return _lseeki64(fd,offset,SEEK_SET);
+#endif
+}
+
+int     file_resize (int fd, int64_t new_size) {
+#ifndef _WIN32
+    return ftruncate(fd, new_size);
+#else
+    // Arno, 2011-10-27: Use 64-bit version
+    if (_chsize_s(fd,new_size) != 0)
+       return -1;
+    else
+       return 0;
+#endif
+}
+
+void print_error(const char* msg) {
+    perror(msg);
+#ifdef _WIN32
+    int e = WSAGetLastError();
+    if (e)
+        fprintf(stderr,"windows error #%u\n",e);
+#endif
+}
+
+void*   memory_map (int fd, size_t size) {
+    if (!size)
+        size = file_size(fd);
+    void *mapping;
+#ifndef _WIN32
+    mapping = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    if (mapping==MAP_FAILED)
+        return NULL;
+    return mapping;
+#else
+    HANDLE fhandle = (HANDLE)_get_osfhandle(fd);
+    assert(fd<1024);
+    HANDLE maphandle = CreateFileMapping(     fhandle,
+                                       NULL,
+                                       PAGE_READWRITE,
+                                       0,
+                                       0,
+                                       NULL    );
+    if (maphandle == NULL)
+        return NULL;
+    map_handles[fd] = maphandle;
+
+    mapping = MapViewOfFile         (  maphandle,
+                                       FILE_MAP_WRITE,
+                                       0,
+                                       0,
+                                       0  );
+
+    return mapping;
+#endif
+}
+
+void    memory_unmap (int fd, void* mapping, size_t size) {
+#ifndef _WIN32
+    munmap(mapping,size);
+    close(fd);
+#else
+    UnmapViewOfFile(mapping);
+    CloseHandle(map_handles[fd]);
+#endif
+}
+
+#ifdef _WIN32
+
+size_t pread(int fildes, void *buf, size_t nbyte, __int64 offset)
+{
+    _lseeki64(fildes,offset,SEEK_SET);
+    return read(fildes,buf,nbyte);
+}
+
+size_t pwrite(int fildes, const void *buf, size_t nbyte, __int64 offset)
+{
+    _lseeki64(fildes,offset,SEEK_SET);
+    return write(fildes,buf,nbyte);
+}
+
+
+int inet_aton(const char *cp, struct in_addr *inp)
+{
+    inp->S_un.S_addr = inet_addr(cp);
+    return 1;
+}
+
+#endif
+
+#ifdef _WIN32
+
+LARGE_INTEGER get_freq() {
+    LARGE_INTEGER proc_freq;
+    if (!::QueryPerformanceFrequency(&proc_freq))
+       print_error("HiResTimeOfDay: QueryPerformanceFrequency() failed");
+    return proc_freq;
+}
+
+tint usec_time(void)
+{
+       static LARGE_INTEGER last_time;
+       LARGE_INTEGER cur_time;
+       QueryPerformanceCounter(&cur_time);
+       if (cur_time.QuadPart<last_time.QuadPart)
+               print_error("QueryPerformanceCounter wrapped"); // does this happen?
+       last_time = cur_time;
+       static float freq = 1000000.0/get_freq().QuadPart;
+       tint usec = cur_time.QuadPart * freq;
+       return usec;
+}
+
+
+#else
+
+tint usec_time(void)
+{
+    struct timeval t;
+    gettimeofday(&t,NULL);
+    tint ret;
+    ret = t.tv_sec;
+    ret *= 1000000;
+    ret += t.tv_usec;
+    return ret;
+}
+
+#endif
+
+void LibraryInit(void)
+{
+#ifdef _WIN32
+       static WSADATA _WSAData;
+       // win32 requires you to initialize the Winsock DLL with the desired
+       // specification version
+       WORD wVersionRequested;
+    wVersionRequested = MAKEWORD(2, 2);
+       WSAStartup(wVersionRequested, &_WSAData);
+#endif
+}
+
+
+std::string gettmpdir(void)
+{
+#ifdef _WIN32
+  DWORD result = ::GetTempPath(0, _T(""));
+  if (result == 0)
+       throw std::runtime_error("Could not get system temp path");
+
+  std::vector<TCHAR> tempPath(result + 1);
+  result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
+  if((result == 0) || (result >= tempPath.size()))
+       throw std::runtime_error("Could not get system temp path");
+
+  return std::string(tempPath.begin(), tempPath.begin() + static_cast<std::size_t>(result));
+#else
+         return std::string("/tmp/");
+#endif
+}
+
+bool    make_socket_nonblocking(evutil_socket_t fd) {
+#ifdef _WIN32
+    u_long enable = 1;
+    return 0==ioctlsocket(fd, FIONBIO, &enable);
+#else
+    return 0==fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+}
+
+bool    close_socket (evutil_socket_t sock) {
+#ifdef _WIN32
+    return 0==closesocket(sock);
+#else
+    return 0==::close(sock);
+#endif
+}
+
+    
+// Arno: not thread safe!
+struct timeval* tint2tv (tint t) {
+    static struct timeval tv;
+    tv.tv_usec = t%TINT_SEC;
+    tv.tv_sec = t/TINT_SEC;
+    return &tv;
+}
+
+
+}
diff --git a/src/libswift_udp/compat.h b/src/libswift_udp/compat.h
new file mode 100644 (file)
index 0000000..0604bb4
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ *  compat.h
+ *  compatibility wrappers
+ *
+ *  Created by Arno Bakker, Victor Grishchenko
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#ifndef SWIFT_COMPAT_H
+#define SWIFT_COMPAT_H
+
+#ifdef _MSC_VER
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned short uint16_t;
+typedef short int16_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <xutility> // for std::min/max
+#else
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#endif
+
+#include <fcntl.h>
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <errno.h>
+
+#ifdef _MSC_VER
+#include "getopt_win.h"
+#else
+#include <getopt.h>
+#endif
+
+#ifdef _WIN32
+#define open(a,b,c)    _open(a,b,c)
+#define strcasecmp        stricmp
+#define strtok_r          strtok_s
+#define stat(a,b)      _stat(a,b)
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR _S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR _S_IWRITE
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP _S_IREAD
+#endif
+#ifndef S_IROTH
+#define S_IROTH _S_IREAD
+#endif
+
+#ifdef _WIN32
+typedef char* setsockoptptr_t;
+typedef int socklen_t;
+#else
+typedef void* setsockoptptr_t;
+#endif
+
+// libevent2 assumes WIN32 is defined
+#ifdef _WIN32
+#define WIN32  _WIN32
+#endif
+#include <event2/util.h>
+
+#ifndef _WIN32
+#define INVALID_SOCKET -1
+#endif
+
+#ifndef LONG_MAX
+#include <limits>
+#define LONG_MAX       numeric_limits<int>::max()
+#endif
+
+#ifdef _WIN32
+// log2 is C99 which is not fully supported by MS VS
+#define log2(x)                (log(x)/log(2.0))
+#endif
+
+
+// Arno, 2012-01-05: Handle 64-bit size_t & printf+scanf
+#if SIZE_MAX > UINT_MAX
+#define PRISIZET               "%llu"
+#else
+#define PRISIZET       "%lu"
+#endif
+
+namespace swift {
+
+/** tint is the time integer type; microsecond-precise. */
+typedef int64_t tint;
+#define TINT_HOUR ((swift::tint)1000000*60*60)
+#define TINT_MIN ((swift::tint)1000000*60)
+#define TINT_SEC ((swift::tint)1000000)
+#define TINT_MSEC ((swift::tint)1000)
+#define TINT_uSEC ((swift::tint)1)
+#define TINT_NEVER ((swift::tint)0x3fffffffffffffffLL)
+
+#ifdef _WIN32
+#define tintabs        _abs64
+#else
+#define tintabs        ::abs
+#endif
+
+
+#ifndef _WIN32
+#define TCHAR  char
+#endif
+
+
+int64_t  file_size (int fd);
+
+int     file_seek (int fd, int64_t offset);
+
+int     file_resize (int fd, int64_t new_size);
+
+void*   memory_map (int fd, size_t size=0);
+void    memory_unmap (int fd, void*, size_t size);
+
+void    print_error (const char* msg);
+
+#ifdef _WIN32
+
+/** UNIX pread approximation. Does change file pointer. Is not thread-safe */
+size_t  pread(int fildes, void *buf, size_t nbyte, __int64 offset); // off_t not 64-bit dynamically on Win32
+
+/** UNIX pwrite approximation. Does change file pointer. Is not thread-safe */
+size_t  pwrite(int fildes, const void *buf, size_t nbyte, __int64 offset);
+
+int     inet_aton(const char *cp, struct in_addr *inp);
+
+#endif
+
+std::string gettmpdir(void);
+
+tint    usec_time ();
+
+bool    make_socket_nonblocking(evutil_socket_t s);
+
+bool    close_socket (evutil_socket_t sock);
+
+struct timeval* tint2tv (tint t);
+
+
+};
+
+#endif
+
diff --git a/src/libswift_udp/do_tests.sh b/src/libswift_udp/do_tests.sh
new file mode 100755 (executable)
index 0000000..f02a6cc
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+for tst in `ls tests/*test | grep -v ledbat`; do
+    if echo $tst; $tst > $tst.log; then
+        echo $tst OK
+    else
+        echo $tst FAIL
+    fi
+done
diff --git a/src/libswift_udp/doc/apusapus.png b/src/libswift_udp/doc/apusapus.png
new file mode 100644 (file)
index 0000000..77a6a26
Binary files /dev/null and b/src/libswift_udp/doc/apusapus.png differ
diff --git a/src/libswift_udp/doc/binmaps-alenex.pdf b/src/libswift_udp/doc/binmaps-alenex.pdf
new file mode 100644 (file)
index 0000000..d67b8c2
Binary files /dev/null and b/src/libswift_udp/doc/binmaps-alenex.pdf differ
diff --git a/src/libswift_udp/doc/cc-states.png b/src/libswift_udp/doc/cc-states.png
new file mode 100644 (file)
index 0000000..57b5d49
Binary files /dev/null and b/src/libswift_udp/doc/cc-states.png differ
diff --git a/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.nroff b/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.nroff
new file mode 100644 (file)
index 0000000..8f6dd7d
--- /dev/null
@@ -0,0 +1,2087 @@
+.\" Auto generated Nroff by NroffEdit on April 12, 2010\r
+.pl 10.0i\r
+.po 0\r
+.ll 7.2i\r
+.lt 7.2i\r
+.nr LL 7.2i\r
+.nr LT 7.2i\r
+.ds LF Grishchenko and Bakker\r
+.ds RF FORMFEED[Page %]\r
+.ds LH Internet-Draft\r
+.ds RH December 19, 2011\r
+.ds CH swift\r
+.ds CF Expires June 21, 2012\r
+.hy 0\r
+.nh\r
+.ad l\r
+.in 0\r
+.nf\r
+.tl 'PPSP' 'A. Bakker'\r
+.tl 'Internet-Draft' 'TU Delft'\r
+.tl 'Intended status: Informational' \r
+.tl 'Expires: June 21, 2012' 'December 19, 2011'\r
+\r
+.fi\r
+.in 3\r
+.in 12\r
+.ti 8\r
+Peer-to-Peer Streaming Protocol (PPSP) \%<draft-ietf-ppsp-peer-protocol-00.txt>\r
+\r
+.ti 0\r
+Abstract\r
+\r
+.in 3\r
+The Generic Multiparty Protocol (swift) is a peer-to-peer based transport\r
+protocol for content dissemination. It can be used for streaming on-demand\r
+and live video content, as well as conventional downloading. In swift, the\r
+clients consuming the content participate in the dissemination by forwarding\r
+the content to other clients via a mesh-like structure.  It is a generic\r
+protocol which can run directly on top of UDP, TCP, HTTP or as a RTP \r
+profile. Features of swift are short time-till-playback and extensibility. \r
+Hence, it can use different mechanisms to prevent freeriding, and work \r
+with different peer discovery schemes (centralized trackers or\r
+Distributed Hash Tables). Depending on the underlying transport\r
+protocol, swift can also use different congestion control algorithms, \r
+such as LEDBAT, and offer transparent NAT traversal. Finally, swift maintains \r
+only a small amount of state per peer and detects malicious modification of \r
+content. This documents describes swift and how it satisfies the requirements \r
+for the IETF Peer-to-Peer Streaming Protocol (PPSP) Working Group's peer \r
+protocol.\r
+\r
+\r
+.ti 0\r
+Status of this memo\r
+\r
+This Internet-Draft is submitted to IETF in full conformance with the\r
+provisions of BCP 78 and BCP 79.\r
+\r
+Internet-Drafts are working documents of the Internet Engineering Task Force\r
+(IETF), its areas, and its working groups.  Note that other groups may also\r
+distribute working documents as Internet- Drafts.\r
+\r
+Internet-Drafts are draft documents valid for a maximum of six months and\r
+may be updated, replaced, or obsoleted by other documents at any time.  It\r
+is inappropriate to use Internet-Drafts as reference material or to cite\r
+them other than as "work in progress."\r
+\r
+The list of current Internet-Drafts can be accessed at\r
+\%http://www.ietf.org/ietf/1id-abstracts.txt.\r
+\r
+The list of Internet-Draft Shadow Directories can be accessed at\r
+http://www.ietf.org/shadow.html.\r
+\r
+\r
+.nf\r
+Copyright (c) 2011 IETF Trust and the persons identified as the\r
+document authors.  All rights reserved.\r
+\r
+This document is subject to BCP 78 and the IETF Trust's Legal\r
+Provisions Relating to IETF Documents\r
+\%(http://trustee.ietf.org/license-info) in effect on the date of\r
+publication of this document.  Please review these documents\r
+carefully, as they describe your rights and restrictions with respect\r
+to this document.  Code Components extracted from this document must\r
+include Simplified BSD License text as described in Section 4.e of\r
+the Trust Legal Provisions and are provided without warranty as\r
+described in the Simplified BSD License.\r
+\r
+.\" \# TD4  -- Set TOC depth by altering this value (TD5 = depth 5)\r
+.\" \# TOC  -- Beginning of auto updated Table of Contents\r
+.in 0\r
+Table of Contents\r
+\r
+.nf\r
+   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  3\r
+     1.1. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . .  3\r
+     1.2. Conventions Used in This Document . . . . . . . . . . . . .  4\r
+     1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . .  5\r
+   2. Overall Operation . . . . . . . . . . . . . . . . . . . . . . .  6\r
+     2.1. Joining a Swarm . . . . . . . . . . . . . . . . . . . . . .  6\r
+     2.2. Exchanging Chunks . . . . . . . . . . . . . . . . . . . . .  6\r
+     2.3. Leaving a Swarm . . . . . . . . . . . . . . . . . . . . . .  7\r
+   3. Messages  . . . . . . . . . . . . . . . . . . . . . . . . . . .  7\r
+     3.1. HANDSHAKE . . . . . . . . . . . . . . . . . . . . . . . . .  8\r
+     3.3. HAVE  . . . . . . . . . . . . . . . . . . . . . . . . . . .  8\r
+       3.3.1. Bin Numbers . . . . . . . . . . . . . . . . . . . . . .  8\r
+       3.3.2. HAVE Message  . . . . . . . . . . . . . . . . . . . . .  9\r
+     3.4. ACK . . . . . . . . . . . . . . . . . . . . . . . . . . . .  9\r
+     3.5. DATA and HASH . . . . . . . . . . . . . . . . . . . . . . . 10\r
+       3.5.1. Merkle Hash Tree  . . . . . . . . . . . . . . . . . . . 10\r
+       3.5.2. Content Integrity Verification  . . . . . . . . . . . . 11\r
+       3.5.3. The Atomic Datagram Principle . . . . . . . . . . . . . 11\r
+       3.5.4. DATA and HASH Messages  . . . . . . . . . . . . . . . . 12\r
+     3.6. HINT  . . . . . . . . . . . . . . . . . . . . . . . . . . . 13\r
+     3.7. Peer Address Exchange and NAT Hole Punching . . . . . . . . 13\r
+     3.8. KEEPALIVE . . . . . . . . . . . . . . . . . . . . . . . . . 14\r
+     3.9. VERSION . . . . . . . . . . . . . . . . . . . . . . . . . . 14\r
+     3.10. Conveying Peer Capabilities  . . . . . . . . . . . . . . . 14\r
+     3.11. Directory Lists  . . . . . . . . . . . . . . . . . . . . . 14\r
+   4. Automatic Detection of Content Size . . . . . . . . . . . . . . 14\r
+     4.1. Peak Hashes . . . . . . . . . . . . . . . . . . . . . . . . 15\r
+     4.2. Procedure . . . . . . . . . . . . . . . . . . . . . . . . . 16\r
+   5. Live streaming  . . . . . . . . . . . . . . . . . . . . . . . . 17\r
+   6. Transport Protocols and Encapsulation . . . . . . . . . . . . . 17\r
+     6.1. UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17\r
+       6.1.1. Chunk Size  . . . . . . . . . . . . . . . . . . . . . . 17\r
+       6.1.2. Datagrams and Messages  . . . . . . . . . . . . . . . . 18\r
+       6.1.3. Channels  . . . . . . . . . . . . . . . . . . . . . . . 18\r
+       6.1.4. HANDSHAKE and VERSION . . . . . . . . . . . . . . . . . 19\r
+       6.1.5. HAVE  . . . . . . . . . . . . . . . . . . . . . . . . . 20\r
+       6.1.6. ACK . . . . . . . . . . . . . . . . . . . . . . . . . . 20\r
+       6.1.7. HASH  . . . . . . . . . . . . . . . . . . . . . . . . . 20\r
+       6.1.8. DATA  . . . . . . . . . . . . . . . . . . . . . . . . . 20\r
+       6.1.9. KEEPALIVE . . . . . . . . . . . . . . . . . . . . . . . 20\r
+       6.1.10. Flow and Congestion Control  . . . . . . . . . . . . . 21\r
+     6.2. TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21\r
+     6.3. RTP Profile for PPSP  . . . . . . . . . . . . . . . . . . . 21\r
+       6.3.1. Design  . . . . . . . . . . . . . . . . . . . . . . . . 22\r
+       6.3.2. PPSP Requirements . . . . . . . . . . . . . . . . . . . 24\r
+     6.4. HTTP (as PPSP)  . . . . . . . . . . . . . . . . . . . . . . 27\r
+       6.4.1. Design  . . . . . . . . . . . . . . . . . . . . . . . . 27\r
+       6.4.2. PPSP Requirements . . . . . . . . . . . . . . . . . . . 29\r
+   7. Security Considerations . . . . . . . . . . . . . . . . . . . . 32\r
+   8. Extensibility . . . . . . . . . . . . . . . . . . . . . . . . . 32\r
+     8.1. 32 bit vs 64 bit  . . . . . . . . . . . . . . . . . . . . . 32\r
+     8.2. IPv6  . . . . . . . . . . . . . . . . . . . . . . . . . . . 32\r
+     8.3. Congestion Control Algorithms . . . . . . . . . . . . . . . 32\r
+     8.4. Piece Picking Algorithms  . . . . . . . . . . . . . . . . . 33\r
+     8.5. Reciprocity Algorithms  . . . . . . . . . . . . . . . . . . 33\r
+     8.6. Different crypto/hashing schemes  . . . . . . . . . . . . . 33\r
+   9. Rationale . . . . . . . . . . . . . . . . . . . . . . . . . . . 33\r
+     9.1.  Design Goals . . . . . . . . . . . . . . . . . . . . . . . 34\r
+     9.2.  Not TCP  . . . . . . . . . . . . . . . . . . . . . . . . . 35\r
+     9.3.  Generic Acknowledgments  . . . . . . . . . . . . . . . . . 36\r
+   Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . 37\r
+   References . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37\r
+   Authors' addresses . . . . . . . . . . . . . . . . . . . . . . . . 39\r
+.fi\r
+.in 3\r
+\r
+.\" \# ETC -- End of auto updated Table of Contents\r
+\r
+\r
+\r
+.ti 0\r
+1.  Introduction\r
+\r
+.ti 0\r
+1.1. Purpose\r
+\r
+This document describes the Generic Multiparty Protocol (swift), designed\r
+from the ground up for the task of disseminating the same content to a group\r
+of interested parties. Swift supports streaming on-demand and\r
+live video content, as well as conventional downloading, thus covering\r
+today's three major use cases for content distribution. To fulfil this task,\r
+clients consuming the content are put on equal footing with the servers\r
+initially providing the content to create a peer-to-peer system where\r
+everyone can provide data. Each peer connects to a random set of other peers\r
+resulting in a mesh-like structure. \r
+\r
+Swift uses a simple method of naming content based on self-certification. In\r
+particular, content in swift is identified by a single cryptographic hash\r
+that is the root hash in a Merkle hash tree calculated recursively from the\r
+content [ABMRKL].  This self-certifying hash tree allows every peer to\r
+directly detect when a malicious peer tries to distribute fake content. It\r
+also ensures only a small amount of information is needed to start a\r
+download (just the root hash and some peer addresses).\r
+\r
+Swift uses a novel method of addressing chunks of content called "bin\r
+numbers". Bin numbers allow the addressing of a binary interval of data\r
+using a single integer. This reduces the amount of state that needs to be\r
+recorded per peer and the space needed to denote intervals on the wire,\r
+making the protocol light-weight. In general, this numbering system allows \r
+swift to work with simpler data structures, e.g. to use arrays instead of \r
+binary trees, thus reducing complexity.\r
+\r
+Swift is a generic protocol which can run directly on top of UDP, TCP, HTTP, \r
+or as a layer below RTP, similar to SRTP [RFC3711]. As such, swift defines a \r
+common set of messages that make up the protocol, which can have different\r
+representations on the wire depending on the lower-level protocol used. When\r
+the lower-level transport is UDP, swift can also use different congestion \r
+control algorithms and facilitate NAT traversal. \r
+\r
+In addition, swift is extensible in the mechanisms it uses to promote client\r
+contribution and prevent freeriding, that is, how to deal with peers \r
+that only download content but never upload to others. Furthermore, \r
+it can work with different peer discovery schemes, \r
+such as centralized trackers or fast Distributed Hash Tables [JIM11]. \r
+\r
+This documents describes not only the swift protocol but also how it\r
+satisfies the requirements for the IETF Peer-to-Peer Streaming Protocol\r
+(PPSP) Working Group's peer protocol [PPSPCHART,I-D.ietf-ppsp-reqs].\r
+A reference implementation of swift over UDP is available [SWIFTIMPL].\r
+\r
+\r
+.ti 0\r
+1.2. Conventions Used in This Document\r
+\r
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",\r
+"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in\r
+this document are to be interpreted as described in [RFC2119].\r
+\r
+.ti 0\r
+1.3. Terminology\r
+\r
+.in 3\r
+message\r
+.br\r
+.in 8\r
+The basic unit of swift communication. A message will have different \r
+representations on the wire depending on the transport protocol used. Messages \r
+are typically multiplexed into a datagram for transmission.\r
+\r
+.in 3\r
+datagram\r
+.br\r
+.in 8\r
+A sequence of messages that is offered as a unit to the underlying transport \r
+protocol (UDP, etc.). The datagram is swift's Protocol Data Unit (PDU).\r
+\r
+.in 3\r
+content\r
+.br\r
+.in 8\r
+Either a live transmission, a pre-recorded multimedia asset, or a file.\r
+\r
+.in 3\r
+bin\r
+.br\r
+.in 8\r
+A number denoting a specific binary interval of the content (i.e., one \r
+or more consecutive chunks).\r
+\r
+.in 3\r
+chunk\r
+.br\r
+.in 8\r
+The basic unit in which the content is divided. E.g. a block of N \r
+kilobyte.\r
+\r
+.in 3\r
+hash\r
+.br\r
+.in 8\r
+The result of applying a cryptographic hash function, more specifically \r
+a modification detection code (MDC) [HAC01], such as SHA1 [FIPS180-2], \r
+to a piece of data. \r
+\r
+.in 3\r
+root hash\r
+.br\r
+.in 8\r
+The root in a Merkle hash tree calculated recursively from the content.\r
+\r
+.in 3\r
+swarm\r
+.br\r
+.in 8\r
+A group of peers participating in the distribution of the same content.\r
+\r
+.in 3\r
+swarm ID\r
+.br\r
+.in 8\r
+Unique identifier for a swarm of peers, in swift the root hash of the \r
+content (video-on-demand,download) or a public key (live streaming).\r
+\r
+.in 3\r
+tracker\r
+.br\r
+.in 8\r
+An entity that records the addresses of peers participating in a swarm,\r
+usually for a set of swarms, and makes this membership information \r
+available to other peers on request.\r
+\r
+.in 3\r
+choking\r
+.br\r
+.in 8\r
+When a peer A is choking peer B it means that A is currently not \r
+willing to accept requests for content from B.\r
+.in 3\r
+\r
+\r
+.ti 0\r
+2. Overall Operation\r
+\r
+The basic unit of communication in swift is the message. Multiple messages are\r
+multiplexed into a single datagram for transmission. A datagram (and hence the \r
+messages it contains) will have different representations on the wire depending \r
+on the transport protocol used (see Sec. 6).\r
+\r
+\r
+.ti 0\r
+2.1. Joining a Swarm\r
+\r
+Consider a peer A that wants to download a certain content asset.\r
+To commence a swift download, peer A must have the swarm ID of the content \r
+and a list of one or more tracker contact points (e.g. host+port). The list \r
+of trackers is optional in the presence of a decentralized tracking mechanism. \r
+The swarm ID consists of the swift root hash of the content (video-on-demand,\r
+downloading) or a public key (live streaming).\r
+\r
+Peer A now registers with the tracker following e.g. the PPSP tracker\r
+protocol [I-D.ietf.ppsp-reqs] and receives the IP address and port of peers\r
+already in the swarm, say B, C, and D. Peer A now sends a datagram\r
+containing a HANDSHAKE message to B, C, and D. This message serves as an\r
+end-to-end check that the peers are actually in the correct swarm, and\r
+contains the root hash of the swarm.  Peer B and C respond with datagrams\r
+containing a HANDSHAKE message and one or more HAVE messages. A HAVE message\r
+conveys (part of) the chunk availability of a peer and thus contains a bin\r
+number that denotes what chunks of the content peer B, resp. C have. Peer D\r
+sends a datagram with just a HANDSHAKE and omits HAVE messages as a way of \r
+choking A.\r
+\r
+.ti 0\r
+2.2. Exchanging Chunks\r
+\r
+In response to B and C, A sends new datagrams to B and C containing HINT messages.\r
+A HINT or request message indicates the chunks that a peer wants to\r
+download, and contains a bin number. The HINT messages to B and C refer to\r
+disjunct sets of chunks.  B and C respond with datagrams containing HASH,\r
+HAVE and DATA messages.  The HASH messages contains all cryptographic hashes\r
+that peer A needs to verify the integrity of the content chunk sent in the\r
+DATA message, using the content's root hash as trusted anchor, see Sec. 3.5.\r
+Using these hashes peer A verifies that the chunks received from B and C are \r
+correct. It also updates the chunk availability of B and C using the information\r
+in the received HAVE messages. \r
+\r
+After processing, A sends a datagram containing HAVE messages for the chunks\r
+it just received to all its peers. In the datagram to B and C it includes an\r
+ACK message acknowledging the receipt of the chunks, and adds HINT messages\r
+for new chunks. ACK messages are not used when a reliable transport protocol\r
+is used. When e.g. C finds that A obtained a chunk (from B) that C did not\r
+yet have, C's next datagram includes a HINT for that chunk.\r
+\r
+Peer D does not send HAVE messages to A when it downloads chunks from other peers,\r
+until D decides to unchoke peer A. In the case, it sends a datagram with\r
+HAVE messages to inform A of its current availability. If B or C decide to\r
+choke A they stop sending HAVE and DATA messages and A should then rerequest\r
+from other peers. They may continue to send HINT messages, or periodic\r
+KEEPALIVE messages such that A keeps sending them HAVE messages.\r
+\r
+Once peer A has received all content (video-on-demand use case) it stops\r
+sending messages to all other peers that have all content (a.k.a. seeders).\r
+Peer A MAY also contact the tracker or another source again to obtain more \r
+peer addresses.\r
+\r
+\r
+.ti 0\r
+2.3. Leaving a Swarm\r
+\r
+Depending on the transport protocol used, peers should either use explicit \r
+leave messages or implicitly leave a swarm by stopping to respond\r
+to messages. Peers that learn about the departure should remove these peers \r
+from the current peer list. The implicit-leave mechanism works for both graceful and \r
+ungraceful leaves (i.e., peer crashes or disconnects). When leaving gracefully, a \r
+peer should deregister from the tracker following the (PPSP) tracker protocol.\r
+\r
+\r
+.ti 0\r
+3. Messages\r
+\r
+.fi\r
+In general, no error codes or responses are used in the protocol; absence \r
+of any response indicates an error. Invalid messages are discarded. \r
+\r
+For the sake of simplicity, one swarm of peers always deals with one content \r
+asset (e.g. file) only. Retrieval of large collections of files is done by \r
+retrieving a directory list file and then recursively retrieving files, which \r
+might also turn to be directory lists, as described in Sec. 3.11. \r
+\r
+.ti 0\r
+3.1. HANDSHAKE\r
+\r
+As an end-to-end check that the peers are actually in the correct swarm, the\r
+initiating peer and the addressed peer SHOULD send a HANDSHAKE message in\r
+the first datagrams they exchange. The only payload of the HANDSHAKE message\r
+is the root hash of the content.  \r
+\r
+After the handshakes are exchanged, the initiator knows that the peer really\r
+responds. Hence, the second datagram the initiator sends MAY already contain\r
+some heavy payload. To minimize the number of initialization roundtrips, \r
+implementations MAY dispense with the HANDSHAKE message. To the same end,\r
+the first two datagrams exchanged MAY also contain some minor payload, e.g.\r
+HAVE messages to indicate the current progress of a peer or a HINT \r
+(see Sec. 3.6).\r
+\r
+\r
+.ti 0\r
+3.3. HAVE\r
+\r
+The HAVE message is used to convey which chunks a peers has available,\r
+expressed in a new content addressing scheme called "bin numbers".\r
+\r
+.ti 0\r
+3.3.1. Bin Numbers\r
+\r
+Swift employs a generic content addressing scheme based on binary intervals \r
+("bins" in short). The smallest interval is a chunk (e.g. a N kilobyte \r
+block), the top interval is the complete 2**63 range.  A novel addition \r
+to the classical scheme are "bin numbers", a scheme of numbering binary \r
+intervals which lays them out into a vector nicely. Consider an chunk\r
+interval of width W. To derive the bin numbers of the complete interval and\r
+the subintervals, a minimal balanced binary tree is built that is at least W \r
+chunks wide at the base. The leaves from left-to-right correspond to \r
+the chunks 0..W in the interval, and have bin number I*2 where I is the \r
+index of the chunk (counting beyond W-1 to balance the tree). The higher \r
+level nodes P in the tree have bin number\r
+\r
+        binP = (binL + binR) / 2\r
+\r
+.br     \r
+where binL is the bin of node P's left-hand child and binR is the bin of node\r
+P's right-hand child. Given that each node in the tree represents a\r
+subinterval of the original interval, each such subinterval now is\r
+addressable by a bin number, a single integer. The bin number tree of a\r
+interval of width W=8 looks like this:\r
+\r
+\r
+\r
+\r
+                        7\r
+.br\r
+                       / \\\r
+.br\r
+                     /     \\\r
+.br\r
+                   /         \\\r
+.br\r
+                 /             \\\r
+.br\r
+               3                11\r
+.br\r
+              / \\              / \\\r
+.br\r
+             /   \\            /   \\\r
+.br\r
+            /     \\          /     \\\r
+.br\r
+           1       5        9       13 \r
+.br\r
+          / \\     / \\      / \\      / \\\r
+.br\r
+         0   2   4   6    8   10  12   14\r
+.br \r
+\r
+.fi\r
+So bin 7 represents the complete interval, 3 represents the interval of\r
+chunk 0..3 and 1 represents the interval of chunks 0 and 1. The special\r
+numbers 0xFFFFFFFF (32-bit) or 0xFFFFFFFFFFFFFFFF (64-bit) stands for an\r
+empty interval, and 0x7FFF...FFF stands for "everything". \r
+\r
+\r
+.ti 0\r
+3.3.2. HAVE Message\r
+\r
+When a receiving peer has successfully checked the integrity of a chunk or\r
+interval of chunks it MUST send a HAVE message to all peers it wants to \r
+interact with. The latter allows the HAVE message to be used as a method of \r
+choking. The HAVE message MUST contain the bin number of the biggest\r
+complete interval of all chunks the receiver has received and checked so far\r
+that fully includes the interval of chunks just received. So the bin number\r
+MUST denote at least the interval received, but the receiver is supposed to \r
+aggregate and acknowledge bigger bins, when possible.\r
+\r
+As a result, every single chunk is acknowledged a logarithmic number of times.\r
+That provides some necessary redundancy of acknowledgments and sufficiently \r
+compensates for unreliable transport protocols.\r
+\r
+To record which chunks a peer has in the state that an implementation keeps\r
+for each peer, an implementation MAY use the "binmap" data structure, which\r
+is a hybrid of a bitmap and a binary tree, discussed in detail in [BINMAP].\r
+\r
+\r
+.ti 0\r
+3.4. ACK\r
+\r
+When swift is run over an unreliable transport protocol, an implementation MAY\r
+choose to use ACK messages to acknowledge received data. When a receiving \r
+peer has successfully checked the integrity of a chunk or interval of chunks\r
+C it MUST send a ACK message containing the bin number of its biggest, complete,\r
+interval covering C to the sending peer (see HAVE). To facilitate delay-based\r
+congestion control, an ACK message contains a timestamp.\r
+\r
+\r
+.ti 0\r
+3.5. DATA and HASH\r
+\r
+The DATA message is used to transfer chunks of content. The associated HASH message \r
+carries cryptographic hashes that are necessary for a receiver to check the\r
+the integrity of the chunk. Swift's content integrity protection is based on a \r
+Merkle hash tree and works as follows.\r
+\r
+.ti 0\r
+3.5.1. Merkle Hash Tree\r
+\r
+Swift uses a method of naming content based on self-certification. In particular, \r
+content in swift is identified by a single cryptographic hash that is the \r
+root hash in a Merkle hash tree calculated recursively from the content [ABMRKL]. \r
+This self-certifying hash tree allows every peer to directly detect when a malicious \r
+peer tries to distribute fake content. It also ensures only a small the amount \r
+of information is needed to start a download (the root hash and some peer \r
+addresses). For live streaming public keys and dynamic trees are used, see below.\r
+\r
+The Merkle hash tree of a content asset that is divided into N chunks\r
+is constructed as follows. Note the construction does not assume chunks\r
+of content to be fixed size. Given a cryptographic hash function, more specifically \r
+a modification detection code (MDC) [HAC01], such as SHA1, the hashes of all the \r
+chunks of the content are calculated. Next, a binary tree of sufficient height \r
+is created. Sufficient \r
+height means that the lowest level in the tree has enough nodes to hold all \r
+chunk hashes in the set, as before, see HAVE message. The figure below shows \r
+the tree for a content asset consisting of 7 chunks. As before with the\r
+content addressing scheme, the leaves of the tree correspond to a chunk and \r
+in this case are assigned the hash of that chunk, starting at the left-most leaf. \r
+As the base of the tree may be wider than the number of chunks, any remaining \r
+leaves in the tree are assigned a empty hash value of all zeros. Finally, the hash \r
+values of the higher levels in the tree are calculated, by concatenating the hash \r
+values of the two children (again left to right) and computing the hash of that \r
+aggregate. This process ends in a hash value for the root node, which is called\r
+the "root hash". Note the root hash only depends on the content and any modification\r
+of the content will result in a different root hash. \r
+\r
+\r
+\r
+\r
+                        7 = root hash\r
+.br\r
+                       / \\\r
+.br\r
+                     /     \\\r
+.br\r
+                   /         \\\r
+.br\r
+                 /             \\\r
+.br\r
+               3*               11\r
+.br\r
+              / \\              / \\\r
+.br\r
+             /   \\            /   \\\r
+.br\r
+            /     \\          /     \\\r
+.br\r
+           1       5        9       13* = uncle hash\r
+.br\r
+          / \\     / \\      / \\      / \\\r
+.br\r
+         0   2   4   6    8   10* 12   14\r
+.br\r
+        \r
+         C0  C1  C2  C3   C4  C5  C6   E\r
+.br\r
+         =chunk index     ^^           = empty hash\r
+.br\r
+                           \r
+\r
+.ti 0\r
+3.5.2. Content Integrity Verification\r
+\r
+.fi\r
+Assuming a peer receives the root hash of the content it wants to download\r
+from a trusted source, it can can check the integrity of any chunk of that \r
+content it receives as follows. It first calculates the hash of the chunk \r
+it received, for example chunk C4 in the previous figure. Along with this \r
+chunk it MUST receive the hashes required to check the integrity of that \r
+chunk. In principle, these are the hash of the chunk's sibling (C5) and \r
+that of its "uncles". A chunk's uncles are the sibling Y of its parent X, \r
+and the uncle of that Y, recursively until the root is reached. For chunk C4 \r
+its uncles are bins 13 and 3, marked with * in the figure. Using this information \r
+the peer recalculates the root hash of the tree, and compares it to the \r
+root hash it received from the trusted source. If they match the chunk of \r
+content has been positively verified to be the requested part of the content. \r
+Otherwise, the sending peer either sent the wrong content or the wrong \r
+sibling or uncle hashes. For simplicity, the set of sibling and uncles \r
+hashes is collectively referred to as the "uncle hashes". \r
+\r
+In the case of live streaming the tree of chunks grows dynamically and\r
+content is identified with a public key instead of a root hash, as the root \r
+hash is undefined or, more precisely, transient, as long as new data is \r
+generated by the live source. Live streaming is described in more detail\r
+below, but content verification works the same for both live and predefined\r
+content.\r
+\r
+.ti 0\r
+3.5.3. The Atomic Datagram Principle\r
+\r
+As explained above, a datagram consists of a sequence of messages. Ideally,\r
+every datagram sent must be independent of other datagrams, so each \r
+datagram SHOULD be processed separately and a loss of one datagram MUST NOT\r
+disrupt the flow.  Thus, as a datagram carries zero or more messages,\r
+neither messages nor message interdependencies should span over multiple\r
+datagrams. \r
+\r
+This principle implies that as any chunk is verified using its uncle \r
+hashes the necessary hashes MUST be put into the same datagram as the \r
+chunk's data (Sec. 3.5.4).  As a general rule, if some additional data is \r
+still missing to process a message within a datagram, the message SHOULD be \r
+dropped. \r
+\r
+The hashes necessary to verify a chunk are in principle its sibling's hash\r
+and all its uncle hashes, but the set of hashes to sent can be optimized. \r
+Before sending a packet of data to the receiver, the sender inspects the \r
+receiver's previous acknowledgments (HAVE or ACK) to derive which hashes the\r
+receiver already has for sure. Suppose, the receiver had acknowledged bin 1\r
+(first two chunks of the file), then it must already have uncle hashes 5,\r
+11 and so on. That is because those hashes are necessary to check packets of\r
+bin 1 against the root hash. Then, hashes 3, 7 and so on must be also known\r
+as they are calculated in the process of checking the uncle hash chain.\r
+Hence, to send bin 12 (i.e. the 7th chunk of content), the sender needs to\r
+include just the hashes for bins 14 and 9, which let the data be checked \r
+against hash 11 which is already known to the receiver. \r
+\r
+The sender MAY optimistically skip hashes which were sent out in previous,\r
+still unacknowledged datagrams. It is an optimization tradeoff between\r
+redundant hash transmission and possibility of collateral data loss in the\r
+case some necessary hashes were lost in the network so some delivered data\r
+cannot be verified and thus has to be dropped. In either case, the receiver\r
+builds the Merkle tree on-demand, incrementally, starting from the root\r
+hash, and uses it for data validation.\r
+\r
+In short, the sender MUST put into the datagram the missing hashes necessary \r
+for the receiver to verify the chunk.\r
+\r
+.ti 0\r
+3.5.4. DATA and HASH Messages\r
+\r
+Concretely, a peer that wants to send a chunk of content creates a datagram \r
+that MUST consist of one or more HASH messages and a DATA message. The datagram \r
+MUST contain a HASH message for each hash the receiver misses for integrity \r
+checking. A HASH message MUST contain the bin number and hash data for each \r
+of those hashes. The DATA message MUST contain the bin number of the chunk \r
+and chunk itself. A peer MAY send the required messages for multiple chunks \r
+in the same datagram. \r
+\r
+\r
+.ti 0\r
+3.6. HINT\r
+\r
+While bulk download protocols normally do explicit requests for certain ranges \r
+of data (i.e., use a pull model, for example, BitTorrent [BITTORRENT]), live \r
+streaming protocols quite often use a request-less push model to save round \r
+trips. Swift supports both models of operation. \r
+\r
+A peer MUST send a HINT message containing the bin of the chunk interval it \r
+wants to download. A peer receiving a HINT message MAY send out requested \r
+pieces. When it receives multiple HINTs (either in one datagram or in multiple), \r
+the peer SHOULD process the HINTs sequentially. When live streaming, \r
+it also may send some other chunks in case it runs out of requests or\r
+for some other reason. In that case the only purpose of HINT messages is \r
+to coordinate peers and to avoid unnecessary data retransmission, hence \r
+the name. \r
+\r
+\r
+\r
+.ti 0\r
+3.7. Peer Address Exchange and NAT Hole Punching\r
+\r
+Peer address exchange messages (or PEX messages for short) are common for \r
+many peer-to-peer protocols. By exchanging peer addresses in gossip fashion, \r
+peers relieve central coordinating entities (the trackers) from unnecessary \r
+work. swift optionally features two types of PEX messages: PEX_REQ and PEX_ADD.\r
+A peer that wants to retrieve some peer addresses MUST send a PEX_REQ message.\r
+The receiving peer MAY respond with a PEX_ADD message containing the addresses\r
+of several peers. The addresses MUST be of peers it has recently exchanged\r
+messages with to guarantee liveliness. \r
+\r
+.fi\r
+To unify peer exchange and NAT hole punching functionality, the\r
+sending pattern of PEX messages is restricted. As the swift handshake\r
+is able to do simple NAT hole punching [SNP] transparently, PEX\r
+messages must be emitted in the way to facilitate that. Namely,\r
+once peer A introduces peer B to peer C by sending a PEX_ADD message to\r
+C, it SHOULD also send a message to B introducing C. The messages\r
+SHOULD be within 2 seconds from each other, but MAY not be,\r
+simultaneous, instead leaving a gap of twice the "typical" RTT, i.e.\r
+\%300-600ms. The peers are supposed to initiate handshakes to each\r
+other thus forming a simple NAT hole punching pattern where the\r
+introducing peer effectively acts as a STUN server [RFC5389]. Still, peers\r
+MAY ignore PEX messages if uninterested in obtaining new peers or\r
+because of security considerations (rate limiting) or any other\r
+reason.\r
+\r
+The PEX messages can be used to construct a dedicated tracker peer.\r
+\r
+\r
+.ti 0\r
+3.8. KEEPALIVE\r
+\r
+A peer MUST send a datagram containing a KEEPALIVE message periodically\r
+to each peer it wants to interact with in the future but has no\r
+other messages to send them at present.\r
+\r
+\r
+.ti 0\r
+3.9. VERSION\r
+.fi\r
+Peers MUST convey which version of the swift protocol they support using a \r
+VERSION message. This message MUST be included in the initial (handshake) \r
+datagrams and MUST indicate which version of the swift protocol the sending \r
+peer supports. \r
+\r
+\r
+.ti 0\r
+3.10. Conveying Peer Capabilities\r
+.fi\r
+Peers may support just a subset of the swift messages. For example, peers \r
+running over TCP may not accept ACK messages, or peers used with a centralized \r
+tracking infrastructure may not accept PEX messages. For these reasons, peers \r
+SHOULD signal which subset of the swift messages they support by means of \r
+the MSGTYPE_RCVD message. This message SHOULD be included in the initial \r
+(handshake) datagrams and MUST indicate which swift protocol messages \r
+the sending peer supports. \r
+\r
+\r
+.ti 0\r
+3.11. Directory Lists\r
+\r
+Directory list files MUST start with magic bytes ".\\n..\\n". The rest of\r
+the file is a newline-separated list of hashes and file names for the\r
+content of the directory. An example:\r
+\r
+.nf\r
+\&.\r
+\&..\r
+1234567890ABCDEF1234567890ABCDEF12345678  readme.txt\r
+01234567890ABCDEF1234567890ABCDEF1234567  big_file.dat\r
+\r
+\r
+\r
+.ti 0\r
+4. Automatic Detection of Content Size\r
+\r
+.fi\r
+In swift, the root hash of a static content asset, such as a video file,\r
+along with some peer addresses is sufficient to start a download.\r
+In addition, swift can reliably and automatically derive the size\r
+of such content from information received from the network when fixed\r
+sized chunks are used. As a result, it is not necessary to include \r
+the size of the content asset as the metadata of the content,\r
+in addition to the root hash. Implementations of swift MAY use \r
+this automatic detection feature.  \r
+\r
+.ti 0\r
+4.1. Peak Hashes\r
+\r
+The ability for a newcomer peer to detect the size of the content \r
+depends heavily on the concept of peak hashes. Peak hashes,\r
+in general, enable two cornerstone features of swift: reliable file \r
+size detection and download/live streaming unification (see Sec. 5). \r
+The concept of peak hashes depends on the concepts of filled and\r
+incomplete bins. Recall that when constructing the binary trees\r
+for content verification and addressing the base of the tree may\r
+have more leaves than the number of chunks in the content. In the\r
+Merkle hash tree these leaves were assigned empty all-zero \r
+hashes to be able to calculate the higher level hashes. A filled\r
+bin is now defined as a bin number that addresses an interval\r
+of leaves that consists only of hashes of content chunks, not\r
+empty hashes. Reversely, an incomplete (not filled) bin \r
+addresses an interval that contains also empty hashes,\r
+typically an interval that extends past the end of the file.\r
+In the following figure bins 7, 11, 13 and 14 are incomplete\r
+the rest is filled.\r
+\r
+Formally, a peak hash is a hash in the Merkle tree defined \r
+over a filled bin, whose sibling is defined over an incomplete bin. \r
+Practically, suppose a file is 7162 bytes long and a chunk \r
+is 1 kilobyte. That file fits into 7 chunks, the tail chunk being \r
+1018 bytes long. The Merkle tree for that file looks as follows. \r
+Following the definition the peak hashes of this file are in \r
+bins 3, 9 and 12, denoted with a *. E denotes an empty \r
+hash.\r
+\r
+                        7\r
+.br\r
+                       / \\\r
+.br\r
+                     /     \\\r
+.br\r
+                   /         \\\r
+.br\r
+                 /             \\\r
+.br\r
+               3*               11\r
+.br\r
+              / \\              / \\\r
+.br\r
+             /   \\            /   \\\r
+.br\r
+            /     \\          /     \\\r
+.br\r
+           1       5        9*      13 \r
+.br\r
+          / \\     / \\      / \\      / \\\r
+.br\r
+         0   2   4   6    8   10  12*  14\r
+.br\r
+\r
+         C0  C1  C2  C3   C4  C5  C6   E\r
+.br\r
+                                  = 1018 bytes\r
+.br\r
+\r
+Peak hashes can be explained by the binary representation of the \r
+number of chunks the file occupies. The binary representation for \r
+7 is 111. Every "1" in binary representation of the file's packet \r
+length corresponds to a peak hash. For this particular file there\r
+are indeed three peaks, bin numbers 3, 9, 12. The number of peak\r
+hashes for a file is therefore also at most logarithmic with its \r
+size.\r
+\r
+.fi\r
+A peer knowing which bins contain the peak hashes for the file \r
+can therefore calculate the number of chunks it consists of, and \r
+thus get an estimate of the file size (given all chunks but the last \r
+are fixed size). Which bins are the peaks can  be securely communicated \r
+from one (untrusted) peer A to another B by letting A send the \r
+peak hashes and their bin numbers to B. It can be shown that \r
+the root hash that B obtained from a trusted source is sufficient \r
+to verify that these are indeed the right peak hashes, as follows.  \r
+\r
+Lemma: Peak hashes can be checked against the root hash.\r
+\r
+Proof: (a) Any peak hash is always the left sibling. Otherwise, be\r
+it the right sibling, its left neighbor/sibling must also be\r
+defined over a filled bin, because of the way chunks are laid\r
+out in the leaves, contradiction. (b) For the rightmost \r
+peak hash, its right sibling is zero. (c) For any peak hash, \r
+its right sibling might be calculated using peak hashes to the \r
+left and zeros for empty bins. (d) Once the right sibling of \r
+the leftmost peak hash is calculated, its parent might be \r
+calculated. (e) Once that parent is calculated, we might \r
+trivially get to the root hash by concatenating the hash with \r
+zeros and hashing it repeatedly.\r
+\r
+.fi\r
+Informally, the Lemma might be expressed as follows: peak hashes cover all\r
+data, so the remaining hashes are either trivial (zeros) or might be\r
+calculated from peak hashes and zero hashes.\r
+\r
+Finally, once peer B has obtained the number of chunks in the content it\r
+can determine the exact file size as follows. Given that all chunks\r
+except the last are fixed size B just needs to know the size of the last\r
+chunk. Knowing the number of chunks B can calculate the bin number of the \r
+last chunk and download it. As always B verifies the integrity of this \r
+chunk against the trusted root hash. As there is only one chunk of data \r
+that leads to a successful verification the size of this chunk must \r
+be correct. B can then determine the exact file size as \r
+\r
+    (number of chunks -1) * fixed chunk size + size of last chunk\r
+\r
+\r
+.ti 0\r
+4.2. Procedure\r
+\r
+A swift implementation that wants to use automatic size detection MUST\r
+operate as follows. When a peer B sends a DATA message for the first time\r
+to a peer A, B MUST include all the peak hashes for the content in the\r
+same datagram, unless A has already signalled earlier in the exchange \r
+that it knows the peak hashes by having acknowledged any bin, even the empty \r
+one. The receiver A MUST check the peak hashes against the root hash\r
+to determine the approximate content size. To obtain the definite content size\r
+peer A MUST download the last chunk of the content from any peer that offers it.\r
+\r
+\r
+\r
+\r
+\r
+.ti 0\r
+5. Live streaming\r
+\r
+.fi\r
+In the case of live streaming a transfer is bootstrapped with a public key\r
+instead of a root hash, as the root hash is undefined or, more precisely,\r
+transient, as long as new data is being generated by the live source. \r
+Live/download unification is achieved by sending signed peak hashes on-demand, \r
+ahead of the actual data. As before, the sender might use acknowledgements \r
+to derive which content range the receiver has peak hashes for and to prepend \r
+the data hashes with the necessary (signed) peak hashes. \r
+Except for the fact that the set of peak hashes changes with time, \r
+other parts of the algorithm work as described in Sec. 3. \r
+\r
+As with static content assets in the previous section, in live streaming \r
+content length is not known on advance, but derived \%on-the-go from the peak \r
+hashes. Suppose, our 7 KB stream extended to another kilobyte. Thus, now hash 7 \r
+becomes the only peak hash, eating hashes 3, 9 and 12. So, the source sends \r
+out a SIGNED_HASH message to announce the fact.\r
+\r
+The number of cryptographic operations will be limited. For example,\r
+consider a 25 frame/second video transmitted over UDP. When each frame is \r
+transmitted in its own chunk, only 25 signature verification operations \r
+per second are required at the receiver for bitrates up to ~12.8 \r
+megabit/second. For higher bitrates multiple UDP packets per frame \r
+are needed and the number of verifications doubles.\r
+\r
+\r
+\r
+\r
+\r
+.ti 0\r
+6. Transport Protocols and Encapsulation\r
+\r
+.ti 0\r
+6.1. UDP\r
+\r
+.ti 0\r
+6.1.1. Chunk Size\r
+\r
+Currently, swift-over-UDP is the preferred deployment option. Effectively, UDP\r
+allows the use of IP with minimal overhead and it also allows userspace\r
+implementations. The default is to use chunks of 1 kilobyte such that a\r
+datagram fits in an Ethernet-sized IP packet. The bin numbering\r
+allows to use swift over Jumbo frames/datagrams. Both DATA and\r
+HAVE/ACK messages may use e.g. 8 kilobyte packets instead of the standard 1 \r
+KiB. The hashing scheme stays the same. Using swift with 512 or 256-byte \r
+packets is theoretically possible with 64-bit byte-precise bin numbers, but IP\r
+fragmentation might be a better method to achieve the same result.\r
+\r
+\r
+.ti 0\r
+6.1.2. Datagrams and Messages\r
+\r
+When using UDP, the abstract datagram described above corresponds directly\r
+to a UDP datagram. Each message within a datagram has a fixed length, which\r
+depends on the type of the message. The first byte of a message denotes its type.\r
+The currently defined types are:\r
+\r
+        HANDSHAKE = 0x00\r
+.br        \r
+        DATA = 0x01\r
+.br\r
+        ACK = 0x02\r
+.br        \r
+        HAVE = 0x03\r
+.br        \r
+        HASH = 0x04\r
+.br        \r
+        PEX_ADD = 0x05\r
+.br        \r
+        PEX_REQ = 0x06\r
+.br        \r
+        SIGNED_HASH = 0x07\r
+.br        \r
+        HINT = 0x08\r
+.br        \r
+        MSGTYPE_RCVD = 0x09\r
+.br        \r
+        VERSION = 0x10\r
+.br        \r
+\r
+\r
+Furthermore, integers are serialized in the network \%(big-endian) byte order. \r
+So consider the example of an ACK message (Sec 3.4). It has message type of 0x02\r
+and a payload of a bin number, a four-byte integer (say, 1); hence, its on the wire\r
+representation for UDP can be written in hex as: "02 00000001". This \%hex-like two \r
+character-per-byte notation is used to represent message formats in the rest of \r
+this section.\r
+\r
+.ti 0\r
+6.1.3. Channels  \r
+\r
+As it is increasingly complex for peers to enable UDP communication\r
+between each other due to NATs and firewalls, swift-over-UDP uses\r
+a multiplexing scheme, called "channels", to allow multiple swarms to use the \r
+same UDP port. Channels loosely correspond to TCP connections and each channel\r
+belongs to a single swarm. When channels are used, each datagram starts with \r
+four bytes corresponding to the receiving channel number. \r
+\r
\r
+.ti 0\r
+6.1.4. HANDSHAKE and VERSION\r
+\r
+A channel is established with a handshake. To start a handshake, the initiating \r
+peer needs to know:\r
+\r
+.nf\r
+(1) the IP address of a peer\r
+(2) peer's UDP port and\r
+(3) the root hash of the content (see Sec. 3.5.1).\r
+.fi\r
+\r
+To do the handshake the initiating peer sends a datagram that MUST start \r
+with an all 0-zeros channel number followed by a VERSION message, then a \r
+HASH message whose payload is the root hash, and a HANDSHAKE message, whose \r
+only payload is a locally unused channel number. \r
+\r
+On the wire the datagram will look something like this:\r
+.nf\r
+   00000000 10 01  \r
+   04 7FFFFFFF 1234123412341234123412341234123412341234\r
+   00 00000011\r
+.fi\r
+(to unknown channel, handshake from channel 0x11 speaking protocol version 0x01, \r
+initiating a transfer of a file with a root hash 123...1234)\r
+\r
+The receiving peer MUST respond with a datagram that starts with the\r
+channel number from the sender's HANDSHAKE message, followed by a \r
+VERSION message, then a HANDSHAKE message, whose only payload is a locally \r
+unused channel number, followed by any other messages it wants to send. \r
+\r
+Peer's response datagram on the wire:\r
+.nf\r
+   00000011 10 01  \r
+   00 00000022  03 00000003\r
+.fi\r
+(peer to the initiator: use channel number 0x22 for this transfer and \r
+proto version 0x01; I also have first 4 chunks of the file, see Sec. 4.3)\r
+\r
+.fi\r
+At this point, the initiator knows that the peer really responds; for that\r
+purpose channel ids MUST be random enough to prevent easy guessing. So, the\r
+third datagram of a handshake MAY already contain some heavy payload. To\r
+minimize the number of initialization roundtrips, the first two datagrams\r
+MAY also contain some minor payload, e.g. a couple of HAVE messages roughly\r
+indicating the current progress of a peer or a HINT (see Sec. 3.6).\r
+When receiving the third datagram, both peers have the proof they really talk \r
+to each other; three-way handshake is complete.\r
+\r
+A peer MAY explicit close a channel by sending a HANDSHAKE message that MUST\r
+contain an all 0-zeros channel number. \r
+\r
+On the wire:\r
+.nf \r
+   00 00000000\r
+\r
+\r
+.ti 0\r
+6.1.5. HAVE\r
+\r
+A HAVE message (type 0x03) states that the sending peer has the \r
+complete specified bin and successfully checked its integrity:\r
+.nf\r
+   03 00000003\r
+(got/checked first four kilobytes of a file/stream)\r
+\r
+\r
+\r
+.ti 0\r
+6.1.6. ACK\r
+\r
+An ACK message (type 0x02) acknowledges data that was received from\r
+its addressee; to facilitate delay-based congestion control, an\r
+ACK message contains a timestamp, in particular, a 64-bit microsecond\r
+time. \r
+.nf\r
+   02 00000002 12345678\r
+(got the second kilobyte of the file from you; my microsecond\r
+timer was showing 0x12345678 at that moment)\r
+\r
+\r
+.ti 0\r
+6.1.7. HASH\r
+\r
+A HASH message (type 0x04) consists of a four-byte bin number and\r
+the cryptographic hash (e.g. a 20-byte SHA1 hash)\r
+.nf\r
+   04 7FFFFFFF 1234123412341234123412341234123412341234\r
+\r
+\r
+.ti 0\r
+6.1.8. DATA\r
+\r
+.fi\r
+A DATA message (type 0x01) consists of a four-byte bin number and the \r
+actual chunk. In case a datagram contains a DATA message, a sender \r
+MUST always put the data message in the tail of the datagram. For \r
+example:\r
+.nf\r
+   01 00000000 48656c6c6f20776f726c6421\r
+(This message accommodates an entire file: "Hello world!")\r
+\r
+\r
+.ti 0\r
+6.1.9. KEEPALIVE\r
+\r
+Keepalives do not have a message type on UDP. They are just simple \r
+datagrams consisting of a 4-byte channel id only.\r
+\r
+On the wire:\r
+.nf\r
+   00000022\r
+\r
+.ti 0\r
+6.1.10. Flow and Congestion Control\r
+\r
+.fi\r
+Explicit flow control is not necessary in swift-over-UDP. In the case of\r
+video-on-demand the receiver will request data explicitly from peers and is \r
+therefore in control of how much data is coming towards it. In the case of\r
+live streaming, where a push-model may be used, the amount of data incoming\r
+is limited to the bitrate, which the receiver must be able to process otherwise\r
+it cannot play the stream. Should, for any reason, the receiver get saturated \r
+with data that situation is perfectly detected by the congestion control.\r
+Swift-over-UDP can support different congestion control algorithms, in particular,\r
+it supports the new IETF Low Extra Delay Background Transport (LEDBAT) congestion \r
+control algorithm that ensures that peer-to-peer traffic yields to regular \r
+best-effort traffic [LEDBAT]. \r
+\r
+\r
+.ti 0\r
+6.2. TCP\r
+\r
+.fi\r
+When run over TCP, swift becomes functionally equivalent to BitTorrent.\r
+Namely, most swift messages have corresponding BitTorrent messages and vice\r
+versa, except for BitTorrent's explicit interest declarations and\r
+choking/unchoking, which serve the classic implementation of the tit-for-tat\r
+algorithm [TIT4TAT]. However, TCP is not well suited for multiparty communication,\r
+as argued in Sec. 9.\r
+\r
+\r
+.ti 0\r
+6.3. RTP Profile for PPSP\r
+\r
+.fi\r
+In this section we sketch how swift can be integrated into RTP [RFC3550] to\r
+form the Peer-to-Peer Streaming Protocol (PPSP) [I-D.ietf-ppsp-reqs] running\r
+over UDP. The PPSP charter requires existing media transfer protocols be\r
+used [PPSPCHART]. Hence, the general idea is to define swift as a profile\r
+of RTP, in the same way as the Secure Real-time Transport Protocol (SRTP) \r
+[RFC3711]. SRTP, and therefore swift is considered ``a "bump in the stack"\r
+implementation which resides between the RTP application and the transport\r
+layer. [swift] intercepts RTP packets and then forwards an equivalent\r
+[swift] packet on the sending side, and intercepts [swift] packets and passes \r
+an equivalent RTP packet up the stack on the receiving side.'' [RFC3711].\r
+\r
+In particular, to encode a swift datagram in an RTP packet all the \r
+non-DATA messages of swift such as HINT and HAVE are postfixed to the RTP \r
+packet using the UDP encoding and the content of DATA messages is sent \r
+in the payload field. Implementations MAY omit the RTP header for packets\r
+without payload. This construction allows the streaming application to use \r
+of all RTP's current features, and with a modification to the Merkle tree \r
+hashing scheme (see below) meets swift's atomic datagram principle. The latter \r
+means that a receiving peer can autonomously verify the RTP packet as being \r
+correct content, thus preventing the spread of corrupt data (see \r
+requirement PPSP.SEC-REQ-4). \r
+\r
+The use of ACK messages for reliability is left as a choice of the\r
+application using PPSP.\r
+\r
+\r
+.ti 0\r
+6.3.1. Design\r
+\r
+6.3.1.1. Joining a Swarm\r
+\r
+To commence a PPSP download a peer A must have the swarm ID of the stream\r
+and a list of one or more tracker contact points (e.g. host+port). The list\r
+of trackers is optional in the presence of a decentralized tracking\r
+mechanism. The swarm ID consists of the swift root hash of the content,\r
+which is divided into chunks (see Discussion).\r
+\r
+Peer A now registers with the PPSP tracker following the tracker protocol\r
+[I-D.ietf.ppsp-reqs] and receives the IP address and RTP port of peers\r
+already in the swarm, say B, C, and D. Peer A now sends an RTP packet\r
+containing a HANDSHAKE without channel information to B, C, and D. This\r
+serves as an end-to-end check that the peers are actually in the correct\r
+swarm. Optionally A could include a HINT message in some RTP packets if it\r
+wants to start receiving content immediately. B and C respond with a\r
+HANDSHAKE and HAVE messages.  D sends just a HANDSHAKE and omits HAVE\r
+messages as a way of choking A.\r
+\r
+\r
+6.3.1.2. Exchanging Chunks\r
+\r
+In response to B and C, A sends new RTP packets to B and C with HINTs for\r
+disjunct sets of chunks. B and C respond with the requested chunks in the\r
+payload and HAVE messages, updating their chunk availability. Upon receipt, \r
+A sends HAVE for the chunks received and new HINT messages to B and C. When \r
+e.g. C finds that A obtained a chunk (from B) that C did not yet have, C's \r
+response includes a HINT for that chunk.\r
+\r
+D does not send HAVE messages, instead if D decides to unchoke peer A, it\r
+sends an RTP packet with HAVE messages to inform A of its current\r
+availability. If B or C decide to choke A they stop sending HAVE and DATA\r
+messages and A should then rerequest from other peers. They may continue to\r
+send HINT messages, or exponentially slowing KEEPALIVE messages such that A\r
+keeps sending them HAVE messages.\r
+\r
+Once A has received all content (video-on-demand use case) it stops\r
+sending messages to all other peers that have all content (a.k.a. seeders).\r
+\r
+\r
+6.3.1.3. Leaving a Swarm\r
+\r
+Peers can implicitly leave a swarm by stopping to respond to messages.\r
+Sending peers should remove these peers from the current peer list. This\r
+mechanism works for both graceful and ungraceful leaves (i.e., peer crashes\r
+or disconnects). When leaving gracefully, a peer should deregister from the\r
+tracker following the PPSP tracker protocol.\r
+\r
+More explicit graceful leaves could be implemented using RTCP. In\r
+particular, a peer could send a RTCP BYE on the RTCP port that is derivable\r
+from a peer's RTP port for all peers in its current peer list. However, to\r
+prevent malicious peers from sending BYEs a form of peer authentication is\r
+required (e.g. using public keys as peer IDs [PERMIDS].)\r
+\r
+\r
+6.3.1.4. Discussion\r
+\r
+Using swift as an RTP profile requires a change to the content\r
+integrity protection scheme (see Sec. 3.5). The fields in the RTP \r
+header, such as the timestamp and PT fields, must be protected by the Merkle \r
+tree hashing scheme to prevent malicious alterations. Therefore, the Merkle\r
+tree is no longer constructed from pure content chunks, but from the complete \r
+RTP packet for a chunk as it would be transmitted (minus the non-DATA swift messages).\r
+In other words, the hash of the leaves in the tree is the hash over the\r
+Authenticated Portion of the RTP packet as defined by SRTP, illustrated\r
+in the following figure (extended from [RFC3711]). There is no need for the \r
+RTP packets to be fixed size, as the hashing scheme can deal with \r
+variable-sized leaves. \r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+.in 0\r
+        0                   1                   2                   3\r
+.br\r
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+\r
+.br\r
+     |V=2|P|X|  CC   |M|     PT      |       sequence number         | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+     |                           timestamp                           | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+     |           synchronization source (SSRC) identifier            | |\r
+.br\r
+     +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ |\r
+.br\r
+     |            contributing source (CSRC) identifiers             | |\r
+.br\r
+     |                               ....                            | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+     |                   RTP extension (OPTIONAL)                    | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+     |                          payload  ...                         | |\r
+.br\r
+     |                               +-------------------------------+ |\r
+.br\r
+     |                               | RTP padding   | RTP pad count | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+\r
+.br\r
+     ~                     swift non-DATA messages (REQUIRED)        ~ |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+     |                 length of swift messages (REQUIRED)           | |\r
+.br\r
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+.br\r
+                                                                       |\r
+.br\r
+                                              Authenticated Portion ---+\r
+.br\r
+\r
+           Figure: The format of an RTP-Swift packet.                                         \r
+.in 3     \r
+                                                                  \r
+As a downside, with variable-sized payloads the automatic content size \r
+detection of Section 4 no longer works, so content length MUST be explicit in the \r
+metadata. In addition, storage on disk is more complex with out-of-order, \r
+variable-sized packets. On the upside, carrying RTP over swift allow \r
+decryption-less caching.\r
\r
+As with UDP, another matter is how much data is carried inside each packet.\r
+An important swift-specific factor here is the resulting number of hash \r
+calculations per second needed to verify chunks. Experiments should be \r
+conducted to ensure they are not excessive for, e.g., mobile hardware.\r
+\r
+At present, Peer IDs are not required in this design.\r
+\r
+\r
+.ti 0\r
+6.3.2. PPSP Requirements\r
+\r
+6.3.2.1. Basic Requirements\r
+\r
+- PPSP.REQ-1: The swift PEX message can also be used as the basis for a tracker protocol, to be discussed elsewhere.\r
+\r
+- PPSP.REQ-2: This draft preserves the properties of RTP.\r
+\r
+- PPSP.REQ-3: This draft does not place requirements on peer IDs, IP+port is sufficient. \r
+\r
+- PPSP.REQ-4: The content is identified by its root hash (video-on-demand) or a public key (live streaming).\r
+\r
+- PPSP.REQ-5: The content is partitioned by the streaming application.\r
+\r
+- PPSP.REQ-6: Each chunk is identified by a bin number (and its cryptographic hash.)\r
+\r
+- PPSP.REQ-7: The protocol is carried over UDP because RTP is.\r
+\r
+- PPSP.REQ-8: The protocol has been designed to allow meaningful data transfer between\r
+peers as soon as possible and to avoid unnecessary round-trips. It supports small and\r
+variable chunk sizes, and its content integrity protection enables wide scale caching.\r
+\r
+\r
+6.3.2.2. Peer Protocol Requirements\r
+\r
+- PPSP.PP.REQ-1: A GET_HAVE would have to be added to request which\r
+chunks are available from a peer, if the proposed push-based HAVE\r
+mechanism is not sufficient. \r
+\r
+- PPSP.PP.REQ-2: A set of HAVE messages satisfies this. \r
+\r
+- PPSP.PP.REQ-3: The PEX_REQ message satisfies this. Care should be taken with \r
+peer address exchange in general, as the use of such hearsay is a risk for the \r
+protocol as it may be exploited by malicious peers (as a DDoS attack mechanism).\r
+A secure tracking / peer sampling protocol like [PUPPETCAST] may be needed \r
+to make peer-address exchange safe.\r
+\r
+- PPSP.PP.REQ-4: HAVE messages convey current availability via a push model.\r
+\r
+- PPSP.PP.REQ-5: Bin numbering enables a compact representation of chunk availability.\r
+\r
+- PPSP.PP.REQ-6: A new PPSP specific Peer Report message would have to be\r
+added to RTCP. \r
+\r
+- PPSP.PP.REQ-7: Transmission and chunk requests are integrated in this\r
+protocol.\r
+\r
+\r
+6.3.2.3. Security Requirements\r
+\r
+- PPSP.SEC.REQ-1: An access control mechanism like Closed Swarms\r
+[CLOSED] would have to be added. \r
+\r
+- PPSP.SEC.REQ-2: As RTP is carried verbatim over swift, RTP encryption\r
+can be used. Note that just encrypting the RTP part will allow for \r
+caching servers that are part of the swarm but do not need access to the \r
+decryption keys. They just need access to the swift HASHES in the postfix to \r
+verify the packet's integrity.\r
+\r
+- PPSP.SEC.REQ-3: RTP encryption or IPsec [RFC4303] can be used, if the\r
+swift messages must also be encrypted. \r
+\r
+- PPSP.SEC.REQ-4: The Merkle tree hashing scheme prevents the indirect\r
+spread of corrupt content, as peers will only forward chunks to others if\r
+their integrity check out. Another protection mechanism is to not depend on\r
+hearsay (i.e., do not forward other peers' availability information), or to\r
+only use it when the information spread is self-certified by its subjects.\r
+\r
+Other attacks, such as a malicious peer claiming it has content but not\r
+replying, are still possible. Or wasting CPU and bandwidth at a receiving\r
+peer by sending packets where the DATA doesn't match the HASHes.\r
+\r
+\r
+- PPSP.SEC.REQ-5: The Merkle tree hashing scheme allows a receiving\r
+peer to detect a malicious or faulty sender, which it can subsequently\r
+ignore. Spreading this knowledge to other peers such that they know\r
+about this bad behavior is hearsay. \r
+\r
+\r
+- PPSP.SEC.REQ-6: A risk in peer-to-peer streaming systems is that malicious\r
+peers launch an Eclipse [ECLIPSE] attack on the initial injectors of the\r
+content (in particular in live streaming). The attack tries to let the\r
+injector upload to just malicious peers which then do not forward the\r
+content to others, thus stopping the distribution. An Eclipse attack could\r
+also be launched on an individual peer. Letting these injectors only use\r
+trusted trackers that provide true random samples of the population or using\r
+a secure peer sampling service [PUPPETCAST] can help negate such an attack. \r
+\r
+\r
+- PPSP.SEC.REQ-7: swift supports decentralized tracking via PEX or\r
+additional mechanisms such as DHTs [SECDHTS], but self-certification of\r
+addresses is needed. Self-certification means For example, that each peer \r
+has a public/private key pair [PERMIDS] and creates self-certified address \r
+changes that include the swarm ID and a timestamp, which are then exchanged\r
+among peers or stored in DHTs. See also discussion of PPSP.PP.REQ-3 \r
+above. Content distribution can continue as long as there are peers that \r
+have it available.\r
+\r
+- PPSP.SEC.REQ-8: The verification of data via hashes obtained from a\r
+trusted source is well-established in the BitTorrent protocol [BITTORRENT].\r
+The proposed Merkle tree scheme is a secure extension of this idea.\r
+Self-certification and not using hearsay are other lessons learned from\r
+existing distributed systems.\r
+\r
+- PPSP.SEC.REQ-9: Swift has built-in content integrity protection via\r
+self-certified naming of content, see SEC.REQ-5 and Sec. 3.5.1.\r
+\r
+\r
+.ti 0\r
+6.4. HTTP (as PPSP)\r
+\r
+In this section we sketch how swift can be carried over HTTP [RFC2616] to\r
+form the PPSP running over TCP. The general idea is to encode a swift\r
+datagram in HTTP GET and PUT requests and their replies by transmitting all\r
+the non-DATA messages such as HINTs and HAVEs as headers and send DATA\r
+messages in the body. This idea follows the atomic datagram principle for\r
+each request and reply. So a receiving peer can autonomously verify the\r
+message as carrying correct data, thus preventing the spread of corrupt data\r
+(see requirement PPSP.SEC-REQ-4). \r
+\r
+A problem with HTTP is that it is a client/server protocol. To overcome this\r
+problem, a peer A uses a PUT request instead of a GET request if the peer B\r
+has indicated in a reply that it wants to retrieve a chunk from A. In cases\r
+where peer A is no longer interested in receiving requests from B (described\r
+below) B may need to establish a new HTTP connection to A to quickly\r
+download a chunk, instead of waiting for a convenient time when A sends\r
+another request. As an alternative design, two HTTP connections could be\r
+used always., but this is inefficient.\r
+\r
+.ti 0\r
+6.4.1. Design\r
+\r
+6.4.1.1. Joining a Swarm\r
+\r
+To commence a PPSP download a peer A must have the swarm ID of the stream\r
+and a list of one or more tracker contact points, as above. The swarm ID as\r
+earlier also consists of the swift root hash of the content, divided in\r
+chunks by the streaming application (e.g. fixed-size chunks of 1 kilobyte\r
+for video-on-demand). \r
+\r
+Peer A now registers with the PPSP tracker following the tracker protocol\r
+[I-D.ietf-ppsp-reqs] and receives the IP address and HTTP port of peers\r
+already in the swarm, say B, C, and D. Peer A now establishes persistent\r
+HTTP connections with B, C, D and sends GET requests with the Request-URI\r
+set to /<encoded roothash>. Optionally A could include a HINT message in\r
+some requests if it wants to start receiving content immediately. A HINT is\r
+encoded as a Range header with a new "bins" unit [RFC2616,$14.35].\r
+\r
+B and C respond with a 200 OK reply with header-encoded HAVE messages.  A\r
+HAVE message is encoded as an extended Accept-Ranges: header [RFC2616,$14.5] \r
+with the new bins unit and the possibility of listing the set of accepted bins. \r
+If no HINT/Range header was present in the request, the body of the reply is\r
+empty. D sends just a 200 OK reply and omits the HAVE/Accept-Ranges header\r
+as a way of choking A.\r
+\r
+6.4.1.2. Exchanging Chunks\r
+\r
+In response to B and C, A sends GET requests with Range headers, requesting\r
+disjunct sets of chunks. B and C respond with 206 Partial Content replies\r
+with the requested chunks in the body and Accept-Ranges headers, updating\r
+their chunk availability. The HASHES for the chunks are encoded in a new\r
+Content-Merkle header and the Content-Range is set to identify the chunk \r
+[RFC2616,$14.16]. A new "multipart-bin ranges" equivalent to the \r
+"multipart-bytes ranges" media type may be used to transmit multiple chunks \r
+in one reply.\r
+\r
+Upon receipt, A sends a new GET request with a HAVE/Accept-Ranges header for\r
+the chunks received and new HINT/Range headers to B and C. Now when e.g. C\r
+finds that A obtained a chunk (from B) that C did not yet have, C's response\r
+includes a HINT/Range for that chunk. In this case, A's next request to C is\r
+not a GET request, but a PUT request with the requested chunk sent in the\r
+body.\r
+\r
+Again, working around the fact that HTTP is a client/server protocol, peer A\r
+periodically sends HEAD requests to peer D (which was virtually choking A)\r
+that serve as keepalives and may contain HAVE/Accept-Ranges headers. If D\r
+decides to unchoke peer A, it includes an Accept-Ranges header in the "200 OK"\r
+reply to inform A of its current chunk availability. \r
+\r
+If B or C decide to choke A they start responding with 204 No Content\r
+replies without HAVE/Accept-Ranges headers and A should then re-request from\r
+other peers. However, if their replies contain HINT/Range headers A should\r
+keep on sending PUT requests with the desired data (another client/server\r
+workaround). If not, A should slowly send HEAD requests as keepalive and\r
+content availability update.\r
+\r
+Once A has received all content (video-on-demand use case) it closes the\r
+persistent connections to all other peers that have all content (a.k.a.\r
+seeders).\r
+\r
+\r
+6.4.1.3. Leaving a Swarm\r
+\r
+Peers can explicitly leave a swarm by closing the connection. This mechanism\r
+works for both graceful and ungraceful leaves (i.e., peer crashes or\r
+disconnects). When leaving gracefully, a peer should deregister from the\r
+tracker following the PPSP tracker protocol.\r
+\r
+\r
+6.4.1.4. Discussion\r
+\r
+As mentioned earlier, this design suffers from the fact that HTTP is a\r
+client/server protocol. A solution where a peer establishes two HTTP\r
+connections with every other peer may be more elegant, but inefficient. \r
+The mapping of swift messages to headers remains the same:\r
+\r
+       HINT = Range\r
+.br\r
+       HAVE = Accept-Ranges\r
+.br\r
+       HASH = Content-Merkle\r
+.br    \r
+       PEX = e.g. extended Content-Location\r
+.br\r
+\r
+The Content-Merkle header should include some parameters to\r
+indicate the hash function and chunk size (e.g. SHA1 and 1K) used to\r
+build the Merkle tree.\r
+\r
+\r
+.ti 0\r
+6.4.2. PPSP Requirements\r
+\r
+6.4.2.1. Basic Requirements\r
+\r
+- PPSP.REQ-1: The HTTP-based BitTorrent tracker protocol [BITTORRENT] can be\r
+used as the basis for a tracker protocol, to be discussed elsewhere.\r
+\r
+- PPSP.REQ-2: This draft preserves the properties of HTTP, but \r
+extra mechanisms may be necessary to protect against faulty\r
+or malicious peers.\r
+\r
+- PPSP.REQ-3: This draft does not place requirements on peer IDs,\r
+IP+port is sufficient. \r
+\r
+- PPSP.REQ-4: The content is identified by its root hash (video-on-demand)\r
+or a public key (live streaming).\r
+\r
+- PPSP.REQ-5: The content is partitioned into chunks by the streaming application (see 6.4.1.1.)\r
+\r
+- PPSP.REQ-6: Each chunk is identified by a bin number (and its cryptographic hash.)\r
+\r
+- PPSP.REQ-7: The protocol is carried over TCP because HTTP is.\r
+\r
+\r
+6.4.2.2. Peer Protocol Requirements\r
+\r
+- PPSP.PP.REQ-1: A HEAD request can be used to find out which\r
+chunks are available from a peer, which returns the new Accept-Ranges header.\r
+\r
+- PPSP.PP.REQ-2: The new Accept-Ranges header satisfies this. \r
+\r
+- PPSP.PP.REQ-3: A GET with a request-URI requesting the peers of a resource\r
+(e.g. /<encoded roothash>/peers) would have to be added to request known\r
+peers from a peer, if the proposed push-based PEX/~Content-Location\r
+mechanism is not sufficient. Care should be taken with peer address exchange \r
+in general, as the use of such hearsay is a risk for the protocol as it may be \r
+exploited by malicious peers (as a DDoS attack mechanism). A secure \r
+tracking / peer sampling protocol like [PUPPETCAST] may be needed \r
+to make peer-address exchange safe.\r
+\r
+\r
+- PPSP.PP.REQ-4: HAVE/Accept-Ranges headers convey current availability.\r
+\r
+- PPSP.PP.REQ-5: Bin numbering enables a compact representation of chunk availability.\r
+\r
+- PPSP.PP.REQ-6: A new PPSP specific Peer-Report header would have to be\r
+added. \r
+\r
+- PPSP.PP.REQ-7: Transmission and chunk requests are integrated in this\r
+protocol.\r
+\r
+\r
+\r
+\r
+6.4.2.3. Security Requirements\r
+\r
+- PPSP.SEC.REQ-1: An access control mechanism like Closed Swarms\r
+[CLOSED] would have to be added. \r
+\r
+- PPSP.SEC.REQ-2: As swift is carried over HTTP, HTTPS encryption can be\r
+used instead. Alternatively, just the body could be encrypted. The latter\r
+allows for caching servers that are part of the swarm but do not need access\r
+to the decryption keys (they need access to the swift HASHES in the headers\r
+to verify the packet's integrity).\r
+\r
+- PPSP.SEC.REQ-3: HTTPS encryption or the content encryption facilities of\r
+HTTP can be used.\r
+\r
+- PPSP.SEC.REQ-4: The Merkle tree hashing scheme prevents the indirect\r
+spread of corrupt content, as peers will only forward content to others if\r
+its integrity checks out. Another protection mechanism is to not depend on\r
+hearsay (i.e., do not forward other peers' availability information), or to\r
+only use it when the information spread is self-certified by its subjects.\r
+\r
+Other attacks such as a malicious peer claiming it has content, but not\r
+replying are still possible. Or wasting CPU and bandwidth at a receiving\r
+peer by sending packets where the body doesn't match the HASH/Content-Merkle\r
+headers.\r
+\r
+\r
+- PPSP.SEC.REQ-5: The Merkle tree hashing scheme allows a receiving peer to\r
+detect a malicious or faulty sender, which it can subsequently close its\r
+connection to and ignore. Spreading this knowledge to other peers such that\r
+they know about this bad behavior is hearsay. \r
+\r
+\r
+- PPSP.SEC.REQ-6: A risk in peer-to-peer streaming systems is that malicious\r
+peers launch an Eclipse [ECLIPSE] attack on the initial injectors of the\r
+content (in particular in live streaming). The attack tries to let the\r
+injector upload to just malicious peers which then do not forward the\r
+content to others, thus stopping the distribution. An Eclipse attack could\r
+also be launched on an individual peer. Letting these injectors only use\r
+trusted trackers that provide true random samples of the population or using\r
+a secure peer sampling service [PUPPETCAST] can help negate such an attack. \r
+\r
+\r
+- PPSP.SEC.REQ-7: swift supports decentralized tracking via PEX or\r
+additional mechanisms such as DHTs [SECDHTS], but self-certification of\r
+addresses is needed. Self-certification means For example, that each peer \r
+has a public/private key pair [PERMIDS] and creates self-certified address \r
+changes that include the swarm ID and a timestamp, which are then exchanged\r
+among peers or stored in DHTs. See also discussion of PPSP.PP.REQ-3 \r
+above. Content distribution can continue as long as there are peers that \r
+have it available.\r
+\r
+- PPSP.SEC.REQ-8: The verification of data via hashes obtained from a\r
+trusted source is well-established in the BitTorrent protocol [BITTORRENT].\r
+The proposed Merkle tree scheme is a secure extension of this idea.\r
+Self-certification and not using hearsay are other lessons learned from\r
+existing distributed systems.\r
+\r
+- PPSP.SEC.REQ-9: Swift has built-in content integrity protection via\r
+self-certified naming of content, see SEC.REQ-5 and Sec. 3.5.1.\r
+\r
+\r
+.ti 0\r
+7. Security Considerations\r
+\r
+As any other network protocol, the swift faces a common set of security\r
+challenges. An implementation must consider the possibility of buffer\r
+overruns, DoS attacks and manipulation (i.e. reflection attacks). Any\r
+guarantee of privacy seems unlikely, as the user is exposing its IP address\r
+to the peers. A probable exception is the case of the user being hidden\r
+behind a public NAT or proxy.\r
+\r
+\r
+.ti 0\r
+8. Extensibility\r
+\r
+.ti 0\r
+8.1. 32 bit vs 64 bit\r
+\r
+.nf\r
+While in principle the protocol supports bigger (>1TB) files, all the\r
+mentioned counters are 32-bit. It is an optimization, as using\r
+\%64-bit numbers on-wire may cost ~2% practical overhead. The 64-bit\r
+version of every message has typeid of 64+t, e.g. typeid 68 for\r
+\%64-bit hash message:\r
+.nf\r
+   44 000000000000000E 01234567890ABCDEF1234567890ABCDEF1234567\r
+\r
+.ti 0\r
+8.2. IPv6\r
+\r
+.fi\r
+IPv6 versions of PEX messages use the same 64+t shift as just mentioned.\r
+\r
+\r
+.ti 0\r
+8.3. Congestion Control Algorithms\r
+\r
+.fi\r
+Congestion control algorithm is left to the implementation and may even vary\r
+from peer to peer. Congestion control is entirely implemented by the sending\r
+peer, the receiver only provides clues, such as hints, acknowledgments and\r
+timestamps. In general, it is expected that servers would use TCP-like\r
+congestion control schemes such as classic AIMD or CUBIC [CUBIC]. End-user\r
+peers are expected to use weaker-than-TCP (least than best effort)\r
+congestion control, such as [LEDBAT] to minimize seeding counter-incentives.\r
+\r
+\r
+.ti 0\r
+8.4. Piece Picking Algorithms\r
+\r
+Piece picking entirely depends on the receiving peer. The sender peer is\r
+made aware of preferred pieces by the means of HINT messages. In some\r
+scenarios it may be beneficial to allow the sender to ignore those hints and\r
+send unrequested data.\r
+\r
+\r
+.ti 0\r
+8.5. Reciprocity Algorithms\r
+\r
+Reciprocity algorithms are the sole responsibility of the sender peer.\r
+Reciprocal intentions of the sender are not manifested by separate messages\r
+(as BitTorrent's CHOKE/UNCHOKE), as it does not guarantee anything anyway\r
+(the "snubbing" syndrome).\r
+\r
+\r
+.ti 0\r
+8.6. Different crypto/hashing schemes\r
+\r
+.fi\r
+Once a flavor of swift will need to use a different crypto scheme\r
+(e.g., SHA-256), a message should be allocated for that. As the root\r
+hash is supplied in the handshake message, the crypto scheme in use\r
+will be known from the very beginning. As the root hash is the\r
+content's identifier, different schemes of crypto cannot be mixed\r
+in the same swarm; different swarms may distribute the same content\r
+using different crypto.\r
+\r
+\r
+.ti 0\r
+9. Rationale\r
+\r
+Historically, the Internet was based on end-to-end unicast\r
+and, considering the failure of multicast, was addressed by\r
+different technologies, which ultimately boiled down to maintaining\r
+and coordinating distributed replicas. On one hand, downloading\r
+from a nearby well-provisioned replica is somewhat faster and/or\r
+cheaper; on the other hand, it requires to coordinate multiple\r
+parties (the data source, mirrors/CDN sites/peers, consumers). As\r
+the Internet progresses to richer and richer content, the overhead\r
+of peer/replica coordination becomes dwarfed by the mass of the\r
+download itself. Thus, the niche for multiparty transfers expands.\r
+Still, current, relevant technologies are tightly coupled to a\r
+single use case or even infrastructure of a particular corporation.\r
+The mission of our project is to create a generic content-centric\r
+multiparty transport protocol to allow seamless, effortless data\r
+dissemination on the Net.\r
+\r
+                    TABLE 1. Use cases.\r
+\r
+.br\r
+      | mirror-based   peer-assisted        peer-to-peer\r
+.br\r
+------+----------------------------------------------------\r
+.br\r
+data  | SunSITE        CacheLogic VelociX   BitTorrent\r
+.br\r
+VoD   | YouTube        Azureus(+seedboxes)  SwarmPlayer\r
+.br\r
+live  | Akamai Str.    Octoshape, Joost     PPlive\r
+.br\r
+\r
+.fi\r
+The protocol must be designed for maximum genericity, thus \r
+focusing on the very core of the mission, contain no magic\r
+ constants and no hardwired policies. Effectively, it is a \r
+set of messages allowing to securely retrieve data from\r
+whatever source available, in parallel. Ideally, the protocol must\r
+be able to run over IP as an independent transport protocol.\r
+Practically, it must run over UDP and TCP.\r
+\r
+\r
+.ti 0\r
+9.1.  Design Goals\r
+\r
+.fi\r
+The technical focus of the swift protocol is to find the \r
+simplest solution involving the minimum set of primitives, still \r
+being sufficient to implement all the targeted usecases (see \r
+Table 1), suitable for use in general-purpose software and hardware\r
+(i.e. a web browser or a set-top box). The five design goals for \r
+the protocol are:\r
+\r
+.nf\r
+1. Embeddable kernel-ready protocol.\r
+2. Embrace real-time streaming, in- and out-of-order download.\r
+3. Have short warm-up times.\r
+4. Traverse NATs transparently.\r
+5. Be extensible, allow for multitude of implementation over\r
+   diverse mediums, allow for drop-in pluggability.\r
+\r
+The objectives are referenced as (1)-(5).\r
+\r
+.fi\r
+The goal of embedding (1) means that the protocol must be ready \r
+to function as a regular transport protocol inside a set-top box, \r
+mobile device, a browser and/or in the kernel space. Thus, the \r
+protocol must have light footprint, preferably less than TCP, \r
+in spite of the necessity to support numerous ongoing connections \r
+as well as to constantly probe the network for new possibilities. \r
+The practical overhead for TCP is estimated at 10KB per connection \r
+[HTTP1MLN]. We aim at <1KB per peer connected. Also, the amount of\r
+ code necessary to make a basic implementation must be limited to \r
+10KLoC of C. Otherwise, besides the resource considerations, \r
+maintaining and auditing the code might become prohibitively expensive.\r
+\r
+The support for all three basic usecases of real-time streaming,\r
+ \%in-order download and out-of-order download (2) is necessary \r
+for the manifested goal of THE multiparty transport protocol as no\r
+ single usecase dominates over the others.\r
+\r
+The objective of short warm-up times (3) is the matter of end-user\r
+ experience; the playback must start as soon as possible. Thus any \r
+unnecessary initialization roundtrips and warm-up cycles must be \r
+eliminated from the transport layer.\r
+\r
+.fi\r
+Transparent NAT traversal (4) is absolutely necessary as at least\r
+60% of today's users are hidden behind NATs. NATs severely affect\r
+connection patterns in P2P networks thus impacting performance \r
+and fairness [MOLNAT,LUCNAT].\r
+\r
+The protocol must define a common message set (5) to be used by \r
+implementations; it must not hardwire any magic constants, \r
+algorithms or schemes beyond that. For example, an implementation\r
+ is free to use its own congestion control, connection rotation \r
+or reciprocity algorithms. Still, the protocol must enable such \r
+algorithms by supplying sufficient information. For example, \r
+trackerless peer discovery needs peer exchange messages, \r
+scavenger congestion control may need timestamped acknowledgments,\r
+ etc.\r
+\r
+\r
+.ti 0\r
+9.2.  Not TCP\r
+\r
+.fi\r
+To large extent, swift's design is defined by the cornerstone decision\r
+to get rid of TCP and not to reinvent any TCP-like transports on\r
+top of UDP or otherwise. The requirements (1), (4), (5) make TCP a\r
+bad choice due to its high per-connection footprint, complex and\r
+less reliable NAT traversal and fixed predefined congestion control\r
+algorithms. Besides that, an important consideration is that no\r
+block of TCP functionality turns out to be useful for the general\r
+case of swarming downloads. Namely,\r
+.nf\r
+  1. in-order delivery is less useful as peer-to-peer protocols\r
+  often employ out-of-order delivery themselves and in either case\r
+  \%out-of-order data can still be stored;\r
+  2. reliable delivery/retransmissions are not useful because\r
+  the same data might be requested from different sources; as\r
+  in-order delivery is not required, packet losses might be\r
+  patched up lazily, without stopping the flow of data;\r
+  3. flow control is not necessary as the receiver is much less\r
+  likely to be saturated with the data and even if so, that\r
+  situation is perfectly detected by the congestion control;\r
+  4. TCP congestion control is less useful as custom congestion\r
+  control is often needed [LEDBAT].\r
+In general, TCP is built and optimized for a different usecase than\r
+we have with swarming downloads. The abstraction of a "data pipe"\r
+orderly delivering some stream of bytes from one peer to another\r
+turned out to be irrelevant. In even more general terms, TCP\r
+supports the abstraction of pairwise _conversations_, while we need\r
+a content-centric protocol built around the abstraction of a cloud\r
+of participants disseminating the same _data_ in any way and order\r
+that is convenient to them.\r
+\r
+.fi\r
+Thus, the choice is to design a protocol that runs on top of \r
+unreliable datagrams. Instead of reimplementing TCP, we create a \r
+\%datagram-based protocol, completely dropping the sequential data \r
+stream abstraction. Removing unnecessary features of TCP makes \r
+it easier both to implement the protocol and to verify it; \r
+numerous TCP vulnerabilities were caused by complexity of the \r
+protocol's state machine. Still, we reserve the possibility \r
+to run swift on top of TCP or HTTP. \r
+\r
+Pursuing the maxim of making things as simple as possible but not \r
+simpler, we fit the protocol into the constraints of the transport\r
+layer by dropping all the transmission's technical metadata except \r
+for the content's root hash (compare that to metadata files used \r
+in BitTorrent). Elimination of technical metadata is achieved \r
+through the use of Merkle [MERKLE,ABMRKL] hash trees, exclusively\r
+single-file transfers and other techniques. As a result, a transfer\r
+is identified and bootstrapped by its root hash only.\r
+\r
+.fi\r
+To avoid the usual layering of positive/negative acknowledgment \r
+mechanisms we introduce a scale-invariant acknowledgment system\r
+ (see Sec 4.4). The system allows for aggregation and variable \r
+level of detail in requesting, announcing and acknowledging data,\r
+ serves \%in-order and out-of-order retrieval with equal ease.\r
+Besides the protocol's footprint, we also aim at lowering the \r
+size of a minimal useful interaction. Once a single datagram is \r
+received, it must be checked for data integrity, and then either \r
+dropped or accepted, consumed and relayed.\r
+\r
+\r
+\r
+.ti 0\r
+9.3.  Generic Acknowledgments\r
+\r
+.nf\r
+Generic acknowledgments came out of the need to simplify the\r
+data addressing/requesting/acknowledging mechanics, which tends\r
+to become overly complex and multilayered with the conventional\r
+approach. Take the BitTorrent+TCP tandem for example:\r
+\r
+1. The basic data unit is a byte of content in a file.\r
+2. BitTorrent's highest-level unit is a "torrent", physically a\r
+byte range resulting from concatenation of content files.\r
+3. A torrent is divided into "pieces", typically about a thousand\r
+of them. Pieces are used to communicate progress to other\r
+peers. Pieces are also basic data integrity units, as the torrent's\r
+metadata includes a SHA1 hash for every piece.\r
+4. The actual data transfers are requested and made in 16KByte\r
+units, named "blocks" or chunks.\r
+5. Still, one layer lower, TCP also operates with bytes and byte\r
+offsets which are totally different from the torrent's bytes and\r
+offsets, as TCP considers cumulative byte offsets for all content\r
+sent by a connection, be it data, metadata or commands.\r
+6. Finally, another layer lower, IP transfers independent datagrams\r
+(typically around 1.5 kilobyte), which TCP then reassembles into\r
+continuous streams.\r
+\r
+Obviously, such addressing schemes need lots of mappings; from\r
+piece number and block to file(s) and offset(s) to TCP sequence\r
+numbers to the actual packets and the other way around. Lots of\r
+complexity is introduced by mismatch of bounds: packet bounds are\r
+different from file, block or hash/piece bounds. The picture is\r
+typical for a codebase which was historically layered.\r
+\r
+To simplify this aspect, we employ a generic content addressing\r
+scheme based on binary intervals, or "bins" for short.\r
+\r
+\r
+\r
+.ti 0\r
+Acknowledgements\r
+\r
+Arno Bakker and Victor Grishchenko are partially supported by the\r
+P2P-Next project (http://www.p2p-next.org/), a research project \r
+supported by the European Community under its 7th Framework Programme\r
+(grant agreement no. 216217).  The views and conclusions contained \r
+herein are those of the authors and should not be interpreted as \r
+necessarily representing the official policies or endorsements, \r
+either expressed or implied, of the P2P-Next project or the European \r
+Commission.\r
+\r
+.fi\r
+The swift protocol was designed by Victor Grishchenko at Technische Universiteit Delft.\r
+The authors would like to thank the following people for their \r
+contributions to this draft: Mihai Capota, Raul Jiminez, Flutra Osmani, \r
+Riccardo Petrocco, Johan Pouwelse, and Raynor Vliegendhart.\r
+\r
+\r
+.ti 0\r
+References\r
+\r
+.nf\r
+.in 0\r
+[RFC2119] Key words for use in RFCs to Indicate Requirement Levels\r
+[HTTP1MLN] Richard Jones. "A Million-user Comet Application with\r
+    Mochiweb", Part 3. http://www.metabrew.com/article/\r
+    \%a-million-user-comet-application-with-mochiweb-part-3\r
+[MOLNAT] J.J.D. Mol, J.A. Pouwelse, D.H.J. Epema and H.J. Sips:\r
+    \%"Free-riding, Fairness, and Firewalls in P2P File-Sharing"\r
+    Proc. Eighth International Conference on Peer-to-Peer Computing \r
+    (P2P '08), Aachen, Germany, 8-11 Sept. 2008, pp. 301 - 310.\r
+[LUCNAT] L. D'Acunto and M. Meulpolder and R. Rahman and J.A. \r
+    Pouwelse and H.J. Sips. "Modeling and Analyzing the Effects\r
+    of Firewalls and NATs in P2P Swarming Systems". In Proc. of \r
+    IEEE IPDPS (HotP2P), Atlanta, USA, April 23, 2010.\r
+[BINMAP] V. Grishchenko, J. Pouwelse: "Binmaps: hybridizing bitmaps\r
+    and binary trees". Technical Report PDS-2011-005, Parallel and \r
+    Distributed Systems Group, Fac. of Electrical Engineering, \r
+    Mathematics, and Computer Science, Delft University of Technology, \r
+    The Netherlands, April 2009.\r
+[SNP] B. Ford, P. Srisuresh, D. Kegel: "Peer-to-Peer Communication\r
+    Across Network Address Translators",\r
+    http://www.brynosaurus.com/pub/net/p2pnat/\r
+[FIPS180-2]\r
+    Federal Information Processing Standards Publication 180-2:\r
+    "Secure Hash Standard" 2002 August 1.\r
+[MERKLE] Merkle, R. "Secrecy, Authentication, and Public Key Systems", \r
+    Ph.D. thesis, Dept. of Electrical Engineering, Stanford University, \r
+    CA, USA, 1979. pp 40-45.\r
+[ABMRKL] Arno Bakker: "Merkle hash torrent extension", BitTorrent \r
+    Enhancement Proposal 30, Mar 2009.\r
+    http://bittorrent.org/beps/bep_0030.html\r
+[CUBIC] Injong Rhee, and Lisong Xu: "CUBIC: A New TCP-Friendly\r
+    \%High-Speed TCP Variant", Proc. Third International Workshop\r
+    on Protocols for Fast Long-Distance Networks (PFLDnet), Lyon,\r
+    France, Feb 2005.\r
+[LEDBAT] S. Shalunov et al. "Low Extra Delay Background Transport \r
+    (LEDBAT)", IETF Internet-Draft draft-ietf-ledbat-congestion\r
+    (work in progress), Oct 2011.\r
+    \%http://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/ \r
+[TIT4TAT] Bram Cohen: "Incentives Build Robustness in BitTorrent", \r
+    Proc. 1st Workshop on Economics of Peer-to-Peer Systems, Berkeley, \r
+    CA, USA, Jun 2003.\r
+[BITTORRENT] B. Cohen, "The BitTorrent Protocol Specification",\r
+     February 2008, \%http://www.bittorrent.org/beps/bep_0003.html\r
+[RFC3550]  Schulzrinne, H., Casner, S., Frederick, R., and V.\r
+    Jacobson, "RTP: A Transport Protocol for Real-Time\r
+    Applications", STD 64, RFC 3550, July 2003.\r
+[RFC3711] M. Baugher, D. McGrew, M. Naslund, E. Carrara, K. Norrman,\r
+    "The Secure Real-time Transport Protocol (SRTP), RFC 3711, March \r
+    2004.\r
+[RFC5389] Rosenberg, J., Mahy, R., Matthews, P., and D. Wing,\r
+   "Session Traversal Utilities for NAT (STUN)", RFC 5389, October 2008.\r
+[RFC2616] R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, \r
+   P. Leach, T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1",\r
+   RFC2616, June 1999.\r
+[I-D.ietf-ppsp-reqs] Zong, N., Zhang, Y., Pascual, V., Williams, C.,\r
+    and L. Xiao, "P2P  Streaming Protocol (PPSP) Requirements", \r
+    draft-ietf-ppsp-reqs-05 (work in progress), October 2011.\r
+[PPSPCHART] M. Stiemerling et al. "Peer to Peer Streaming Protocol (ppsp)\r
+    Description of Working Group" \r
+   \%http://datatracker.ietf.org/wg/ppsp/charter/\r
+[PERMIDS] A. Bakker et al. "Next-Share Platform M8--Specification\r
+    Part", App. C. P2P-Next project deliverable D4.0.1 (revised), \r
+    June 2009.\r
+    \%http://www.p2p-next.org/download.php?id=E7750C654035D8C2E04D836243E6526E\r
+[PUPPETCAST] A. Bakker and M. van Steen. "PuppetCast: A Secure Peer\r
+    Sampling Protocol". Proceedings 4th Annual European Conference on\r
+    Computer Network Defense (EC2ND'08), pp. 3-10, Dublin, Ireland,\r
+    11-12 December 2008.\r
+[CLOSED] N. Borch, K. Michell, I. Arntzen, and D. Gabrijelcic: "Access\r
+    control to BitTorrent swarms using closed swarms". In Proceedings\r
+    of the 2010 ACM workshop on Advanced video streaming techniques\r
+    for peer-to-peer networks and social networking (AVSTP2P '10).\r
+    ACM, New York, NY, USA, 25-30. \r
+    \%http://doi.acm.org/10.1145/1877891.1877898 \r
+[ECLIPSE] E. Sit and R. Morris, "Security Considerations for\r
+    Peer-to-Peer Distributed Hash Tables", IPTPS '01: Revised Papers\r
+    from the First International Workshop on Peer-to-Peer Systems, pp.\r
+    261-269, Springer-Verlag, London, UK, 2002.\r
+[SECDHTS] G. Urdaneta, G. Pierre, M. van Steen, "A Survey of DHT\r
+   Security Techniques", ACM Computing Surveys, vol. 43(2), June 2011.\r
+[SWIFTIMPL] V. Grishchenko, et al. "Swift M40 reference implementation",\r
+   \%http://swarmplayer.p2p-next.org/download/Next-Share-M40.tar.bz2\r
+   (subdirectory Next-Share/TUD/swift-trial-r2242/), July 2011.\r
+[CCNWIKI] http://en.wikipedia.org/wiki/Content-centric_networking\r
+[HAC01] A.J. Menezes, P.C. van Oorschot and S.A. Vanstone. "Handbook of\r
+   Applied Cryptography", CRC Press, October 1996 (Fifth Printing, \r
+   August 2001). \r
+[JIM11] R. Jimenez, F. Osmani, and B. Knutsson. "Sub-Second Lookups on \r
+   a Large-Scale Kademlia-Based Overlay". 11th IEEE International \r
+   Conference on Peer-to-Peer Computing 2011, Kyoto, Japan, Aug. 2011 \r
+\r
+\r
+.ti 0\r
+Authors' addresses\r
+\r
+.in 3\r
+A. Bakker\r
+Technische Universiteit Delft \r
+Department EWI/ST/PDS\r
+Room HB 9.160\r
+Mekelweg 4\r
+2628CD Delft\r
+The Netherlands\r
+\r
+Email: arno@cs.vu.nl\r
+\r
+.ce 0\r
diff --git a/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.txt b/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.txt
new file mode 100644 (file)
index 0000000..22a3633
--- /dev/null
@@ -0,0 +1,2240 @@
+
+
+
+PPSP                                                           A. Bakker
+Internet-Draft                                                  TU Delft
+
+Expires: June 21, 2012                                 December 19, 2011
+
+        Peer-to-Peer Streaming Protocol (PPSP)
+            <draft-ietf-ppsp-peer-protocol-00.txt>
+
+Abstract
+
+   The Generic Multiparty Protocol (swift) is a peer-to-peer based
+   transport protocol for content dissemination. It can be used for
+   streaming on-demand and live video content, as well as conventional
+   downloading. In swift, the clients consuming the content participate
+   in the dissemination by forwarding the content to other clients via a
+   mesh-like structure.  It is a generic protocol which can run directly
+   on top of UDP, TCP, HTTP or as a RTP profile. Features of swift are
+   short time-till-playback and extensibility. Hence, it can use
+   different mechanisms to prevent freeriding, and work with different
+   peer discovery schemes (centralized trackers or Distributed Hash
+   Tables). Depending on the underlying transport protocol, swift can
+   also use different congestion control algorithms, such as LEDBAT, and
+   offer transparent NAT traversal. Finally, swift maintains only a
+   small amount of state per peer and detects malicious modification of
+   content. This documents describes swift and how it satisfies the
+   requirements for the IETF Peer-to-Peer Streaming Protocol (PPSP)
+   Working Group's peer protocol.
+
+
+Status of this memo
+
+   This Internet-Draft is submitted to IETF in full conformance with the
+   provisions of BCP 78 and BCP 79.
+
+   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 inappropriate to use Internet-Drafts as reference
+   material or to cite them other than as "work in progress."
+
+   The list of current Internet-Drafts can be accessed at
+   http://www.ietf.org/ietf/1id-abstracts.txt.
+
+   The list of Internet-Draft Shadow Directories can be accessed at
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 1]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   http://www.ietf.org/shadow.html.
+
+
+   Copyright (c) 2011 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+Table of Contents
+
+   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  3
+     1.1. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . .  3
+     1.2. Conventions Used in This Document . . . . . . . . . . . . .  4
+     1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . .  5
+   2. Overall Operation . . . . . . . . . . . . . . . . . . . . . . .  6
+     2.1. Joining a Swarm . . . . . . . . . . . . . . . . . . . . . .  6
+     2.2. Exchanging Chunks . . . . . . . . . . . . . . . . . . . . .  6
+     2.3. Leaving a Swarm . . . . . . . . . . . . . . . . . . . . . .  7
+   3. Messages  . . . . . . . . . . . . . . . . . . . . . . . . . . .  7
+     3.1. HANDSHAKE . . . . . . . . . . . . . . . . . . . . . . . . .  8
+     3.3. HAVE  . . . . . . . . . . . . . . . . . . . . . . . . . . .  8
+       3.3.1. Bin Numbers . . . . . . . . . . . . . . . . . . . . . .  8
+       3.3.2. HAVE Message  . . . . . . . . . . . . . . . . . . . . .  9
+     3.4. ACK . . . . . . . . . . . . . . . . . . . . . . . . . . . .  9
+     3.5. DATA and HASH . . . . . . . . . . . . . . . . . . . . . . . 10
+       3.5.1. Merkle Hash Tree  . . . . . . . . . . . . . . . . . . . 10
+       3.5.2. Content Integrity Verification  . . . . . . . . . . . . 11
+       3.5.3. The Atomic Datagram Principle . . . . . . . . . . . . . 11
+       3.5.4. DATA and HASH Messages  . . . . . . . . . . . . . . . . 12
+     3.6. HINT  . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
+     3.7. Peer Address Exchange and NAT Hole Punching . . . . . . . . 13
+     3.8. KEEPALIVE . . . . . . . . . . . . . . . . . . . . . . . . . 14
+     3.9. VERSION . . . . . . . . . . . . . . . . . . . . . . . . . . 14
+     3.10. Conveying Peer Capabilities  . . . . . . . . . . . . . . . 14
+     3.11. Directory Lists  . . . . . . . . . . . . . . . . . . . . . 14
+   4. Automatic Detection of Content Size . . . . . . . . . . . . . . 14
+     4.1. Peak Hashes . . . . . . . . . . . . . . . . . . . . . . . . 15
+     4.2. Procedure . . . . . . . . . . . . . . . . . . . . . . . . . 16
+   5. Live streaming  . . . . . . . . . . . . . . . . . . . . . . . . 17
+   6. Transport Protocols and Encapsulation . . . . . . . . . . . . . 17
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 2]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+     6.1. UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
+       6.1.1. Chunk Size  . . . . . . . . . . . . . . . . . . . . . . 17
+       6.1.2. Datagrams and Messages  . . . . . . . . . . . . . . . . 18
+       6.1.3. Channels  . . . . . . . . . . . . . . . . . . . . . . . 18
+       6.1.4. HANDSHAKE and VERSION . . . . . . . . . . . . . . . . . 19
+       6.1.5. HAVE  . . . . . . . . . . . . . . . . . . . . . . . . . 20
+       6.1.6. ACK . . . . . . . . . . . . . . . . . . . . . . . . . . 20
+       6.1.7. HASH  . . . . . . . . . . . . . . . . . . . . . . . . . 20
+       6.1.8. DATA  . . . . . . . . . . . . . . . . . . . . . . . . . 20
+       6.1.9. KEEPALIVE . . . . . . . . . . . . . . . . . . . . . . . 20
+       6.1.10. Flow and Congestion Control  . . . . . . . . . . . . . 21
+     6.2. TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
+     6.3. RTP Profile for PPSP  . . . . . . . . . . . . . . . . . . . 21
+       6.3.1. Design  . . . . . . . . . . . . . . . . . . . . . . . . 22
+       6.3.2. PPSP Requirements . . . . . . . . . . . . . . . . . . . 24
+     6.4. HTTP (as PPSP)  . . . . . . . . . . . . . . . . . . . . . . 27
+       6.4.1. Design  . . . . . . . . . . . . . . . . . . . . . . . . 27
+       6.4.2. PPSP Requirements . . . . . . . . . . . . . . . . . . . 29
+   7. Security Considerations . . . . . . . . . . . . . . . . . . . . 32
+   8. Extensibility . . . . . . . . . . . . . . . . . . . . . . . . . 32
+     8.1. 32 bit vs 64 bit  . . . . . . . . . . . . . . . . . . . . . 32
+     8.2. IPv6  . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
+     8.3. Congestion Control Algorithms . . . . . . . . . . . . . . . 32
+     8.4. Piece Picking Algorithms  . . . . . . . . . . . . . . . . . 33
+     8.5. Reciprocity Algorithms  . . . . . . . . . . . . . . . . . . 33
+     8.6. Different crypto/hashing schemes  . . . . . . . . . . . . . 33
+   9. Rationale . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
+     9.1.  Design Goals . . . . . . . . . . . . . . . . . . . . . . . 34
+     9.2.  Not TCP  . . . . . . . . . . . . . . . . . . . . . . . . . 35
+     9.3.  Generic Acknowledgments  . . . . . . . . . . . . . . . . . 36
+   Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . 37
+   References . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
+   Authors' addresses . . . . . . . . . . . . . . . . . . . . . . . . 39
+
+
+
+
+1.  Introduction
+
+1.1. Purpose
+
+   This document describes the Generic Multiparty Protocol (swift),
+   designed from the ground up for the task of disseminating the same
+   content to a group of interested parties. Swift supports streaming
+   on-demand and live video content, as well as conventional
+   downloading, thus covering today's three major use cases for content
+   distribution. To fulfil this task, clients consuming the content are
+   put on equal footing with the servers initially providing the content
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 3]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   to create a peer-to-peer system where everyone can provide data. Each
+   peer connects to a random set of other peers resulting in a mesh-like
+   structure.
+
+   Swift uses a simple method of naming content based on self-
+   certification. In particular, content in swift is identified by a
+   single cryptographic hash that is the root hash in a Merkle hash tree
+   calculated recursively from the content [ABMRKL].  This self-
+   certifying hash tree allows every peer to directly detect when a
+   malicious peer tries to distribute fake content. It also ensures only
+   a small amount of information is needed to start a download (just the
+   root hash and some peer addresses).
+
+   Swift uses a novel method of addressing chunks of content called "bin
+   numbers". Bin numbers allow the addressing of a binary interval of
+   data using a single integer. This reduces the amount of state that
+   needs to be recorded per peer and the space needed to denote
+   intervals on the wire, making the protocol light-weight. In general,
+   this numbering system allows swift to work with simpler data
+   structures, e.g. to use arrays instead of binary trees, thus reducing
+   complexity.
+
+   Swift is a generic protocol which can run directly on top of UDP,
+   TCP, HTTP, or as a layer below RTP, similar to SRTP [RFC3711]. As
+   such, swift defines a common set of messages that make up the
+   protocol, which can have different representations on the wire
+   depending on the lower-level protocol used. When the lower-level
+   transport is UDP, swift can also use different congestion control
+   algorithms and facilitate NAT traversal. 
+
+   In addition, swift is extensible in the mechanisms it uses to promote
+   client contribution and prevent freeriding, that is, how to deal with
+   peers that only download content but never upload to others.
+   Furthermore, it can work with different peer discovery schemes, such
+   as centralized trackers or fast Distributed Hash Tables [JIM11]. 
+
+   This documents describes not only the swift protocol but also how it
+   satisfies the requirements for the IETF Peer-to-Peer Streaming
+   Protocol (PPSP) Working Group's peer protocol [PPSPCHART,I-D.ietf-
+   ppsp-reqs]. A reference implementation of swift over UDP is available
+   [SWIFTIMPL].
+
+
+1.2. Conventions Used in This Document
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in [RFC2119].
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 4]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+1.3. Terminology
+
+   message
+        The basic unit of swift communication. A message will have
+        different representations on the wire depending on the transport
+        protocol used. Messages are typically multiplexed into a
+        datagram for transmission.
+
+   datagram
+        A sequence of messages that is offered as a unit to the
+        underlying transport protocol (UDP, etc.). The datagram is
+        swift's Protocol Data Unit (PDU).
+
+   content
+        Either a live transmission, a pre-recorded multimedia asset, or
+        a file.
+
+   bin
+        A number denoting a specific binary interval of the content
+        (i.e., one or more consecutive chunks).
+
+   chunk
+        The basic unit in which the content is divided. E.g. a block of
+        N kilobyte.
+
+   hash
+        The result of applying a cryptographic hash function, more
+        specifically a modification detection code (MDC) [HAC01], such
+        as SHA1 [FIPS180-2], to a piece of data. 
+
+   root hash
+        The root in a Merkle hash tree calculated recursively from the
+        content.
+
+   swarm
+        A group of peers participating in the distribution of the same
+        content.
+
+   swarm ID
+        Unique identifier for a swarm of peers, in swift the root hash
+        of the content (video-on-demand,download) or a public key (live
+        streaming).
+
+   tracker
+        An entity that records the addresses of peers participating in a
+        swarm, usually for a set of swarms, and makes this membership
+        information available to other peers on request.
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 5]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   choking
+        When a peer A is choking peer B it means that A is currently not
+        willing to accept requests for content from B.
+
+
+2. Overall Operation
+
+   The basic unit of communication in swift is the message. Multiple
+   messages are multiplexed into a single datagram for transmission. A
+   datagram (and hence the messages it contains) will have different
+   representations on the wire depending on the transport protocol used
+   (see Sec. 6).
+
+
+2.1. Joining a Swarm
+
+   Consider a peer A that wants to download a certain content asset. To
+   commence a swift download, peer A must have the swarm ID of the
+   content and a list of one or more tracker contact points (e.g.
+   host+port). The list of trackers is optional in the presence of a
+   decentralized tracking mechanism. The swarm ID consists of the swift
+   root hash of the content (video-on-demand, downloading) or a public
+   key (live streaming).
+
+   Peer A now registers with the tracker following e.g. the PPSP tracker
+   protocol [I-D.ietf.ppsp-reqs] and receives the IP address and port of
+   peers already in the swarm, say B, C, and D. Peer A now sends a
+   datagram containing a HANDSHAKE message to B, C, and D. This message
+   serves as an end-to-end check that the peers are actually in the
+   correct swarm, and contains the root hash of the swarm.  Peer B and C
+   respond with datagrams containing a HANDSHAKE message and one or more
+   HAVE messages. A HAVE message conveys (part of) the chunk
+   availability of a peer and thus contains a bin number that denotes
+   what chunks of the content peer B, resp. C have. Peer D sends a
+   datagram with just a HANDSHAKE and omits HAVE messages as a way of
+   choking A.
+
+2.2. Exchanging Chunks
+
+   In response to B and C, A sends new datagrams to B and C containing
+   HINT messages. A HINT or request message indicates the chunks that a
+   peer wants to download, and contains a bin number. The HINT messages
+   to B and C refer to disjunct sets of chunks.  B and C respond with
+   datagrams containing HASH, HAVE and DATA messages.  The HASH messages
+   contains all cryptographic hashes that peer A needs to verify the
+   integrity of the content chunk sent in the DATA message, using the
+   content's root hash as trusted anchor, see Sec. 3.5. Using these
+   hashes peer A verifies that the chunks received from B and C are
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 6]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   correct. It also updates the chunk availability of B and C using the
+   information in the received HAVE messages. 
+
+   After processing, A sends a datagram containing HAVE messages for the
+   chunks it just received to all its peers. In the datagram to B and C
+   it includes an ACK message acknowledging the receipt of the chunks,
+   and adds HINT messages for new chunks. ACK messages are not used when
+   a reliable transport protocol is used. When e.g. C finds that A
+   obtained a chunk (from B) that C did not yet have, C's next datagram
+   includes a HINT for that chunk.
+
+   Peer D does not send HAVE messages to A when it downloads chunks from
+   other peers, until D decides to unchoke peer A. In the case, it sends
+   a datagram with HAVE messages to inform A of its current
+   availability. If B or C decide to choke A they stop sending HAVE and
+   DATA messages and A should then rerequest from other peers. They may
+   continue to send HINT messages, or periodic KEEPALIVE messages such
+   that A keeps sending them HAVE messages.
+
+   Once peer A has received all content (video-on-demand use case) it
+   stops sending messages to all other peers that have all content
+   (a.k.a. seeders). Peer A MAY also contact the tracker or another
+   source again to obtain more peer addresses.
+
+
+2.3. Leaving a Swarm
+
+   Depending on the transport protocol used, peers should either use
+   explicit leave messages or implicitly leave a swarm by stopping to
+   respond to messages. Peers that learn about the departure should
+   remove these peers from the current peer list. The implicit-leave
+   mechanism works for both graceful and ungraceful leaves (i.e., peer
+   crashes or disconnects). When leaving gracefully, a peer should
+   deregister from the tracker following the (PPSP) tracker protocol.
+
+
+3. Messages
+
+   In general, no error codes or responses are used in the protocol;
+   absence of any response indicates an error. Invalid messages are
+   discarded.
+
+   For the sake of simplicity, one swarm of peers always deals with one
+   content asset (e.g. file) only. Retrieval of large collections of
+   files is done by retrieving a directory list file and then
+   recursively retrieving files, which might also turn to be directory
+   lists, as described in Sec. 3.11. 
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 7]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+3.1. HANDSHAKE
+
+   As an end-to-end check that the peers are actually in the correct
+   swarm, the initiating peer and the addressed peer SHOULD send a
+   HANDSHAKE message in the first datagrams they exchange. The only
+   payload of the HANDSHAKE message is the root hash of the content.  
+
+   After the handshakes are exchanged, the initiator knows that the peer
+   really responds. Hence, the second datagram the initiator sends MAY
+   already contain some heavy payload. To minimize the number of
+   initialization roundtrips, implementations MAY dispense with the
+   HANDSHAKE message. To the same end, the first two datagrams exchanged
+   MAY also contain some minor payload, e.g. HAVE messages to indicate
+   the current progress of a peer or a HINT (see Sec. 3.6).
+
+
+3.3. HAVE
+
+   The HAVE message is used to convey which chunks a peers has
+   available, expressed in a new content addressing scheme called "bin
+   numbers".
+
+3.3.1. Bin Numbers
+
+   Swift employs a generic content addressing scheme based on binary
+   intervals ("bins" in short). The smallest interval is a chunk (e.g. a
+   N kilobyte block), the top interval is the complete 2**63 range.  A
+   novel addition to the classical scheme are "bin numbers", a scheme of
+   numbering binary intervals which lays them out into a vector nicely.
+   Consider an chunk interval of width W. To derive the bin numbers of
+   the complete interval and the subintervals, a minimal balanced binary
+   tree is built that is at least W chunks wide at the base. The leaves
+   from left-to-right correspond to the chunks 0..W in the interval, and
+   have bin number I*2 where I is the index of the chunk (counting
+   beyond W-1 to balance the tree). The higher level nodes P in the tree
+   have bin number
+
+        binP = (binL + binR) / 2
+
+   where binL is the bin of node P's left-hand child and binR is the bin
+   of node P's right-hand child. Given that each node in the tree
+   represents a subinterval of the original interval, each such
+   subinterval now is addressable by a bin number, a single integer. The
+   bin number tree of a interval of width W=8 looks like this:
+
+
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 8]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+                           7
+                          / \
+                        /     \
+                      /         \
+                    /             \
+                  3                11
+                 / \              / \
+                /   \            /   \
+               /     \          /     \
+              1       5        9       13 
+             / \     / \      / \      / \
+            0   2   4   6    8   10  12   14
+
+   So bin 7 represents the complete interval, 3 represents the interval
+   of chunk 0..3 and 1 represents the interval of chunks 0 and 1. The
+   special numbers 0xFFFFFFFF (32-bit) or 0xFFFFFFFFFFFFFFFF (64-bit)
+   stands for an empty interval, and 0x7FFF...FFF stands for
+   "everything".
+
+
+3.3.2. HAVE Message
+
+   When a receiving peer has successfully checked the integrity of a
+   chunk or interval of chunks it MUST send a HAVE message to all peers
+   it wants to interact with. The latter allows the HAVE message to be
+   used as a method of choking. The HAVE message MUST contain the bin
+   number of the biggest complete interval of all chunks the receiver
+   has received and checked so far that fully includes the interval of
+   chunks just received. So the bin number MUST denote at least the
+   interval received, but the receiver is supposed to aggregate and
+   acknowledge bigger bins, when possible.
+
+   As a result, every single chunk is acknowledged a logarithmic number
+   of times. That provides some necessary redundancy of acknowledgments
+   and sufficiently compensates for unreliable transport protocols.
+
+   To record which chunks a peer has in the state that an implementation
+   keeps for each peer, an implementation MAY use the "binmap" data
+   structure, which is a hybrid of a bitmap and a binary tree, discussed
+   in detail in [BINMAP].
+
+
+3.4. ACK
+
+   When swift is run over an unreliable transport protocol, an
+   implementation MAY choose to use ACK messages to acknowledge received
+   data. When a receiving peer has successfully checked the integrity of
+   a chunk or interval of chunks C it MUST send a ACK message containing
+
+
+Grishchenko and Bakker   Expires June 21, 2012                  [Page 9]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   the bin number of its biggest, complete, interval covering C to the
+   sending peer (see HAVE). To facilitate delay-based congestion
+   control, an ACK message contains a timestamp.
+
+
+3.5. DATA and HASH
+
+   The DATA message is used to transfer chunks of content. The
+   associated HASH message carries cryptographic hashes that are
+   necessary for a receiver to check the the integrity of the chunk.
+   Swift's content integrity protection is based on a Merkle hash tree
+   and works as follows.
+
+3.5.1. Merkle Hash Tree
+
+   Swift uses a method of naming content based on self-certification. In
+   particular, content in swift is identified by a single cryptographic
+   hash that is the root hash in a Merkle hash tree calculated
+   recursively from the content [ABMRKL]. This self-certifying hash tree
+   allows every peer to directly detect when a malicious peer tries to
+   distribute fake content. It also ensures only a small the amount of
+   information is needed to start a download (the root hash and some
+   peer addresses). For live streaming public keys and dynamic trees are
+   used, see below.
+
+   The Merkle hash tree of a content asset that is divided into N chunks
+   is constructed as follows. Note the construction does not assume
+   chunks of content to be fixed size. Given a cryptographic hash
+   function, more specifically a modification detection code (MDC)
+   [HAC01], such as SHA1, the hashes of all the chunks of the content
+   are calculated. Next, a binary tree of sufficient height is created.
+   Sufficient height means that the lowest level in the tree has enough
+   nodes to hold all chunk hashes in the set, as before, see HAVE
+   message. The figure below shows the tree for a content asset
+   consisting of 7 chunks. As before with the content addressing scheme,
+   the leaves of the tree correspond to a chunk and in this case are
+   assigned the hash of that chunk, starting at the left-most leaf. As
+   the base of the tree may be wider than the number of chunks, any
+   remaining leaves in the tree are assigned a empty hash value of all
+   zeros. Finally, the hash values of the higher levels in the tree are
+   calculated, by concatenating the hash values of the two children
+   (again left to right) and computing the hash of that aggregate. This
+   process ends in a hash value for the root node, which is called the
+   "root hash". Note the root hash only depends on the content and any
+   modification of the content will result in a different root hash. 
+
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 10]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+                           7 = root hash
+                          / \
+                        /     \
+                      /         \
+                    /             \
+                  3*               11
+                 / \              / \
+                /   \            /   \
+               /     \          /     \
+              1       5        9       13* = uncle hash
+             / \     / \      / \      / \
+            0   2   4   6    8   10* 12   14
+
+            C0  C1  C2  C3   C4  C5  C6   E
+            =chunk index     ^^           = empty hash
+
+
+3.5.2. Content Integrity Verification
+
+   Assuming a peer receives the root hash of the content it wants to
+   download from a trusted source, it can can check the integrity of any
+   chunk of that content it receives as follows. It first calculates the
+   hash of the chunk it received, for example chunk C4 in the previous
+   figure. Along with this chunk it MUST receive the hashes required to
+   check the integrity of that chunk. In principle, these are the hash
+   of the chunk's sibling (C5) and that of its "uncles". A chunk's
+   uncles are the sibling Y of its parent X, and the uncle of that Y,
+   recursively until the root is reached. For chunk C4 its uncles are
+   bins 13 and 3, marked with * in the figure. Using this information
+   the peer recalculates the root hash of the tree, and compares it to
+   the root hash it received from the trusted source. If they match the
+   chunk of content has been positively verified to be the requested
+   part of the content. Otherwise, the sending peer either sent the
+   wrong content or the wrong sibling or uncle hashes. For simplicity,
+   the set of sibling and uncles hashes is collectively referred to as
+   the "uncle hashes". 
+
+   In the case of live streaming the tree of chunks grows dynamically
+   and content is identified with a public key instead of a root hash,
+   as the root hash is undefined or, more precisely, transient, as long
+   as new data is generated by the live source. Live streaming is
+   described in more detail below, but content verification works the
+   same for both live and predefined content.
+
+3.5.3. The Atomic Datagram Principle
+
+   As explained above, a datagram consists of a sequence of messages.
+   Ideally, every datagram sent must be independent of other datagrams,
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 11]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   so each datagram SHOULD be processed separately and a loss of one
+   datagram MUST NOT disrupt the flow.  Thus, as a datagram carries zero
+   or more messages, neither messages nor message interdependencies
+   should span over multiple datagrams. 
+
+   This principle implies that as any chunk is verified using its uncle
+   hashes the necessary hashes MUST be put into the same datagram as the
+   chunk's data (Sec. 3.5.4).  As a general rule, if some additional
+   data is still missing to process a message within a datagram, the
+   message SHOULD be dropped. 
+
+   The hashes necessary to verify a chunk are in principle its sibling's
+   hash and all its uncle hashes, but the set of hashes to sent can be
+   optimized. Before sending a packet of data to the receiver, the
+   sender inspects the receiver's previous acknowledgments (HAVE or ACK)
+   to derive which hashes the receiver already has for sure. Suppose,
+   the receiver had acknowledged bin 1 (first two chunks of the file),
+   then it must already have uncle hashes 5, 11 and so on. That is
+   because those hashes are necessary to check packets of bin 1 against
+   the root hash. Then, hashes 3, 7 and so on must be also known as they
+   are calculated in the process of checking the uncle hash chain.
+   Hence, to send bin 12 (i.e. the 7th chunk of content), the sender
+   needs to include just the hashes for bins 14 and 9, which let the
+   data be checked against hash 11 which is already known to the
+   receiver.
+
+   The sender MAY optimistically skip hashes which were sent out in
+   previous, still unacknowledged datagrams. It is an optimization
+   tradeoff between redundant hash transmission and possibility of
+   collateral data loss in the case some necessary hashes were lost in
+   the network so some delivered data cannot be verified and thus has to
+   be dropped. In either case, the receiver builds the Merkle tree on-
+   demand, incrementally, starting from the root hash, and uses it for
+   data validation.
+
+   In short, the sender MUST put into the datagram the missing hashes
+   necessary for the receiver to verify the chunk.
+
+3.5.4. DATA and HASH Messages
+
+   Concretely, a peer that wants to send a chunk of content creates a
+   datagram that MUST consist of one or more HASH messages and a DATA
+   message. The datagram MUST contain a HASH message for each hash the
+   receiver misses for integrity checking. A HASH message MUST contain
+   the bin number and hash data for each of those hashes. The DATA
+   message MUST contain the bin number of the chunk and chunk itself. A
+   peer MAY send the required messages for multiple chunks in the same
+   datagram.
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 12]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+3.6. HINT
+
+   While bulk download protocols normally do explicit requests for
+   certain ranges of data (i.e., use a pull model, for example,
+   BitTorrent [BITTORRENT]), live streaming protocols quite often use a
+   request-less push model to save round trips. Swift supports both
+   models of operation. 
+
+   A peer MUST send a HINT message containing the bin of the chunk
+   interval it wants to download. A peer receiving a HINT message MAY
+   send out requested pieces. When it receives multiple HINTs (either in
+   one datagram or in multiple), the peer SHOULD process the HINTs
+   sequentially. When live streaming, it also may send some other chunks
+   in case it runs out of requests or for some other reason. In that
+   case the only purpose of HINT messages is to coordinate peers and to
+   avoid unnecessary data retransmission, hence the name. 
+
+
+
+3.7. Peer Address Exchange and NAT Hole Punching
+
+   Peer address exchange messages (or PEX messages for short) are common
+   for many peer-to-peer protocols. By exchanging peer addresses in
+   gossip fashion, peers relieve central coordinating entities (the
+   trackers) from unnecessary work. swift optionally features two types
+   of PEX messages: PEX_REQ and PEX_ADD. A peer that wants to retrieve
+   some peer addresses MUST send a PEX_REQ message. The receiving peer
+   MAY respond with a PEX_ADD message containing the addresses of
+   several peers. The addresses MUST be of peers it has recently
+   exchanged messages with to guarantee liveliness. 
+
+   To unify peer exchange and NAT hole punching functionality, the
+   sending pattern of PEX messages is restricted. As the swift handshake
+   is able to do simple NAT hole punching [SNP] transparently, PEX
+   messages must be emitted in the way to facilitate that. Namely, once
+   peer A introduces peer B to peer C by sending a PEX_ADD message to C,
+   it SHOULD also send a message to B introducing C. The messages SHOULD
+   be within 2 seconds from each other, but MAY not be, simultaneous,
+   instead leaving a gap of twice the "typical" RTT, i.e. 300-600ms. The
+   peers are supposed to initiate handshakes to each other thus forming
+   a simple NAT hole punching pattern where the introducing peer
+   effectively acts as a STUN server [RFC5389]. Still, peers MAY ignore
+   PEX messages if uninterested in obtaining new peers or because of
+   security considerations (rate limiting) or any other reason.
+
+   The PEX messages can be used to construct a dedicated tracker peer.
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 13]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+3.8. KEEPALIVE
+
+   A peer MUST send a datagram containing a KEEPALIVE message
+   periodically to each peer it wants to interact with in the future but
+   has no other messages to send them at present.
+
+
+3.9. VERSION
+   Peers MUST convey which version of the swift protocol they support
+   using a VERSION message. This message MUST be included in the initial
+   (handshake) datagrams and MUST indicate which version of the swift
+   protocol the sending peer supports. 
+
+
+3.10. Conveying Peer Capabilities
+   Peers may support just a subset of the swift messages. For example,
+   peers running over TCP may not accept ACK messages, or peers used
+   with a centralized tracking infrastructure may not accept PEX
+   messages. For these reasons, peers SHOULD signal which subset of the
+   swift messages they support by means of the MSGTYPE_RCVD message.
+   This message SHOULD be included in the initial (handshake) datagrams
+   and MUST indicate which swift protocol messages the sending peer
+   supports.
+
+
+3.11. Directory Lists
+
+   Directory list files MUST start with magic bytes ".\n..\n". The rest
+   of the file is a newline-separated list of hashes and file names for
+   the content of the directory. An example:
+
+   .
+   ..
+   1234567890ABCDEF1234567890ABCDEF12345678  readme.txt
+   01234567890ABCDEF1234567890ABCDEF1234567  big_file.dat
+
+
+
+4. Automatic Detection of Content Size
+
+   In swift, the root hash of a static content asset, such as a video
+   file, along with some peer addresses is sufficient to start a
+   download. In addition, swift can reliably and automatically derive
+   the size of such content from information received from the network
+   when fixed sized chunks are used. As a result, it is not necessary to
+   include the size of the content asset as the metadata of the content,
+   in addition to the root hash. Implementations of swift MAY use this
+   automatic detection feature.  
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 14]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+4.1. Peak Hashes
+
+   The ability for a newcomer peer to detect the size of the content
+   depends heavily on the concept of peak hashes. Peak hashes, in
+   general, enable two cornerstone features of swift: reliable file size
+   detection and download/live streaming unification (see Sec. 5). The
+   concept of peak hashes depends on the concepts of filled and
+   incomplete bins. Recall that when constructing the binary trees for
+   content verification and addressing the base of the tree may have
+   more leaves than the number of chunks in the content. In the Merkle
+   hash tree these leaves were assigned empty all-zero hashes to be able
+   to calculate the higher level hashes. A filled bin is now defined as
+   a bin number that addresses an interval of leaves that consists only
+   of hashes of content chunks, not empty hashes. Reversely, an
+   incomplete (not filled) bin addresses an interval that contains also
+   empty hashes, typically an interval that extends past the end of the
+   file. In the following figure bins 7, 11, 13 and 14 are incomplete
+   the rest is filled.
+
+   Formally, a peak hash is a hash in the Merkle tree defined over a
+   filled bin, whose sibling is defined over an incomplete bin.
+   Practically, suppose a file is 7162 bytes long and a chunk is 1
+   kilobyte. That file fits into 7 chunks, the tail chunk being 1018
+   bytes long. The Merkle tree for that file looks as follows. Following
+   the definition the peak hashes of this file are in bins 3, 9 and 12,
+   denoted with a *. E denotes an empty hash.
+
+                           7
+                          / \
+                        /     \
+                      /         \
+                    /             \
+                  3*               11
+                 / \              / \
+                /   \            /   \
+               /     \          /     \
+              1       5        9*      13 
+             / \     / \      / \      / \
+            0   2   4   6    8   10  12*  14
+
+            C0  C1  C2  C3   C4  C5  C6   E
+                                     = 1018 bytes
+
+   Peak hashes can be explained by the binary representation of the
+   number of chunks the file occupies. The binary representation for 7
+   is 111. Every "1" in binary representation of the file's packet
+   length corresponds to a peak hash. For this particular file there are
+   indeed three peaks, bin numbers 3, 9, 12. The number of peak hashes
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 15]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   for a file is therefore also at most logarithmic with its size.
+
+   A peer knowing which bins contain the peak hashes for the file can
+   therefore calculate the number of chunks it consists of, and thus get
+   an estimate of the file size (given all chunks but the last are fixed
+   size). Which bins are the peaks can  be securely communicated from
+   one (untrusted) peer A to another B by letting A send the peak hashes
+   and their bin numbers to B. It can be shown that the root hash that B
+   obtained from a trusted source is sufficient to verify that these are
+   indeed the right peak hashes, as follows.  
+
+   Lemma: Peak hashes can be checked against the root hash.
+
+   Proof: (a) Any peak hash is always the left sibling. Otherwise, be it
+   the right sibling, its left neighbor/sibling must also be defined
+   over a filled bin, because of the way chunks are laid out in the
+   leaves, contradiction. (b) For the rightmost peak hash, its right
+   sibling is zero. (c) For any peak hash, its right sibling might be
+   calculated using peak hashes to the left and zeros for empty bins.
+   (d) Once the right sibling of the leftmost peak hash is calculated,
+   its parent might be calculated. (e) Once that parent is calculated,
+   we might trivially get to the root hash by concatenating the hash
+   with zeros and hashing it repeatedly.
+
+   Informally, the Lemma might be expressed as follows: peak hashes
+   cover all data, so the remaining hashes are either trivial (zeros) or
+   might be calculated from peak hashes and zero hashes.
+
+   Finally, once peer B has obtained the number of chunks in the content
+   it can determine the exact file size as follows. Given that all
+   chunks except the last are fixed size B just needs to know the size
+   of the last chunk. Knowing the number of chunks B can calculate the
+   bin number of the last chunk and download it. As always B verifies
+   the integrity of this chunk against the trusted root hash. As there
+   is only one chunk of data that leads to a successful verification the
+   size of this chunk must be correct. B can then determine the exact
+   file size as 
+
+       (number of chunks -1) * fixed chunk size + size of last chunk
+
+
+4.2. Procedure
+
+   A swift implementation that wants to use automatic size detection
+   MUST operate as follows. When a peer B sends a DATA message for the
+   first time to a peer A, B MUST include all the peak hashes for the
+   content in the same datagram, unless A has already signalled earlier
+   in the exchange that it knows the peak hashes by having acknowledged
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 16]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   any bin, even the empty one. The receiver A MUST check the peak
+   hashes against the root hash to determine the approximate content
+   size. To obtain the definite content size peer A MUST download the
+   last chunk of the content from any peer that offers it.
+
+
+
+
+
+5. Live streaming
+
+   In the case of live streaming a transfer is bootstrapped with a
+   public key instead of a root hash, as the root hash is undefined or,
+   more precisely, transient, as long as new data is being generated by
+   the live source. Live/download unification is achieved by sending
+   signed peak hashes on-demand, ahead of the actual data. As before,
+   the sender might use acknowledgements to derive which content range
+   the receiver has peak hashes for and to prepend the data hashes with
+   the necessary (signed) peak hashes. Except for the fact that the set
+   of peak hashes changes with time, other parts of the algorithm work
+   as described in Sec. 3. 
+
+   As with static content assets in the previous section, in live
+   streaming content length is not known on advance, but derived
+   on-the-go from the peak hashes. Suppose, our 7 KB stream extended to
+   another kilobyte. Thus, now hash 7 becomes the only peak hash, eating
+   hashes 3, 9 and 12. So, the source sends out a SIGNED_HASH message to
+   announce the fact.
+
+   The number of cryptographic operations will be limited. For example,
+   consider a 25 frame/second video transmitted over UDP. When each
+   frame is transmitted in its own chunk, only 25 signature verification
+   operations per second are required at the receiver for bitrates up to
+   ~12.8 megabit/second. For higher bitrates multiple UDP packets per
+   frame are needed and the number of verifications doubles.
+
+
+
+
+
+6. Transport Protocols and Encapsulation
+
+6.1. UDP
+
+6.1.1. Chunk Size
+
+   Currently, swift-over-UDP is the preferred deployment option.
+   Effectively, UDP allows the use of IP with minimal overhead and it
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 17]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   also allows userspace implementations. The default is to use chunks
+   of 1 kilobyte such that a datagram fits in an Ethernet-sized IP
+   packet. The bin numbering allows to use swift over Jumbo
+   frames/datagrams. Both DATA and HAVE/ACK messages may use e.g. 8
+   kilobyte packets instead of the standard 1 KiB. The hashing scheme
+   stays the same. Using swift with 512 or 256-byte packets is
+   theoretically possible with 64-bit byte-precise bin numbers, but IP
+   fragmentation might be a better method to achieve the same result.
+
+
+6.1.2. Datagrams and Messages
+
+   When using UDP, the abstract datagram described above corresponds
+   directly to a UDP datagram. Each message within a datagram has a
+   fixed length, which depends on the type of the message. The first
+   byte of a message denotes its type. The currently defined types are:
+
+           HANDSHAKE = 0x00
+           DATA = 0x01
+           ACK = 0x02
+           HAVE = 0x03
+           HASH = 0x04
+           PEX_ADD = 0x05
+           PEX_REQ = 0x06
+           SIGNED_HASH = 0x07
+           HINT = 0x08
+           MSGTYPE_RCVD = 0x09
+           VERSION = 0x10
+
+
+   Furthermore, integers are serialized in the network (big-endian) byte
+   order. So consider the example of an ACK message (Sec 3.4). It has
+   message type of 0x02 and a payload of a bin number, a four-byte
+   integer (say, 1); hence, its on the wire representation for UDP can
+   be written in hex as: "02 00000001". This hex-like two character-per-
+   byte notation is used to represent message formats in the rest of
+   this section.
+
+6.1.3. Channels  
+
+   As it is increasingly complex for peers to enable UDP communication
+   between each other due to NATs and firewalls, swift-over-UDP uses a
+   multiplexing scheme, called "channels", to allow multiple swarms to
+   use the same UDP port. Channels loosely correspond to TCP connections
+   and each channel belongs to a single swarm. When channels are used,
+   each datagram starts with four bytes corresponding to the receiving
+   channel number. 
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 18]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+6.1.4. HANDSHAKE and VERSION
+
+   A channel is established with a handshake. To start a handshake, the
+   initiating peer needs to know:
+
+   (1) the IP address of a peer
+   (2) peer's UDP port and
+   (3) the root hash of the content (see Sec. 3.5.1).
+
+   To do the handshake the initiating peer sends a datagram that MUST
+   start with an all 0-zeros channel number followed by a VERSION
+   message, then a HASH message whose payload is the root hash, and a
+   HANDSHAKE message, whose only payload is a locally unused channel
+   number.
+
+   On the wire the datagram will look something like this:
+      00000000 10 01  
+      04 7FFFFFFF 1234123412341234123412341234123412341234
+      00 00000011
+   (to unknown channel, handshake from channel 0x11 speaking protocol
+   version 0x01, initiating a transfer of a file with a root hash
+   123...1234)
+
+   The receiving peer MUST respond with a datagram that starts with the
+   channel number from the sender's HANDSHAKE message, followed by a
+   VERSION message, then a HANDSHAKE message, whose only payload is a
+   locally unused channel number, followed by any other messages it
+   wants to send. 
+
+   Peer's response datagram on the wire:
+      00000011 10 01  
+      00 00000022  03 00000003
+   (peer to the initiator: use channel number 0x22 for this transfer and
+   proto version 0x01; I also have first 4 chunks of the file, see Sec.
+   4.3)
+
+   At this point, the initiator knows that the peer really responds; for
+   that purpose channel ids MUST be random enough to prevent easy
+   guessing. So, the third datagram of a handshake MAY already contain
+   some heavy payload. To minimize the number of initialization
+   roundtrips, the first two datagrams MAY also contain some minor
+   payload, e.g. a couple of HAVE messages roughly indicating the
+   current progress of a peer or a HINT (see Sec. 3.6). When receiving
+   the third datagram, both peers have the proof they really talk to
+   each other; three-way handshake is complete.
+
+   A peer MAY explicit close a channel by sending a HANDSHAKE message
+   that MUST contain an all 0-zeros channel number. 
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 19]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   On the wire:
+      00 00000000
+
+
+6.1.5. HAVE
+
+   A HAVE message (type 0x03) states that the sending peer has the 
+   complete specified bin and successfully checked its integrity:
+      03 00000003
+   (got/checked first four kilobytes of a file/stream)
+
+
+
+6.1.6. ACK
+
+   An ACK message (type 0x02) acknowledges data that was received from
+   its addressee; to facilitate delay-based congestion control, an
+   ACK message contains a timestamp, in particular, a 64-bit microsecond
+   time. 
+      02 00000002 12345678
+   (got the second kilobyte of the file from you; my microsecond
+   timer was showing 0x12345678 at that moment)
+
+
+6.1.7. HASH
+
+   A HASH message (type 0x04) consists of a four-byte bin number and
+   the cryptographic hash (e.g. a 20-byte SHA1 hash)
+      04 7FFFFFFF 1234123412341234123412341234123412341234
+
+
+6.1.8. DATA
+
+   A DATA message (type 0x01) consists of a four-byte bin number and the
+   actual chunk. In case a datagram contains a DATA message, a sender
+   MUST always put the data message in the tail of the datagram. For
+   example:
+      01 00000000 48656c6c6f20776f726c6421
+   (This message accommodates an entire file: "Hello world!")
+
+
+6.1.9. KEEPALIVE
+
+   Keepalives do not have a message type on UDP. They are just simple 
+   datagrams consisting of a 4-byte channel id only.
+
+   On the wire:
+      00000022
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 20]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+6.1.10. Flow and Congestion Control
+
+   Explicit flow control is not necessary in swift-over-UDP. In the case
+   of video-on-demand the receiver will request data explicitly from
+   peers and is therefore in control of how much data is coming towards
+   it. In the case of live streaming, where a push-model may be used,
+   the amount of data incoming is limited to the bitrate, which the
+   receiver must be able to process otherwise it cannot play the stream.
+   Should, for any reason, the receiver get saturated with data that
+   situation is perfectly detected by the congestion control. Swift-
+   over-UDP can support different congestion control algorithms, in
+   particular, it supports the new IETF Low Extra Delay Background
+   Transport (LEDBAT) congestion control algorithm that ensures that
+   peer-to-peer traffic yields to regular best-effort traffic [LEDBAT]. 
+
+
+6.2. TCP
+
+   When run over TCP, swift becomes functionally equivalent to
+   BitTorrent. Namely, most swift messages have corresponding BitTorrent
+   messages and vice versa, except for BitTorrent's explicit interest
+   declarations and choking/unchoking, which serve the classic
+   implementation of the tit-for-tat algorithm [TIT4TAT]. However, TCP
+   is not well suited for multiparty communication, as argued in Sec. 9.
+
+
+6.3. RTP Profile for PPSP
+
+   In this section we sketch how swift can be integrated into RTP
+   [RFC3550] to form the Peer-to-Peer Streaming Protocol (PPSP) [I-
+   D.ietf-ppsp-reqs] running over UDP. The PPSP charter requires
+   existing media transfer protocols be used [PPSPCHART]. Hence, the
+   general idea is to define swift as a profile of RTP, in the same way
+   as the Secure Real-time Transport Protocol (SRTP) [RFC3711]. SRTP,
+   and therefore swift is considered ``a "bump in the stack"
+   implementation which resides between the RTP application and the
+   transport layer. [swift] intercepts RTP packets and then forwards an
+   equivalent [swift] packet on the sending side, and intercepts [swift]
+   packets and passes an equivalent RTP packet up the stack on the
+   receiving side.'' [RFC3711].
+
+   In particular, to encode a swift datagram in an RTP packet all the
+   non-DATA messages of swift such as HINT and HAVE are postfixed to the
+   RTP packet using the UDP encoding and the content of DATA messages is
+   sent in the payload field. Implementations MAY omit the RTP header
+   for packets without payload. This construction allows the streaming
+   application to use of all RTP's current features, and with a
+   modification to the Merkle tree hashing scheme (see below) meets
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 21]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   swift's atomic datagram principle. The latter means that a receiving
+   peer can autonomously verify the RTP packet as being correct content,
+   thus preventing the spread of corrupt data (see requirement PPSP.SEC-
+   REQ-4). 
+
+   The use of ACK messages for reliability is left as a choice of the
+   application using PPSP.
+
+
+6.3.1. Design
+
+   6.3.1.1. Joining a Swarm
+
+   To commence a PPSP download a peer A must have the swarm ID of the
+   stream and a list of one or more tracker contact points (e.g.
+   host+port). The list of trackers is optional in the presence of a
+   decentralized tracking mechanism. The swarm ID consists of the swift
+   root hash of the content, which is divided into chunks (see
+   Discussion).
+
+   Peer A now registers with the PPSP tracker following the tracker
+   protocol [I-D.ietf.ppsp-reqs] and receives the IP address and RTP
+   port of peers already in the swarm, say B, C, and D. Peer A now sends
+   an RTP packet containing a HANDSHAKE without channel information to
+   B, C, and D. This serves as an end-to-end check that the peers are
+   actually in the correct swarm. Optionally A could include a HINT
+   message in some RTP packets if it wants to start receiving content
+   immediately. B and C respond with a HANDSHAKE and HAVE messages.  D
+   sends just a HANDSHAKE and omits HAVE messages as a way of choking A.
+
+
+   6.3.1.2. Exchanging Chunks
+
+   In response to B and C, A sends new RTP packets to B and C with HINTs
+   for disjunct sets of chunks. B and C respond with the requested
+   chunks in the payload and HAVE messages, updating their chunk
+   availability. Upon receipt, A sends HAVE for the chunks received and
+   new HINT messages to B and C. When e.g. C finds that A obtained a
+   chunk (from B) that C did not yet have, C's response includes a HINT
+   for that chunk.
+
+   D does not send HAVE messages, instead if D decides to unchoke peer
+   A, it sends an RTP packet with HAVE messages to inform A of its
+   current availability. If B or C decide to choke A they stop sending
+   HAVE and DATA messages and A should then rerequest from other peers.
+   They may continue to send HINT messages, or exponentially slowing
+   KEEPALIVE messages such that A keeps sending them HAVE messages.
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 22]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   Once A has received all content (video-on-demand use case) it stops
+   sending messages to all other peers that have all content (a.k.a.
+   seeders).
+
+
+   6.3.1.3. Leaving a Swarm
+
+   Peers can implicitly leave a swarm by stopping to respond to
+   messages. Sending peers should remove these peers from the current
+   peer list. This mechanism works for both graceful and ungraceful
+   leaves (i.e., peer crashes or disconnects). When leaving gracefully,
+   a peer should deregister from the tracker following the PPSP tracker
+   protocol.
+
+   More explicit graceful leaves could be implemented using RTCP. In
+   particular, a peer could send a RTCP BYE on the RTCP port that is
+   derivable from a peer's RTP port for all peers in its current peer
+   list. However, to prevent malicious peers from sending BYEs a form of
+   peer authentication is required (e.g. using public keys as peer IDs
+   [PERMIDS].)
+
+
+   6.3.1.4. Discussion
+
+   Using swift as an RTP profile requires a change to the content
+   integrity protection scheme (see Sec. 3.5). The fields in the RTP
+   header, such as the timestamp and PT fields, must be protected by the
+   Merkle tree hashing scheme to prevent malicious alterations.
+   Therefore, the Merkle tree is no longer constructed from pure content
+   chunks, but from the complete RTP packet for a chunk as it would be
+   transmitted (minus the non-DATA swift messages). In other words, the
+   hash of the leaves in the tree is the hash over the Authenticated
+   Portion of the RTP packet as defined by SRTP, illustrated in the
+   following figure (extended from [RFC3711]). There is no need for the
+   RTP packets to be fixed size, as the hashing scheme can deal with
+   variable-sized leaves. 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 23]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+        0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+
+     |V=2|P|X|  CC   |M|     PT      |       sequence number         | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+     |                           timestamp                           | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+     |           synchronization source (SSRC) identifier            | |
+     +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ |
+     |            contributing source (CSRC) identifiers             | |
+     |                               ....                            | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+     |                   RTP extension (OPTIONAL)                    | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+     |                          payload  ...                         | |
+     |                               +-------------------------------+ |
+     |                               | RTP padding   | RTP pad count | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+
+     ~                     swift non-DATA messages (REQUIRED)        ~ |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+     |                 length of swift messages (REQUIRED)           | |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+                                                                       |
+                                              Authenticated Portion ---+
+
+           Figure: The format of an RTP-Swift packet.                   
+                     
+
+   As a downside, with variable-sized payloads the automatic content
+   size detection of Section 4 no longer works, so content length MUST
+   be explicit in the metadata. In addition, storage on disk is more
+   complex with out-of-order, variable-sized packets. On the upside,
+   carrying RTP over swift allow decryption-less caching.
+
+   As with UDP, another matter is how much data is carried inside each
+   packet. An important swift-specific factor here is the resulting
+   number of hash calculations per second needed to verify chunks.
+   Experiments should be conducted to ensure they are not excessive for,
+   e.g., mobile hardware.
+
+   At present, Peer IDs are not required in this design.
+
+
+6.3.2. PPSP Requirements
+
+   6.3.2.1. Basic Requirements
+
+   - PPSP.REQ-1: The swift PEX message can also be used as the basis for
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 24]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   a tracker protocol, to be discussed elsewhere.
+
+   - PPSP.REQ-2: This draft preserves the properties of RTP.
+
+   - PPSP.REQ-3: This draft does not place requirements on peer IDs,
+   IP+port is sufficient. 
+
+   - PPSP.REQ-4: The content is identified by its root hash (video-on-
+   demand) or a public key (live streaming).
+
+   - PPSP.REQ-5: The content is partitioned by the streaming
+   application.
+
+   - PPSP.REQ-6: Each chunk is identified by a bin number (and its
+   cryptographic hash.)
+
+   - PPSP.REQ-7: The protocol is carried over UDP because RTP is.
+
+   - PPSP.REQ-8: The protocol has been designed to allow meaningful data
+   transfer between peers as soon as possible and to avoid unnecessary
+   round-trips. It supports small and variable chunk sizes, and its
+   content integrity protection enables wide scale caching.
+
+
+   6.3.2.2. Peer Protocol Requirements
+
+   - PPSP.PP.REQ-1: A GET_HAVE would have to be added to request which
+   chunks are available from a peer, if the proposed push-based HAVE
+   mechanism is not sufficient. 
+
+   - PPSP.PP.REQ-2: A set of HAVE messages satisfies this. 
+
+   - PPSP.PP.REQ-3: The PEX_REQ message satisfies this. Care should be
+   taken with peer address exchange in general, as the use of such
+   hearsay is a risk for the protocol as it may be exploited by
+   malicious peers (as a DDoS attack mechanism). A secure tracking /
+   peer sampling protocol like [PUPPETCAST] may be needed to make peer-
+   address exchange safe.
+
+   - PPSP.PP.REQ-4: HAVE messages convey current availability via a push
+   model.
+
+   - PPSP.PP.REQ-5: Bin numbering enables a compact representation of
+   chunk availability.
+
+   - PPSP.PP.REQ-6: A new PPSP specific Peer Report message would have
+   to be added to RTCP. 
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 25]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   - PPSP.PP.REQ-7: Transmission and chunk requests are integrated in
+   this protocol.
+
+
+   6.3.2.3. Security Requirements
+
+   - PPSP.SEC.REQ-1: An access control mechanism like Closed Swarms
+   [CLOSED] would have to be added. 
+
+   - PPSP.SEC.REQ-2: As RTP is carried verbatim over swift, RTP
+   encryption can be used. Note that just encrypting the RTP part will
+   allow for caching servers that are part of the swarm but do not need
+   access to the decryption keys. They just need access to the swift
+   HASHES in the postfix to verify the packet's integrity.
+
+   - PPSP.SEC.REQ-3: RTP encryption or IPsec [RFC4303] can be used, if
+   the swift messages must also be encrypted. 
+
+   - PPSP.SEC.REQ-4: The Merkle tree hashing scheme prevents the
+   indirect spread of corrupt content, as peers will only forward chunks
+   to others if their integrity check out. Another protection mechanism
+   is to not depend on hearsay (i.e., do not forward other peers'
+   availability information), or to only use it when the information
+   spread is self-certified by its subjects.
+
+   Other attacks, such as a malicious peer claiming it has content but
+   not replying, are still possible. Or wasting CPU and bandwidth at a
+   receiving peer by sending packets where the DATA doesn't match the
+   HASHes.
+
+
+   - PPSP.SEC.REQ-5: The Merkle tree hashing scheme allows a receiving
+   peer to detect a malicious or faulty sender, which it can
+   subsequently ignore. Spreading this knowledge to other peers such
+   that they know about this bad behavior is hearsay. 
+
+
+   - PPSP.SEC.REQ-6: A risk in peer-to-peer streaming systems is that
+   malicious peers launch an Eclipse [ECLIPSE] attack on the initial
+   injectors of the content (in particular in live streaming). The
+   attack tries to let the injector upload to just malicious peers which
+   then do not forward the content to others, thus stopping the
+   distribution. An Eclipse attack could also be launched on an
+   individual peer. Letting these injectors only use trusted trackers
+   that provide true random samples of the population or using a secure
+   peer sampling service [PUPPETCAST] can help negate such an attack. 
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 26]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   - PPSP.SEC.REQ-7: swift supports decentralized tracking via PEX or
+   additional mechanisms such as DHTs [SECDHTS], but self-certification
+   of addresses is needed. Self-certification means For example, that
+   each peer has a public/private key pair [PERMIDS] and creates self-
+   certified address changes that include the swarm ID and a timestamp,
+   which are then exchanged among peers or stored in DHTs. See also
+   discussion of PPSP.PP.REQ-3 above. Content distribution can continue
+   as long as there are peers that have it available.
+
+   - PPSP.SEC.REQ-8: The verification of data via hashes obtained from a
+   trusted source is well-established in the BitTorrent protocol
+   [BITTORRENT]. The proposed Merkle tree scheme is a secure extension
+   of this idea. Self-certification and not using hearsay are other
+   lessons learned from existing distributed systems.
+
+   - PPSP.SEC.REQ-9: Swift has built-in content integrity protection via
+   self-certified naming of content, see SEC.REQ-5 and Sec. 3.5.1.
+
+
+6.4. HTTP (as PPSP)
+
+   In this section we sketch how swift can be carried over HTTP
+   [RFC2616] to form the PPSP running over TCP. The general idea is to
+   encode a swift datagram in HTTP GET and PUT requests and their
+   replies by transmitting all the non-DATA messages such as HINTs and
+   HAVEs as headers and send DATA messages in the body. This idea
+   follows the atomic datagram principle for each request and reply. So
+   a receiving peer can autonomously verify the message as carrying
+   correct data, thus preventing the spread of corrupt data (see
+   requirement PPSP.SEC-REQ-4). 
+
+   A problem with HTTP is that it is a client/server protocol. To
+   overcome this problem, a peer A uses a PUT request instead of a GET
+   request if the peer B has indicated in a reply that it wants to
+   retrieve a chunk from A. In cases where peer A is no longer
+   interested in receiving requests from B (described below) B may need
+   to establish a new HTTP connection to A to quickly download a chunk,
+   instead of waiting for a convenient time when A sends another
+   request. As an alternative design, two HTTP connections could be used
+   always., but this is inefficient.
+
+6.4.1. Design
+
+   6.4.1.1. Joining a Swarm
+
+   To commence a PPSP download a peer A must have the swarm ID of the
+   stream and a list of one or more tracker contact points, as above.
+   The swarm ID as earlier also consists of the swift root hash of the
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 27]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   content, divided in chunks by the streaming application (e.g. fixed-
+   size chunks of 1 kilobyte for video-on-demand). 
+
+   Peer A now registers with the PPSP tracker following the tracker
+   protocol [I-D.ietf-ppsp-reqs] and receives the IP address and HTTP
+   port of peers already in the swarm, say B, C, and D. Peer A now
+   establishes persistent HTTP connections with B, C, D and sends GET
+   requests with the Request-URI set to /<encoded roothash>. Optionally
+   A could include a HINT message in some requests if it wants to start
+   receiving content immediately. A HINT is encoded as a Range header
+   with a new "bins" unit [RFC2616,$14.35].
+
+   B and C respond with a 200 OK reply with header-encoded HAVE
+   messages.  A HAVE message is encoded as an extended Accept-Ranges:
+   header [RFC2616,$14.5] with the new bins unit and the possibility of
+   listing the set of accepted bins. If no HINT/Range header was present
+   in the request, the body of the reply is empty. D sends just a 200 OK
+   reply and omits the HAVE/Accept-Ranges header as a way of choking A.
+
+   6.4.1.2. Exchanging Chunks
+
+   In response to B and C, A sends GET requests with Range headers,
+   requesting disjunct sets of chunks. B and C respond with 206 Partial
+   Content replies with the requested chunks in the body and Accept-
+   Ranges headers, updating their chunk availability. The HASHES for the
+   chunks are encoded in a new Content-Merkle header and the Content-
+   Range is set to identify the chunk [RFC2616,$14.16]. A new
+   "multipart-bin ranges" equivalent to the "multipart-bytes ranges"
+   media type may be used to transmit multiple chunks in one reply.
+
+   Upon receipt, A sends a new GET request with a HAVE/Accept-Ranges
+   header for the chunks received and new HINT/Range headers to B and C.
+   Now when e.g. C finds that A obtained a chunk (from B) that C did not
+   yet have, C's response includes a HINT/Range for that chunk. In this
+   case, A's next request to C is not a GET request, but a PUT request
+   with the requested chunk sent in the body.
+
+   Again, working around the fact that HTTP is a client/server protocol,
+   peer A periodically sends HEAD requests to peer D (which was
+   virtually choking A) that serve as keepalives and may contain
+   HAVE/Accept-Ranges headers. If D decides to unchoke peer A, it
+   includes an Accept-Ranges header in the "200 OK" reply to inform A of
+   its current chunk availability. 
+
+   If B or C decide to choke A they start responding with 204 No Content
+   replies without HAVE/Accept-Ranges headers and A should then re-
+   request from other peers. However, if their replies contain
+   HINT/Range headers A should keep on sending PUT requests with the
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 28]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   desired data (another client/server workaround). If not, A should
+   slowly send HEAD requests as keepalive and content availability
+   update.
+
+   Once A has received all content (video-on-demand use case) it closes
+   the persistent connections to all other peers that have all content
+   (a.k.a. seeders).
+
+
+   6.4.1.3. Leaving a Swarm
+
+   Peers can explicitly leave a swarm by closing the connection. This
+   mechanism works for both graceful and ungraceful leaves (i.e., peer
+   crashes or disconnects). When leaving gracefully, a peer should
+   deregister from the tracker following the PPSP tracker protocol.
+
+
+   6.4.1.4. Discussion
+
+   As mentioned earlier, this design suffers from the fact that HTTP is
+   a client/server protocol. A solution where a peer establishes two
+   HTTP connections with every other peer may be more elegant, but
+   inefficient. The mapping of swift messages to headers remains the
+   same:
+
+       HINT = Range
+       HAVE = Accept-Ranges
+       HASH = Content-Merkle
+       PEX = e.g. extended Content-Location
+
+   The Content-Merkle header should include some parameters to indicate
+   the hash function and chunk size (e.g. SHA1 and 1K) used to build the
+   Merkle tree.
+
+
+6.4.2. PPSP Requirements
+
+   6.4.2.1. Basic Requirements
+
+   - PPSP.REQ-1: The HTTP-based BitTorrent tracker protocol [BITTORRENT]
+   can be used as the basis for a tracker protocol, to be discussed
+   elsewhere.
+
+   - PPSP.REQ-2: This draft preserves the properties of HTTP, but extra
+   mechanisms may be necessary to protect against faulty or malicious
+   peers.
+
+   - PPSP.REQ-3: This draft does not place requirements on peer IDs,
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 29]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   IP+port is sufficient. 
+
+   - PPSP.REQ-4: The content is identified by its root hash (video-on-
+   demand) or a public key (live streaming).
+
+   - PPSP.REQ-5: The content is partitioned into chunks by the streaming
+   application (see 6.4.1.1.)
+
+   - PPSP.REQ-6: Each chunk is identified by a bin number (and its
+   cryptographic hash.)
+
+   - PPSP.REQ-7: The protocol is carried over TCP because HTTP is.
+
+
+   6.4.2.2. Peer Protocol Requirements
+
+   - PPSP.PP.REQ-1: A HEAD request can be used to find out which chunks
+   are available from a peer, which returns the new Accept-Ranges
+   header.
+
+   - PPSP.PP.REQ-2: The new Accept-Ranges header satisfies this. 
+
+   - PPSP.PP.REQ-3: A GET with a request-URI requesting the peers of a
+   resource (e.g. /<encoded roothash>/peers) would have to be added to
+   request known peers from a peer, if the proposed push-based
+   PEX/~Content-Location mechanism is not sufficient. Care should be
+   taken with peer address exchange in general, as the use of such
+   hearsay is a risk for the protocol as it may be exploited by
+   malicious peers (as a DDoS attack mechanism). A secure tracking /
+   peer sampling protocol like [PUPPETCAST] may be needed to make peer-
+   address exchange safe.
+
+
+   - PPSP.PP.REQ-4: HAVE/Accept-Ranges headers convey current
+   availability.
+
+   - PPSP.PP.REQ-5: Bin numbering enables a compact representation of
+   chunk availability.
+
+   - PPSP.PP.REQ-6: A new PPSP specific Peer-Report header would have to
+   be added. 
+
+   - PPSP.PP.REQ-7: Transmission and chunk requests are integrated in
+   this protocol.
+
+
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 30]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   6.4.2.3. Security Requirements
+
+   - PPSP.SEC.REQ-1: An access control mechanism like Closed Swarms
+   [CLOSED] would have to be added. 
+
+   - PPSP.SEC.REQ-2: As swift is carried over HTTP, HTTPS encryption can
+   be used instead. Alternatively, just the body could be encrypted. The
+   latter allows for caching servers that are part of the swarm but do
+   not need access to the decryption keys (they need access to the swift
+   HASHES in the headers to verify the packet's integrity).
+
+   - PPSP.SEC.REQ-3: HTTPS encryption or the content encryption
+   facilities of HTTP can be used.
+
+   - PPSP.SEC.REQ-4: The Merkle tree hashing scheme prevents the
+   indirect spread of corrupt content, as peers will only forward
+   content to others if its integrity checks out. Another protection
+   mechanism is to not depend on hearsay (i.e., do not forward other
+   peers' availability information), or to only use it when the
+   information spread is self-certified by its subjects.
+
+   Other attacks such as a malicious peer claiming it has content, but
+   not replying are still possible. Or wasting CPU and bandwidth at a
+   receiving peer by sending packets where the body doesn't match the
+   HASH/Content-Merkle headers.
+
+
+   - PPSP.SEC.REQ-5: The Merkle tree hashing scheme allows a receiving
+   peer to detect a malicious or faulty sender, which it can
+   subsequently close its connection to and ignore. Spreading this
+   knowledge to other peers such that they know about this bad behavior
+   is hearsay. 
+
+
+   - PPSP.SEC.REQ-6: A risk in peer-to-peer streaming systems is that
+   malicious peers launch an Eclipse [ECLIPSE] attack on the initial
+   injectors of the content (in particular in live streaming). The
+   attack tries to let the injector upload to just malicious peers which
+   then do not forward the content to others, thus stopping the
+   distribution. An Eclipse attack could also be launched on an
+   individual peer. Letting these injectors only use trusted trackers
+   that provide true random samples of the population or using a secure
+   peer sampling service [PUPPETCAST] can help negate such an attack. 
+
+
+   - PPSP.SEC.REQ-7: swift supports decentralized tracking via PEX or
+   additional mechanisms such as DHTs [SECDHTS], but self-certification
+   of addresses is needed. Self-certification means For example, that
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 31]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   each peer has a public/private key pair [PERMIDS] and creates self-
+   certified address changes that include the swarm ID and a timestamp,
+   which are then exchanged among peers or stored in DHTs. See also
+   discussion of PPSP.PP.REQ-3 above. Content distribution can continue
+   as long as there are peers that have it available.
+
+   - PPSP.SEC.REQ-8: The verification of data via hashes obtained from a
+   trusted source is well-established in the BitTorrent protocol
+   [BITTORRENT]. The proposed Merkle tree scheme is a secure extension
+   of this idea. Self-certification and not using hearsay are other
+   lessons learned from existing distributed systems.
+
+   - PPSP.SEC.REQ-9: Swift has built-in content integrity protection via
+   self-certified naming of content, see SEC.REQ-5 and Sec. 3.5.1.
+
+
+7. Security Considerations
+
+   As any other network protocol, the swift faces a common set of
+   security challenges. An implementation must consider the possibility
+   of buffer overruns, DoS attacks and manipulation (i.e. reflection
+   attacks). Any guarantee of privacy seems unlikely, as the user is
+   exposing its IP address to the peers. A probable exception is the
+   case of the user being hidden behind a public NAT or proxy.
+
+
+8. Extensibility
+
+8.1. 32 bit vs 64 bit
+
+   While in principle the protocol supports bigger (>1TB) files, all the
+   mentioned counters are 32-bit. It is an optimization, as using
+   64-bit numbers on-wire may cost ~2% practical overhead. The 64-bit
+   version of every message has typeid of 64+t, e.g. typeid 68 for
+   64-bit hash message:
+      44 000000000000000E 01234567890ABCDEF1234567890ABCDEF1234567
+
+8.2. IPv6
+
+   IPv6 versions of PEX messages use the same 64+t shift as just
+   mentioned.
+
+
+8.3. Congestion Control Algorithms
+
+   Congestion control algorithm is left to the implementation and may
+   even vary from peer to peer. Congestion control is entirely
+   implemented by the sending peer, the receiver only provides clues,
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 32]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   such as hints, acknowledgments and timestamps. In general, it is
+   expected that servers would use TCP-like congestion control schemes
+   such as classic AIMD or CUBIC [CUBIC]. End-user peers are expected to
+   use weaker-than-TCP (least than best effort) congestion control, such
+   as [LEDBAT] to minimize seeding counter-incentives.
+
+
+8.4. Piece Picking Algorithms
+
+   Piece picking entirely depends on the receiving peer. The sender peer
+   is made aware of preferred pieces by the means of HINT messages. In
+   some scenarios it may be beneficial to allow the sender to ignore
+   those hints and send unrequested data.
+
+
+8.5. Reciprocity Algorithms
+
+   Reciprocity algorithms are the sole responsibility of the sender
+   peer. Reciprocal intentions of the sender are not manifested by
+   separate messages (as BitTorrent's CHOKE/UNCHOKE), as it does not
+   guarantee anything anyway (the "snubbing" syndrome).
+
+
+8.6. Different crypto/hashing schemes
+
+   Once a flavor of swift will need to use a different crypto scheme
+   (e.g., SHA-256), a message should be allocated for that. As the root
+   hash is supplied in the handshake message, the crypto scheme in use
+   will be known from the very beginning. As the root hash is the
+   content's identifier, different schemes of crypto cannot be mixed in
+   the same swarm; different swarms may distribute the same content
+   using different crypto.
+
+
+9. Rationale
+
+   Historically, the Internet was based on end-to-end unicast and,
+   considering the failure of multicast, was addressed by different
+   technologies, which ultimately boiled down to maintaining and
+   coordinating distributed replicas. On one hand, downloading from a
+   nearby well-provisioned replica is somewhat faster and/or cheaper; on
+   the other hand, it requires to coordinate multiple parties (the data
+   source, mirrors/CDN sites/peers, consumers). As the Internet
+   progresses to richer and richer content, the overhead of peer/replica
+   coordination becomes dwarfed by the mass of the download itself.
+   Thus, the niche for multiparty transfers expands. Still, current,
+   relevant technologies are tightly coupled to a single use case or
+   even infrastructure of a particular corporation. The mission of our
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 33]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   project is to create a generic content-centric multiparty transport
+   protocol to allow seamless, effortless data dissemination on the Net.
+
+                       TABLE 1. Use cases.
+
+         | mirror-based   peer-assisted        peer-to-peer
+   ------+----------------------------------------------------
+   data  | SunSITE        CacheLogic VelociX   BitTorrent
+   VoD   | YouTube        Azureus(+seedboxes)  SwarmPlayer
+   live  | Akamai Str.    Octoshape, Joost     PPlive
+
+   The protocol must be designed for maximum genericity, thus focusing
+   on the very core of the mission, contain no magic constants and no
+   hardwired policies. Effectively, it is a set of messages allowing to
+   securely retrieve data from whatever source available, in parallel.
+   Ideally, the protocol must be able to run over IP as an independent
+   transport protocol. Practically, it must run over UDP and TCP.
+
+
+9.1.  Design Goals
+
+   The technical focus of the swift protocol is to find the simplest
+   solution involving the minimum set of primitives, still being
+   sufficient to implement all the targeted usecases (see Table 1),
+   suitable for use in general-purpose software and hardware (i.e. a web
+   browser or a set-top box). The five design goals for the protocol
+   are:
+
+   1. Embeddable kernel-ready protocol.
+   2. Embrace real-time streaming, in- and out-of-order download.
+   3. Have short warm-up times.
+   4. Traverse NATs transparently.
+   5. Be extensible, allow for multitude of implementation over
+      diverse mediums, allow for drop-in pluggability.
+
+   The objectives are referenced as (1)-(5).
+
+   The goal of embedding (1) means that the protocol must be ready to
+   function as a regular transport protocol inside a set-top box, mobile
+   device, a browser and/or in the kernel space. Thus, the protocol must
+   have light footprint, preferably less than TCP, in spite of the
+   necessity to support numerous ongoing connections as well as to
+   constantly probe the network for new possibilities. The practical
+   overhead for TCP is estimated at 10KB per connection [HTTP1MLN]. We
+   aim at <1KB per peer connected. Also, the amount of code necessary to
+   make a basic implementation must be limited to 10KLoC of C.
+   Otherwise, besides the resource considerations, maintaining and
+   auditing the code might become prohibitively expensive.
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 34]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   The support for all three basic usecases of real-time streaming,
+   in-order download and out-of-order download (2) is necessary for the
+   manifested goal of THE multiparty transport protocol as no single
+   usecase dominates over the others.
+
+   The objective of short warm-up times (3) is the matter of end-user
+   experience; the playback must start as soon as possible. Thus any
+   unnecessary initialization roundtrips and warm-up cycles must be
+   eliminated from the transport layer.
+
+   Transparent NAT traversal (4) is absolutely necessary as at least 60%
+   of today's users are hidden behind NATs. NATs severely affect
+   connection patterns in P2P networks thus impacting performance and
+   fairness [MOLNAT,LUCNAT].
+
+   The protocol must define a common message set (5) to be used by
+   implementations; it must not hardwire any magic constants, algorithms
+   or schemes beyond that. For example, an implementation is free to use
+   its own congestion control, connection rotation or reciprocity
+   algorithms. Still, the protocol must enable such algorithms by
+   supplying sufficient information. For example, trackerless peer
+   discovery needs peer exchange messages, scavenger congestion control
+   may need timestamped acknowledgments, etc.
+
+
+9.2.  Not TCP
+
+   To large extent, swift's design is defined by the cornerstone
+   decision to get rid of TCP and not to reinvent any TCP-like
+   transports on top of UDP or otherwise. The requirements (1), (4), (5)
+   make TCP a bad choice due to its high per-connection footprint,
+   complex and less reliable NAT traversal and fixed predefined
+   congestion control algorithms. Besides that, an important
+   consideration is that no block of TCP functionality turns out to be
+   useful for the general case of swarming downloads. Namely,
+     1. in-order delivery is less useful as peer-to-peer protocols
+     often employ out-of-order delivery themselves and in either case
+     out-of-order data can still be stored;
+     2. reliable delivery/retransmissions are not useful because
+     the same data might be requested from different sources; as
+     in-order delivery is not required, packet losses might be
+     patched up lazily, without stopping the flow of data;
+     3. flow control is not necessary as the receiver is much less
+     likely to be saturated with the data and even if so, that
+     situation is perfectly detected by the congestion control;
+     4. TCP congestion control is less useful as custom congestion
+     control is often needed [LEDBAT].
+   In general, TCP is built and optimized for a different usecase than
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 35]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   we have with swarming downloads. The abstraction of a "data pipe"
+   orderly delivering some stream of bytes from one peer to another
+   turned out to be irrelevant. In even more general terms, TCP
+   supports the abstraction of pairwise _conversations_, while we need
+   a content-centric protocol built around the abstraction of a cloud
+   of participants disseminating the same _data_ in any way and order
+   that is convenient to them.
+
+   Thus, the choice is to design a protocol that runs on top of
+   unreliable datagrams. Instead of reimplementing TCP, we create a
+   datagram-based protocol, completely dropping the sequential data
+   stream abstraction. Removing unnecessary features of TCP makes it
+   easier both to implement the protocol and to verify it; numerous TCP
+   vulnerabilities were caused by complexity of the protocol's state
+   machine. Still, we reserve the possibility to run swift on top of TCP
+   or HTTP. 
+
+   Pursuing the maxim of making things as simple as possible but not
+   simpler, we fit the protocol into the constraints of the transport
+   layer by dropping all the transmission's technical metadata except
+   for the content's root hash (compare that to metadata files used in
+   BitTorrent). Elimination of technical metadata is achieved through
+   the use of Merkle [MERKLE,ABMRKL] hash trees, exclusively single-file
+   transfers and other techniques. As a result, a transfer is identified
+   and bootstrapped by its root hash only.
+
+   To avoid the usual layering of positive/negative acknowledgment
+   mechanisms we introduce a scale-invariant acknowledgment system (see
+   Sec 4.4). The system allows for aggregation and variable level of
+   detail in requesting, announcing and acknowledging data, serves
+   in-order and out-of-order retrieval with equal ease. Besides the
+   protocol's footprint, we also aim at lowering the size of a minimal
+   useful interaction. Once a single datagram is received, it must be
+   checked for data integrity, and then either dropped or accepted,
+   consumed and relayed.
+
+
+
+9.3.  Generic Acknowledgments
+
+   Generic acknowledgments came out of the need to simplify the
+   data addressing/requesting/acknowledging mechanics, which tends
+   to become overly complex and multilayered with the conventional
+   approach. Take the BitTorrent+TCP tandem for example:
+
+   1. The basic data unit is a byte of content in a file.
+   2. BitTorrent's highest-level unit is a "torrent", physically a
+   byte range resulting from concatenation of content files.
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 36]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   3. A torrent is divided into "pieces", typically about a thousand
+   of them. Pieces are used to communicate progress to other
+   peers. Pieces are also basic data integrity units, as the torrent's
+   metadata includes a SHA1 hash for every piece.
+   4. The actual data transfers are requested and made in 16KByte
+   units, named "blocks" or chunks.
+   5. Still, one layer lower, TCP also operates with bytes and byte
+   offsets which are totally different from the torrent's bytes and
+   offsets, as TCP considers cumulative byte offsets for all content
+   sent by a connection, be it data, metadata or commands.
+   6. Finally, another layer lower, IP transfers independent datagrams
+   (typically around 1.5 kilobyte), which TCP then reassembles into
+   continuous streams.
+
+   Obviously, such addressing schemes need lots of mappings; from
+   piece number and block to file(s) and offset(s) to TCP sequence
+   numbers to the actual packets and the other way around. Lots of
+   complexity is introduced by mismatch of bounds: packet bounds are
+   different from file, block or hash/piece bounds. The picture is
+   typical for a codebase which was historically layered.
+
+   To simplify this aspect, we employ a generic content addressing
+   scheme based on binary intervals, or "bins" for short.
+
+
+
+Acknowledgements
+
+   Arno Bakker and Victor Grishchenko are partially supported by the
+   P2P-Next project (http://www.p2p-next.org/), a research project 
+   supported by the European Community under its 7th Framework Programme
+   (grant agreement no. 216217).  The views and conclusions contained 
+   herein are those of the authors and should not be interpreted as 
+   necessarily representing the official policies or endorsements, 
+   either expressed or implied, of the P2P-Next project or the European 
+   Commission.
+
+   The swift protocol was designed by Victor Grishchenko at Technische
+   Universiteit Delft. The authors would like to thank the following
+   people for their contributions to this draft: Mihai Capota, Raul
+   Jiminez, Flutra Osmani, Riccardo Petrocco, Johan Pouwelse, and Raynor
+   Vliegendhart.
+
+
+References
+
+[RFC2119] Key words for use in RFCs to Indicate Requirement Levels
+[HTTP1MLN] Richard Jones. "A Million-user Comet Application with
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 37]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+    Mochiweb", Part 3. http://www.metabrew.com/article/
+    a-million-user-comet-application-with-mochiweb-part-3
+[MOLNAT] J.J.D. Mol, J.A. Pouwelse, D.H.J. Epema and H.J. Sips:
+    "Free-riding, Fairness, and Firewalls in P2P File-Sharing"
+    Proc. Eighth International Conference on Peer-to-Peer Computing 
+    (P2P '08), Aachen, Germany, 8-11 Sept. 2008, pp. 301 - 310.
+[LUCNAT] L. D'Acunto and M. Meulpolder and R. Rahman and J.A. 
+    Pouwelse and H.J. Sips. "Modeling and Analyzing the Effects
+    of Firewalls and NATs in P2P Swarming Systems". In Proc. of 
+    IEEE IPDPS (HotP2P), Atlanta, USA, April 23, 2010.
+[BINMAP] V. Grishchenko, J. Pouwelse: "Binmaps: hybridizing bitmaps
+    and binary trees". Technical Report PDS-2011-005, Parallel and 
+    Distributed Systems Group, Fac. of Electrical Engineering, 
+    Mathematics, and Computer Science, Delft University of Technology, 
+    The Netherlands, April 2009.
+[SNP] B. Ford, P. Srisuresh, D. Kegel: "Peer-to-Peer Communication
+    Across Network Address Translators",
+    http://www.brynosaurus.com/pub/net/p2pnat/
+[FIPS180-2]
+    Federal Information Processing Standards Publication 180-2:
+    "Secure Hash Standard" 2002 August 1.
+[MERKLE] Merkle, R. "Secrecy, Authentication, and Public Key Systems", 
+    Ph.D. thesis, Dept. of Electrical Engineering, Stanford University, 
+    CA, USA, 1979. pp 40-45.
+[ABMRKL] Arno Bakker: "Merkle hash torrent extension", BitTorrent 
+    Enhancement Proposal 30, Mar 2009.
+    http://bittorrent.org/beps/bep_0030.html
+[CUBIC] Injong Rhee, and Lisong Xu: "CUBIC: A New TCP-Friendly
+    High-Speed TCP Variant", Proc. Third International Workshop
+    on Protocols for Fast Long-Distance Networks (PFLDnet), Lyon,
+    France, Feb 2005.
+[LEDBAT] S. Shalunov et al. "Low Extra Delay Background Transport 
+    (LEDBAT)", IETF Internet-Draft draft-ietf-ledbat-congestion
+    (work in progress), Oct 2011.
+    http://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/ 
+[TIT4TAT] Bram Cohen: "Incentives Build Robustness in BitTorrent", 
+    Proc. 1st Workshop on Economics of Peer-to-Peer Systems, Berkeley, 
+    CA, USA, Jun 2003.
+[BITTORRENT] B. Cohen, "The BitTorrent Protocol Specification",
+     February 2008, http://www.bittorrent.org/beps/bep_0003.html
+[RFC3550]  Schulzrinne, H., Casner, S., Frederick, R., and V.
+    Jacobson, "RTP: A Transport Protocol for Real-Time
+    Applications", STD 64, RFC 3550, July 2003.
+[RFC3711] M. Baugher, D. McGrew, M. Naslund, E. Carrara, K. Norrman,
+    "The Secure Real-time Transport Protocol (SRTP), RFC 3711, March 
+    2004.
+[RFC5389] Rosenberg, J., Mahy, R., Matthews, P., and D. Wing,
+   "Session Traversal Utilities for NAT (STUN)", RFC 5389, October 2008.
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 38]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+[RFC2616] R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, 
+   P. Leach, T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1",
+   RFC2616, June 1999.
+[I-D.ietf-ppsp-reqs] Zong, N., Zhang, Y., Pascual, V., Williams, C.,
+    and L. Xiao, "P2P  Streaming Protocol (PPSP) Requirements", 
+    draft-ietf-ppsp-reqs-05 (work in progress), October 2011.
+[PPSPCHART] M. Stiemerling et al. "Peer to Peer Streaming Protocol (ppsp)
+    Description of Working Group" 
+   http://datatracker.ietf.org/wg/ppsp/charter/
+[PERMIDS] A. Bakker et al. "Next-Share Platform M8--Specification
+    Part", App. C. P2P-Next project deliverable D4.0.1 (revised), 
+    June 2009.
+    http://www.p2p-next.org/download.php?id=E7750C654035D8C2E04D836243E6526E
+[PUPPETCAST] A. Bakker and M. van Steen. "PuppetCast: A Secure Peer
+    Sampling Protocol". Proceedings 4th Annual European Conference on
+    Computer Network Defense (EC2ND'08), pp. 3-10, Dublin, Ireland,
+    11-12 December 2008.
+[CLOSED] N. Borch, K. Michell, I. Arntzen, and D. Gabrijelcic: "Access
+    control to BitTorrent swarms using closed swarms". In Proceedings
+    of the 2010 ACM workshop on Advanced video streaming techniques
+    for peer-to-peer networks and social networking (AVSTP2P '10).
+    ACM, New York, NY, USA, 25-30. 
+    http://doi.acm.org/10.1145/1877891.1877898 
+[ECLIPSE] E. Sit and R. Morris, "Security Considerations for
+    Peer-to-Peer Distributed Hash Tables", IPTPS '01: Revised Papers
+    from the First International Workshop on Peer-to-Peer Systems, pp.
+    261-269, Springer-Verlag, London, UK, 2002.
+[SECDHTS] G. Urdaneta, G. Pierre, M. van Steen, "A Survey of DHT
+   Security Techniques", ACM Computing Surveys, vol. 43(2), June 2011.
+[SWIFTIMPL] V. Grishchenko, et al. "Swift M40 reference implementation",
+   http://swarmplayer.p2p-next.org/download/Next-Share-M40.tar.bz2
+   (subdirectory Next-Share/TUD/swift-trial-r2242/), July 2011.
+[CCNWIKI] http://en.wikipedia.org/wiki/Content-centric_networking
+[HAC01] A.J. Menezes, P.C. van Oorschot and S.A. Vanstone. "Handbook of
+   Applied Cryptography", CRC Press, October 1996 (Fifth Printing, 
+   August 2001). 
+[JIM11] R. Jimenez, F. Osmani, and B. Knutsson. "Sub-Second Lookups on 
+   a Large-Scale Kademlia-Based Overlay". 11th IEEE International 
+   Conference on Peer-to-Peer Computing 2011, Kyoto, Japan, Aug. 2011 
+
+
+Authors' addresses
+
+   A. Bakker
+   Technische Universiteit Delft 
+   Department EWI/ST/PDS
+   Room HB 9.160
+   Mekelweg 4
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 39]
+\f
+Internet-Draft                   swift                 December 19, 2011
+
+
+   2628CD Delft
+   The Netherlands
+
+   Email: arno@cs.vu.nl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Grishchenko and Bakker   Expires June 21, 2012                 [Page 40]
diff --git a/src/libswift_udp/doc/index.html b/src/libswift_udp/doc/index.html
new file mode 100644 (file)
index 0000000..6207e1d
--- /dev/null
@@ -0,0 +1,164 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head> 
+       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+       <title>swift: the multiparty transport protocol</title>         
+       <link rel="stylesheet" type="text/css" href="swift.css"></link>
+</head> 
+
+
+<body> 
+
+
+<div id="container">
+
+
+
+<div id="header">
+    <img id=logo src="apusapus.png"/>
+    <i>swift</i> <br/>
+    the multiparty transport protocol
+</div>
+
+
+
+<div id="intro"> 
+
+       <h2>Turn the Net into a single data cloud</h2>
+       <p>Current Internet protocols are geared for 1:1 client/server communication. We expanded the TCP/IP protocol suite with swarming. Our protocol is designed to be capable of integration into browsers or operating systems and is able to serve 95% of current Internet traffic.</p> 
+       <p><em>swift</em> is a multiparty transport protocol. Its mission is to disseminate content among a swarm of peers. It might be understood as <a href="http://en.wikipedia.org/wiki/BitTorrent">BitTorrent</a> at the <a href="http://en.wikipedia.org/wiki/Transport_Layer">transport layer</a>.</p>
+    <p>The TCP+BitTorrent stack consists of 60+90K lines of code (according to <a href="http://www.dwheeler.com/sloccount/">SLOCCount</a>). With novel datastructures and refactoring we managed to <a href="http://github.com/gritzko/swift">implement</a> swift in a mere 4,000 lines of cross-platform C++ code. The lib<em>swift</em> library is licensed under LGPL; it runs on Mac OS X, Windows and a variety of Unices; it uses UDP with <a href="http://tools.ietf.org/wg/ledbat/">LEDBAT</a> congestion control. Currently maximum throughput is 400Mbps, but we are <a href="http://mughal.tribler.org:8080">working on that</a>: our next target is 1 Gbps. The library is delivered as a part of <a href="http://p2p-next.org">P2P-Next</a>, funded by the European Union <a href="http://cordis.europa.eu/fp7/dc/index.cfm">Seventh Framework Programme</a>.</p> 
+
+</div> <!-- intro -->
+
+
+
+<div id="content">
+
+<h2>Ideas</h2> 
+
+<p>As <a href="http://en.wikipedia.org/wiki/Content-centric_networking"> wise people say</a>, the Internet was initially built for remotely connecting scientists to expensive supercomputers (whose computing power was comparable to modern cell phones). Thus, they supported the abstraction of <em>conversation</em>. Currently, however, the Internet is mostly used for <em>disseminating</em> content &ndash; and this mismatch definitely creates some problems.</p> 
+<p>The <em>swift</em> protocol is a content-centric multiparty transport protocol. Basically, it answers one and only one question: <em>'Here is a <a href="http://en.wikipedia.org/wiki/Hash_function">hash</a>! Give me data for it!'</em>. Ultimately <em>swift</em> aims at the abstraction of the Internet as a single big data <a href="http://en.wikipedia.org/wiki/Cloud_computing">cloud</a>. Such entities as storage, servers and connections are abstracted away and are virtually invisible at the API layer. Given a hash, the data is received from whatever source available and data integrity is checked cryptographically with <a href="http://en.wikipedia.org/wiki/Hash_tree"> Merkle hash trees</a>.</p> 
+<p>An old Unix adage says: 'free memory is wasted memory'. Once a computer is powered on, there is no benefit in keeping some memory unoccupied. We may extend this principle a bit further:</p>
+<ul> 
+       <li>free bandwidth is wasted bandwidth</li> 
+       <li>free storage is wasted storage.</li> 
+</ul> 
+<p>Unless your power budget is really tight, there is no sense in conserving either. Thus, instead of emphasising reciprocity and incentives we focus on code with a lighter footprint, <a href="http://tools.ietf.org/wg/ledbat/">non-intrusive</a> <a href="http://en.wikipedia.org/wiki/TCP_congestion_avoidance_algorithm"> congestion control</a> and automatic disk space management. </p> 
+<p>Currently, most parts of the protocol/library are implemented, pass basic testing and successfully transfer data on real networks. After more scrutinized testing, the protocol and the library are expected to be real-life-ready in December 2010. </p> 
+
+
+
+<h2>Design of the protocol</h2> 
+
+<p>Most features of the protocol are defined by its function as a content-centric multiparty transport protocol. It entirely drops TCP's abstraction of sequential reliable data stream delivery: for <em>swift</em> this is redundant. For example, out-of-order data could still be saved and the same piece of data might always be received from another peer. Being implemented over UDP, the protocol does its best to make every datagram self-contained. In general, pruning of unneeded functions and aggressive layer collapsing greatly simplifies the protocol compared to, for example, the BitTorrent+TCP stack.</p> 
+
+<h3>Atomic datagrams, not data stream</h3> 
+<p>To achieve per-datagram flexibility of data flow and also to adapt to the unreliable medium (UDP, and, ultimately, IP), the protocol was built around the abstraction of atomic datagrams. Ideally, once received, a datagram is either immediately discarded or permanently accepted, ready to be forwarded to other peers. For the sake of flexibility, most of the protocol's messages are optional. It also has no 'standard' header. Instead, each datagram is a concatenation of zero or more messages. No message ever spans two datagrams. Except for the data pieces themselves, no message is acknowledged or guaranteed to be delivered.</p>
+
+<h3>Scale-independent unit system</h3> 
+<p>To avoid a multilayered request/acknowledgement system, where every layer basically does the same but for bigger chunks of data &ndash; as is the case with BitTorrent+TCP packet-block-piece-file-torrent stacking &ndash; <em>swift</em> employs a scale-independent acknowledgement/request system, where data is measured by aligned power-of-2 intervals (so called bins). All acknowledgements and requests are done in terms of bins.</p>
+
+<h3>Datagram-level integrity checks</h3> 
+<p><em>swift</em> builds Merkle hash trees down to every single packet (1KB of data). Once data is transmitted, all uncle hashes necessary for verification are prepended to the same datagram. As the receiver constantly remembers old hashes, the average number of 'new' hashes which have to be transmitted is small: normally around one per packet of data.</p>
+
+<h3>NAT traversal by design</h3>  
+<p>The only method of peer discovery in <em>swift</em> is <a href="http://en.wikipedia.org/wiki/Peer_exchange">PEX</a>: a third peer initiates a connection between two of its contacted peers. The protocol's handshake is engineered to perform simple NAT hole punching transparently if needed. </p>
+
+<h3>Subsetting of the protocol</h3> 
+<p>Different kinds of peers might implement different subsets of messages; a 'tracker', for example, uses the same protocol as every peer, except it only accepts the HANDSHAKE message and the HASH message (to let peers explain what content they are interested in), while returning only HANDSHAKE and PEX_ADD messages (to return the list of peers). Different subsets of accepted/emitted messages may correspond to push/pull peers, plain trackers, hash storing trackers, live streaming peers, etc. </p>
+
+<h3>Push AND pull</h3> 
+<p>The protocol allows both for PUSH (sender decides what to send) and PULL (receiver explicitly requests the data). PUSH is normally used as a fallback if PULL fails; also, the sender may ignore requests and send any data it finds convenient to send. Merkle hash trees allow this flexibility without causing security implications.</p>
+
+<h3>No transmission metadata</h3> 
+<p>Unlike BitTorrent <em>swift</em> employs no transmission metadata (the .torrent file). The only bootstrap information is the root hash; file size is derived from the hash tree once the first packet is received; the hash tree is reconstructed incrementally in the process of download.</p>
+
+
+
+<h2>Specifications and documentation</h2> 
+
+<ul> 
+       <li><a href="https://datatracker.ietf.org/doc/draft-grishchenko-ppsp-swift/">Internet draft style technical description</a></li>
+    <li>
+    <a href="http://github.com/gritzko/swift/tree/master/doc/">Other docs</a></li>
+</ul> 
+
+
+
+<h2>Downloads</h2> 
+
+<ul> 
+       <li><a href="http://github.com/gritzko/swift/tarball/master">Fairly recent tarball</a></li>
+       <li><a href="http://github.com/gritzko/swift"><em>swift</em> sources at github</a></li>
+       <li>Mac OS X: <a href="http://97.107.136.211/files/swift"><em>swift</em> binary</a> and <a href="http://97.107.136.211/files/swift-test.sh">test script</a></li> 
+       <li>Windows: <a href="http://97.107.136.211/files/swift.exe"><em>swift</em> binary</a> and <a href="http://97.107.136.211/files/swift-test.bat">test script</a></li> 
+       <li>One-click demo for Windows: <a href="http://97.107.136.211/files/swift-nobrainer.exe">swift-nobrainer.exe</a></li> 
+</ul>  
+
+
+
+<h2>Frequently asked questions</h2> 
+
+<h3>Well, why <em>swift</em>?</h3> 
+<p>That name has served well for many other protocols; we hope it will serve well for ours. It may be thought of as a meta-joke. The working name for the protocol was 'VicTorrent'. We also insist on lowercase italic <em>swift</em> to keep the name formally unique (for some definition of unique).</p> 
+
+<h3>How is it different from... </h3> 
+
+<h4>...TCP?</h4> 
+<p>TCP emulates reliable in-order delivery ("data pipe") over chaotic unreliable multi-hop networks. TCP has no idea what data it is dealing with, as the data is passed from the userspace. In our case, the data is fixed in advance and many peers participate in distributing the same data. Order of delivery is of little importance and unreliability is naturally compensated for by redundance. Thus, many functions of TCP turn out to be redundant. The only function of TCP that is also critical for <em>swift</em> is the congestion control, but... we need our own custom congestion control! Thus, we did not use TCP.</p> 
+<p>That led both to hurdles and to some savings. As one example, every TCP connection needs to maintain buffers for the data that has left the sender's userspace but not yet arrived at the receiver's userspace. As we know that we are dealing with the same fixed data, we don't need to maintain per-connection buffers. </p> 
+
+<h4>...UDP?</h4> 
+<p>UDP, which is the thinnest wrapper around <a href="http://en.wikipedia.org/wiki/IPv4">IP</a>, is our choice of underlying protocol. From the standpoint of ideology, a transport protocol should be implemented over IP, but unfortunately that causes some chicken-and-egg problems, like a need to get into the kernel to get deployments, and a need to get deployments to be accepted into the kernel. UDP is also quite nice with regard to NAT penetration.</p> 
+
+<h4>...BitTorrent?</h4> 
+<p>BitTorrent is an application-level protocol and quite a heavy one. We focused on fitting our protocol into the restrictions of the transport layer, assuming that the protocol might eventually be included into operating system kernels. For example, we stripped the protocol of any transmission's metadata (the .torrent file); leaving a file's root hash as the only parameter.</p>
+
+<h4>...&micro;Torrent's &micro;TP?</h4> 
+<p>Historically, BitTorrent required lots of adaptations to its underlying transport. First and foremost, TCP is unable to prioritize traffic, so BitTorrent needed to coerce users somehow to tolerate the inconveniences of seeding. That caused tit-for-tat and, to significant degree, rarest-first. Another example is the four-upload-slots limitation. (Apparently some architectural decisions in BitTorrent were dictated by the oddities of Windows 95, but... never mind.)</p> 
+<p>Eventually, BitTorrent developers came to the conclusion that not annoying the user in the first place was probably a better stimulus. So they came up with the <a href="http://www.ietf.org/dyn/wg/charter/ledbat-charter.html">LEDBAT</a> congestion control algorithm (Low Extra Delay Background Transport). LEDBAT allows a peer to seed without interfering with regular traffic (in other words, without slowing down the browser). To integrate the novel congestion control algorithm into BitTorrent incrementally, BitTorrent Inc had to develop a TCP-alike transport named <a href="http://en.wikipedia.org/wiki/Micro_Transport_Protocol">&micro;TP</a>. The <em>swift</em> project (then named VicTorrent) began by trying to understand what would happen if BitTorrent was stripped of any Win95-specific, TCP-specific or Python-specific workarounds. As it turned out, not much was left.</p>
+
+<h4>...Van Jacobson's CCN?</h4> 
+<p><a href="http://www.parc.com/about/people/88/van-jacobson.html">Van Jacobson's</a> team in PARC is doing exploratory research on <a href="http://en.wikipedia.org/wiki/Content-centric_networking">content-centric networking</a>. While BitTorrent works at layer 5 (application), we go to layer 4 (transport). PARC people are bold enough to go to layer 3 and to propose a complete replacement for the entire TCP/IP world. That is certainly a compelling vision, but we focus on the near future (&lt;10 years) while <a href="http://www.ccnx.org/">CCNx</a> is a much more ambitious rework.</p>
+
+<h4>...DCCP?</h4> 
+<p>This question arises quite frequently as DCCP is a congestion-controlled datagram transport. The option of implementing <em>swift</em> over <a href="http://en.wikipedia.org/wiki/DCCP">DCCP</a> was considered, but the inconvenience of working with an esoteric transport was not compensated by the added value of DCCP, which is limited to one mode of congestion control being readily implemented. Architectural restrictions imposed by DCCP were also found to be a major inconvenience. Last but not least, currently only Linux supports DCCP at the kernel level.</p>
+
+<h4>...SCTP?</h4> 
+<p>
+<a href="http://en.wikipedia.org/wiki/SCTP">SCTP</a> is a protocol fixing some shortcomings of TCP mostly in the context of telephony. As was the case with DCCP, features of SCTP were of little interest to us, while things we really needed were missing from SCTP. Still, we must admit that we employ quite a similar message-oriented model (as opposed to TCP's stream orientation).</p>
+
+</div> <!-- content -->
+
+
+
+<div id="contact">
+
+<h2>Who we are</h2> 
+<ul> 
+       <li> <a href="http://p2p-next.org/">P2P-Next Project</a>, EU 7th Framework Program</li> 
+       <li> <a href="http://ewi.tudelft.nl">Delft University of Technology</a> EWI PDS</li> 
+       <li> <a href="http://www.st.ewi.tudelft.nl/~pouwelse/">Dr. Johan Pouwelse</a></li> 
+       <li> <a href="http://www.tribler.org/trac/wiki/VictorGrishchenko"> Dr. Victor Grishchenko</a></li> 
+</ul> 
+
+<h2>Contacts &amp; feedback</h2> 
+
+<ul>
+       <li><a href="mailto:victor.grishchenko@gmail.com">Email us</a></li> 
+       <li><a href="https://listserv2.tudelft.nl/mailman/listinfo/swift-ewi">Subscribe to our mailing list</a></li> 
+</ul>
+
+</div> <!-- contact -->
+
+<div id="footer">
+Copyright &copy; 2010, Delft University of Technology
+</div> <!-- footer -->
+
+</div> <!-- container -->
+
+</body>
+</html> 
diff --git a/src/libswift_udp/doc/mfold-article/IEEEtran.bst b/src/libswift_udp/doc/mfold-article/IEEEtran.bst
new file mode 100644 (file)
index 0000000..90acb4c
--- /dev/null
@@ -0,0 +1,2425 @@
+%%
+%% IEEEtran.bst
+%% BibTeX Bibliography Style file for IEEE Journals and Conferences (unsorted)
+%% Version 1.13 (2008/09/30)
+%% 
+%% Copyright (c) 2003-2008 Michael Shell
+%% 
+%% Original starting code base and algorithms obtained from the output of
+%% Patrick W. Daly's makebst package as well as from prior versions of
+%% IEEE BibTeX styles:
+%% 
+%% 1. Howard Trickey and Oren Patashnik's ieeetr.bst  (1985/1988)
+%% 2. Silvano Balemi and Richard H. Roy's IEEEbib.bst (1993)
+%% 
+%% Support sites:
+%% http://www.michaelshell.org/tex/ieeetran/
+%% http://www.ctan.org/tex-archive/macros/latex/contrib/IEEEtran/
+%% and/or
+%% http://www.ieee.org/
+%% 
+%% For use with BibTeX version 0.99a or later
+%%
+%% This is a numerical citation style.
+%% 
+%%*************************************************************************
+%% Legal Notice:
+%% This code is offered as-is without any warranty either expressed or
+%% implied; without even the implied warranty of MERCHANTABILITY or
+%% FITNESS FOR A PARTICULAR PURPOSE! 
+%% User assumes all risk.
+%% In no event shall IEEE or any contributor to this code be liable for
+%% any damages or losses, including, but not limited to, incidental,
+%% consequential, or any other damages, resulting from the use or misuse
+%% of any information contained here.
+%%
+%% All comments are the opinions of their respective authors and are not
+%% necessarily endorsed by the IEEE.
+%%
+%% This work is distributed under the LaTeX Project Public License (LPPL)
+%% ( http://www.latex-project.org/ ) version 1.3, and may be freely used,
+%% distributed and modified. A copy of the LPPL, version 1.3, is included
+%% in the base LaTeX documentation of all distributions of LaTeX released
+%% 2003/12/01 or later.
+%% Retain all contribution notices and credits.
+%% ** Modified files should be clearly indicated as such, including  **
+%% ** renaming them and changing author support contact information. **
+%%
+%% File list of work: IEEEabrv.bib, IEEEfull.bib, IEEEexample.bib,
+%%                    IEEEtran.bst, IEEEtranS.bst, IEEEtranSA.bst,
+%%                    IEEEtranN.bst, IEEEtranSN.bst, IEEEtran_bst_HOWTO.pdf
+%%*************************************************************************
+%
+%
+% Changelog:
+%
+% 1.00 (2002/08/13) Initial release
+%
+% 1.10 (2002/09/27)
+%  1. Corrected minor bug for improperly formed warning message when a
+%     book was not given a title. Thanks to Ming Kin Lai for reporting this.
+%  2. Added support for CTLname_format_string and CTLname_latex_cmd fields
+%     in the BST control entry type.
+%
+% 1.11 (2003/04/02)
+%  1. Fixed bug with URLs containing underscores when using url.sty. Thanks
+%     to Ming Kin Lai for reporting this.
+%
+% 1.12 (2007/01/11)
+%  1. Fixed bug with unwanted comma before "et al." when an entry contained
+%     more than two author names. Thanks to Pallav Gupta for reporting this.
+%  2. Fixed bug with anomalous closing quote in tech reports that have a
+%     type, but without a number or address. Thanks to Mehrdad Mirreza for
+%     reporting this.
+%  3. Use braces in \providecommand in begin.bib to better support
+%     latex2html. TeX style length assignments OK with recent versions
+%     of latex2html - 1.71 (2002/2/1) or later is strongly recommended.
+%     Use of the language field still causes trouble with latex2html.
+%     Thanks to Federico Beffa for reporting this.
+%  4. Added IEEEtran.bst ID and version comment string to .bbl output.
+%  5. Provide a \BIBdecl hook that allows the user to execute commands
+%     just prior to the first entry.
+%  6. Use default urlstyle (is using url.sty) of "same" rather than rm to
+%     better work with a wider variety of bibliography styles.
+%  7. Changed month abbreviations from Sept., July and June to Sep., Jul.,
+%     and Jun., respectively, as IEEE now does. Thanks to Moritz Borgmann
+%     for reporting this.
+%  8. Control entry types should not be considered when calculating longest
+%     label width.
+%  9. Added alias www for electronic/online.
+% 10. Added CTLname_url_prefix control entry type.
+%
+% 1.13 (2008/09/30)
+%  1. Fixed bug with edition number to ordinal conversion. Thanks to
+%     Michael Roland for reporting this and correcting the algorithm.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DEFAULTS FOR THE CONTROLS OF THE BST STYLE %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% These are the defaults for the user adjustable controls. The values used
+% here can be overridden by the user via IEEEtranBSTCTL entry type.
+
+% NOTE: The recommended LaTeX command to invoke a control entry type is:
+% 
+%\makeatletter
+%\def\bstctlcite{\@ifnextchar[{\@bstctlcite}{\@bstctlcite[@auxout]}}
+%\def\@bstctlcite[#1]#2{\@bsphack
+%  \@for\@citeb:=#2\do{%
+%    \edef\@citeb{\expandafter\@firstofone\@citeb}%
+%    \if@filesw\immediate\write\csname #1\endcsname{\string\citation{\@citeb}}\fi}%
+%  \@esphack}
+%\makeatother
+%
+% It is called at the start of the document, before the first \cite, like:
+% \bstctlcite{IEEEexample:BSTcontrol}
+%
+% IEEEtran.cls V1.6 and later does provide this command.
+
+
+
+% #0 turns off the display of the number for articles.
+% #1 enables
+FUNCTION {default.is.use.number.for.article} { #1 }
+
+
+% #0 turns off the display of the paper and type fields in @inproceedings.
+% #1 enables
+FUNCTION {default.is.use.paper} { #1 }
+
+
+% #0 turns off the forced use of "et al."
+% #1 enables
+FUNCTION {default.is.forced.et.al} { #0 }
+
+% The maximum number of names that can be present beyond which an "et al."
+% usage is forced. Be sure that num.names.shown.with.forced.et.al (below)
+% is not greater than this value!
+% Note: There are many instances of references in IEEE journals which have
+% a very large number of authors as well as instances in which "et al." is
+% used profusely.
+FUNCTION {default.max.num.names.before.forced.et.al} { #10 }
+
+% The number of names that will be shown with a forced "et al.".
+% Must be less than or equal to max.num.names.before.forced.et.al
+FUNCTION {default.num.names.shown.with.forced.et.al} { #1 }
+
+
+% #0 turns off the alternate interword spacing for entries with URLs.
+% #1 enables
+FUNCTION {default.is.use.alt.interword.spacing} { #1 }
+
+% If alternate interword spacing for entries with URLs is enabled, this is
+% the interword spacing stretch factor that will be used. For example, the
+% default "4" here means that the interword spacing in entries with URLs can
+% stretch to four times normal. Does not have to be an integer. Note that
+% the value specified here can be overridden by the user in their LaTeX
+% code via a command such as: 
+% "\providecommand\BIBentryALTinterwordstretchfactor{1.5}" in addition to
+% that via the IEEEtranBSTCTL entry type.
+FUNCTION {default.ALTinterwordstretchfactor} { "4" }
+
+
+% #0 turns off the "dashification" of repeated (i.e., identical to those
+% of the previous entry) names. IEEE normally does this.
+% #1 enables
+FUNCTION {default.is.dash.repeated.names} { #1 }
+
+
+% The default name format control string.
+FUNCTION {default.name.format.string}{ "{f.~}{vv~}{ll}{, jj}" }
+
+
+% The default LaTeX font command for the names.
+FUNCTION {default.name.latex.cmd}{ "" }
+
+
+% The default URL prefix.
+FUNCTION {default.name.url.prefix}{ "[Online]. Available:" }
+
+
+% Other controls that cannot be accessed via IEEEtranBSTCTL entry type.
+
+% #0 turns off the terminal startup banner/completed message so as to
+% operate more quietly.
+% #1 enables
+FUNCTION {is.print.banners.to.terminal} { #1 }
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% FILE VERSION AND BANNER %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FUNCTION{bst.file.version} { "1.13" }
+FUNCTION{bst.file.date} { "2008/09/30" }
+FUNCTION{bst.file.website} { "http://www.michaelshell.org/tex/ieeetran/bibtex/" }
+
+FUNCTION {banner.message}
+{ is.print.banners.to.terminal
+     { "-- IEEEtran.bst version" " " * bst.file.version *
+       " (" * bst.file.date * ") " * "by Michael Shell." *
+       top$
+       "-- " bst.file.website *
+       top$
+       "-- See the " quote$ * "IEEEtran_bst_HOWTO.pdf" * quote$ * " manual for usage information." *
+       top$
+     }
+     { skip$ }
+   if$
+}
+
+FUNCTION {completed.message}
+{ is.print.banners.to.terminal
+     { ""
+       top$
+       "Done."
+       top$
+     }
+     { skip$ }
+   if$
+}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%
+%% STRING CONSTANTS %%
+%%%%%%%%%%%%%%%%%%%%%%
+
+FUNCTION {bbl.and}{ "and" }
+FUNCTION {bbl.etal}{ "et~al." }
+FUNCTION {bbl.editors}{ "eds." }
+FUNCTION {bbl.editor}{ "ed." }
+FUNCTION {bbl.edition}{ "ed." }
+FUNCTION {bbl.volume}{ "vol." }
+FUNCTION {bbl.of}{ "of" }
+FUNCTION {bbl.number}{ "no." }
+FUNCTION {bbl.in}{ "in" }
+FUNCTION {bbl.pages}{ "pp." }
+FUNCTION {bbl.page}{ "p." }
+FUNCTION {bbl.chapter}{ "ch." }
+FUNCTION {bbl.paper}{ "paper" }
+FUNCTION {bbl.part}{ "pt." }
+FUNCTION {bbl.patent}{ "Patent" }
+FUNCTION {bbl.patentUS}{ "U.S." }
+FUNCTION {bbl.revision}{ "Rev." }
+FUNCTION {bbl.series}{ "ser." }
+FUNCTION {bbl.standard}{ "Std." }
+FUNCTION {bbl.techrep}{ "Tech. Rep." }
+FUNCTION {bbl.mthesis}{ "Master's thesis" }
+FUNCTION {bbl.phdthesis}{ "Ph.D. dissertation" }
+FUNCTION {bbl.st}{ "st" }
+FUNCTION {bbl.nd}{ "nd" }
+FUNCTION {bbl.rd}{ "rd" }
+FUNCTION {bbl.th}{ "th" }
+
+
+% This is the LaTeX spacer that is used when a larger than normal space
+% is called for (such as just before the address:publisher).
+FUNCTION {large.space} { "\hskip 1em plus 0.5em minus 0.4em\relax " }
+
+% The LaTeX code for dashes that are used to represent repeated names.
+% Note: Some older IEEE journals used something like
+% "\rule{0.275in}{0.5pt}\," which is fairly thick and runs right along
+% the baseline. However, IEEE now uses a thinner, above baseline,
+% six dash long sequence.
+FUNCTION {repeated.name.dashes} { "------" }
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% PREDEFINED STRING MACROS %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+MACRO {jan} {"Jan."}
+MACRO {feb} {"Feb."}
+MACRO {mar} {"Mar."}
+MACRO {apr} {"Apr."}
+MACRO {may} {"May"}
+MACRO {jun} {"Jun."}
+MACRO {jul} {"Jul."}
+MACRO {aug} {"Aug."}
+MACRO {sep} {"Sep."}
+MACRO {oct} {"Oct."}
+MACRO {nov} {"Nov."}
+MACRO {dec} {"Dec."}
+
+
+
+%%%%%%%%%%%%%%%%%%
+%% ENTRY FIELDS %%
+%%%%%%%%%%%%%%%%%%
+
+ENTRY
+  { address
+    assignee
+    author
+    booktitle
+    chapter
+    day
+    dayfiled
+    edition
+    editor
+    howpublished
+    institution
+    intype
+    journal
+    key
+    language
+    month
+    monthfiled
+    nationality
+    note
+    number
+    organization
+    pages
+    paper
+    publisher
+    school
+    series
+    revision
+    title
+    type
+    url
+    volume
+    year
+    yearfiled
+    CTLuse_article_number
+    CTLuse_paper
+    CTLuse_forced_etal
+    CTLmax_names_forced_etal
+    CTLnames_show_etal
+    CTLuse_alt_spacing
+    CTLalt_stretch_factor
+    CTLdash_repeated_names
+    CTLname_format_string
+    CTLname_latex_cmd
+    CTLname_url_prefix
+  }
+  {}
+  { label }
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%
+%% INTEGER VARIABLES %%
+%%%%%%%%%%%%%%%%%%%%%%%
+
+INTEGERS { prev.status.punct this.status.punct punct.std
+           punct.no punct.comma punct.period 
+           prev.status.space this.status.space space.std
+           space.no space.normal space.large
+           prev.status.quote this.status.quote quote.std
+           quote.no quote.close
+           prev.status.nline this.status.nline nline.std
+           nline.no nline.newblock 
+           status.cap cap.std
+           cap.no cap.yes}
+
+INTEGERS { longest.label.width multiresult nameptr namesleft number.label numnames }
+
+INTEGERS { is.use.number.for.article
+           is.use.paper
+           is.forced.et.al
+           max.num.names.before.forced.et.al
+           num.names.shown.with.forced.et.al
+           is.use.alt.interword.spacing
+           is.dash.repeated.names}
+
+
+%%%%%%%%%%%%%%%%%%%%%%
+%% STRING VARIABLES %%
+%%%%%%%%%%%%%%%%%%%%%%
+
+STRINGS { bibinfo
+          longest.label
+          oldname
+          s
+          t
+          ALTinterwordstretchfactor
+          name.format.string
+          name.latex.cmd
+          name.url.prefix}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOW LEVEL FUNCTIONS %%
+%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FUNCTION {initialize.controls}
+{ default.is.use.number.for.article 'is.use.number.for.article :=
+  default.is.use.paper 'is.use.paper :=
+  default.is.forced.et.al 'is.forced.et.al :=
+  default.max.num.names.before.forced.et.al 'max.num.names.before.forced.et.al :=
+  default.num.names.shown.with.forced.et.al 'num.names.shown.with.forced.et.al :=
+  default.is.use.alt.interword.spacing 'is.use.alt.interword.spacing :=
+  default.is.dash.repeated.names 'is.dash.repeated.names :=
+  default.ALTinterwordstretchfactor 'ALTinterwordstretchfactor :=
+  default.name.format.string 'name.format.string :=
+  default.name.latex.cmd 'name.latex.cmd :=
+  default.name.url.prefix 'name.url.prefix :=
+}
+
+
+% This IEEEtran.bst features a very powerful and flexible mechanism for
+% controlling the capitalization, punctuation, spacing, quotation, and
+% newlines of the formatted entry fields. (Note: IEEEtran.bst does not need
+% or use the newline/newblock feature, but it has been implemented for
+% possible future use.) The output states of IEEEtran.bst consist of
+% multiple independent attributes and, as such, can be thought of as being
+% vectors, rather than the simple scalar values ("before.all", 
+% "mid.sentence", etc.) used in most other .bst files.
+% 
+% The more flexible and complex design used here was motivated in part by
+% IEEE's rather unusual bibliography style. For example, IEEE ends the
+% previous field item with a period and large space prior to the publisher
+% address; the @electronic entry types use periods as inter-item punctuation
+% rather than the commas used by the other entry types; and URLs are never
+% followed by periods even though they are the last item in the entry.
+% Although it is possible to accommodate these features with the conventional
+% output state system, the seemingly endless exceptions make for convoluted,
+% unreliable and difficult to maintain code.
+%
+% IEEEtran.bst's output state system can be easily understood via a simple
+% illustration of two most recently formatted entry fields (on the stack):
+%
+%               CURRENT_ITEM
+%               "PREVIOUS_ITEM
+%
+% which, in this example, is to eventually appear in the bibliography as:
+% 
+%               "PREVIOUS_ITEM," CURRENT_ITEM
+%
+% It is the job of the output routine to take the previous item off of the
+% stack (while leaving the current item at the top of the stack), apply its
+% trailing punctuation (including closing quote marks) and spacing, and then
+% to write the result to BibTeX's output buffer:
+% 
+%               "PREVIOUS_ITEM," 
+% 
+% Punctuation (and spacing) between items is often determined by both of the
+% items rather than just the first one. The presence of quotation marks
+% further complicates the situation because, in standard English, trailing
+% punctuation marks are supposed to be contained within the quotes.
+% 
+% IEEEtran.bst maintains two output state (aka "status") vectors which
+% correspond to the previous and current (aka "this") items. Each vector
+% consists of several independent attributes which track punctuation,
+% spacing, quotation, and newlines. Capitalization status is handled by a
+% separate scalar because the format routines, not the output routine,
+% handle capitalization and, therefore, there is no need to maintain the
+% capitalization attribute for both the "previous" and "this" items.
+% 
+% When a format routine adds a new item, it copies the current output status
+% vector to the previous output status vector and (usually) resets the
+% current (this) output status vector to a "standard status" vector. Using a
+% "standard status" vector in this way allows us to redefine what we mean by
+% "standard status" at the start of each entry handler and reuse the same
+% format routines under the various inter-item separation schemes. For
+% example, the standard status vector for the @book entry type may use
+% commas for item separators, while the @electronic type may use periods,
+% yet both entry handlers exploit many of the exact same format routines.
+% 
+% Because format routines have write access to the output status vector of
+% the previous item, they can override the punctuation choices of the
+% previous format routine! Therefore, it becomes trivial to implement rules
+% such as "Always use a period and a large space before the publisher." By
+% pushing the generation of the closing quote mark to the output routine, we
+% avoid all the problems caused by having to close a quote before having all
+% the information required to determine what the punctuation should be.
+%
+% The IEEEtran.bst output state system can easily be expanded if needed.
+% For instance, it is easy to add a "space.tie" attribute value if the
+% bibliography rules mandate that two items have to be joined with an
+% unbreakable space. 
+
+FUNCTION {initialize.status.constants}
+{ #0 'punct.no :=
+  #1 'punct.comma :=
+  #2 'punct.period :=
+  #0 'space.no := 
+  #1 'space.normal :=
+  #2 'space.large :=
+  #0 'quote.no :=
+  #1 'quote.close :=
+  #0 'cap.no :=
+  #1 'cap.yes :=
+  #0 'nline.no :=
+  #1 'nline.newblock :=
+}
+
+FUNCTION {std.status.using.comma}
+{ punct.comma 'punct.std :=
+  space.normal 'space.std :=
+  quote.no 'quote.std :=
+  nline.no 'nline.std :=
+  cap.no 'cap.std :=
+}
+
+FUNCTION {std.status.using.period}
+{ punct.period 'punct.std :=
+  space.normal 'space.std :=
+  quote.no 'quote.std :=
+  nline.no 'nline.std :=
+  cap.yes 'cap.std :=
+}
+
+FUNCTION {initialize.prev.this.status}
+{ punct.no 'prev.status.punct :=
+  space.no 'prev.status.space :=
+  quote.no 'prev.status.quote :=
+  nline.no 'prev.status.nline :=
+  punct.no 'this.status.punct :=
+  space.no 'this.status.space :=
+  quote.no 'this.status.quote :=
+  nline.no 'this.status.nline :=
+  cap.yes 'status.cap :=
+}
+
+FUNCTION {this.status.std}
+{ punct.std 'this.status.punct :=
+  space.std 'this.status.space :=
+  quote.std 'this.status.quote :=
+  nline.std 'this.status.nline :=
+}
+
+FUNCTION {cap.status.std}{ cap.std 'status.cap := }
+
+FUNCTION {this.to.prev.status}
+{ this.status.punct 'prev.status.punct :=
+  this.status.space 'prev.status.space :=
+  this.status.quote 'prev.status.quote :=
+  this.status.nline 'prev.status.nline :=
+}
+
+
+FUNCTION {not}
+{   { #0 }
+    { #1 }
+  if$
+}
+
+FUNCTION {and}
+{   { skip$ }
+    { pop$ #0 }
+  if$
+}
+
+FUNCTION {or}
+{   { pop$ #1 }
+    { skip$ }
+  if$
+}
+
+
+% convert the strings "yes" or "no" to #1 or #0 respectively
+FUNCTION {yes.no.to.int}
+{ "l" change.case$ duplicate$
+    "yes" =
+    { pop$  #1 }
+    { duplicate$ "no" =
+        { pop$ #0 }
+        { "unknown boolean " quote$ * swap$ * quote$ *
+          " in " * cite$ * warning$
+          #0
+        }
+      if$
+    }
+  if$
+}
+
+
+% pushes true if the single char string on the stack is in the
+% range of "0" to "9"
+FUNCTION {is.num}
+{ chr.to.int$
+  duplicate$ "0" chr.to.int$ < not
+  swap$ "9" chr.to.int$ > not and
+}
+
+% multiplies the integer on the stack by a factor of 10
+FUNCTION {bump.int.mag}
+{ #0 'multiresult :=
+    { duplicate$ #0 > }
+    { #1 -
+      multiresult #10 +
+      'multiresult :=
+    }
+  while$
+pop$
+multiresult
+}
+
+% converts a single character string on the stack to an integer
+FUNCTION {char.to.integer}
+{ duplicate$ 
+  is.num
+    { chr.to.int$ "0" chr.to.int$ - }
+    {"noninteger character " quote$ * swap$ * quote$ *
+          " in integer field of " * cite$ * warning$
+    #0
+    }
+  if$
+}
+
+% converts a string on the stack to an integer
+FUNCTION {string.to.integer}
+{ duplicate$ text.length$ 'namesleft :=
+  #1 'nameptr :=
+  #0 'numnames :=
+    { nameptr namesleft > not }
+    { duplicate$ nameptr #1 substring$
+      char.to.integer numnames bump.int.mag +
+      'numnames :=
+      nameptr #1 +
+      'nameptr :=
+    }
+  while$
+pop$
+numnames
+}
+
+
+
+
+% The output routines write out the *next* to the top (previous) item on the
+% stack, adding punctuation and such as needed. Since IEEEtran.bst maintains
+% the output status for the top two items on the stack, these output
+% routines have to consider the previous output status (which corresponds to
+% the item that is being output). Full independent control of punctuation,
+% closing quote marks, spacing, and newblock is provided.
+% 
+% "output.nonnull" does not check for the presence of a previous empty
+% item.
+% 
+% "output" does check for the presence of a previous empty item and will
+% remove an empty item rather than outputing it.
+% 
+% "output.warn" is like "output", but will issue a warning if it detects
+% an empty item.
+
+FUNCTION {output.nonnull}
+{ swap$
+  prev.status.punct punct.comma =
+     { "," * }
+     { skip$ }
+   if$
+  prev.status.punct punct.period =
+     { add.period$ }
+     { skip$ }
+   if$ 
+  prev.status.quote quote.close =
+     { "''" * }
+     { skip$ }
+   if$
+  prev.status.space space.normal =
+     { " " * }
+     { skip$ }
+   if$
+  prev.status.space space.large =
+     { large.space * }
+     { skip$ }
+   if$
+  write$
+  prev.status.nline nline.newblock =
+     { newline$ "\newblock " write$ }
+     { skip$ }
+   if$
+}
+
+FUNCTION {output}
+{ duplicate$ empty$
+    'pop$
+    'output.nonnull
+  if$
+}
+
+FUNCTION {output.warn}
+{ 't :=
+  duplicate$ empty$
+    { pop$ "empty " t * " in " * cite$ * warning$ }
+    'output.nonnull
+  if$
+}
+
+% "fin.entry" is the output routine that handles the last item of the entry
+% (which will be on the top of the stack when "fin.entry" is called).
+
+FUNCTION {fin.entry}
+{ this.status.punct punct.no =
+     { skip$ }
+     { add.period$ }
+   if$
+   this.status.quote quote.close =
+     { "''" * }
+     { skip$ }
+   if$
+write$
+newline$
+}
+
+
+FUNCTION {is.last.char.not.punct}
+{ duplicate$
+   "}" * add.period$
+   #-1 #1 substring$ "." =
+}
+
+FUNCTION {is.multiple.pages}
+{ 't :=
+  #0 'multiresult :=
+    { multiresult not
+      t empty$ not
+      and
+    }
+    { t #1 #1 substring$
+      duplicate$ "-" =
+      swap$ duplicate$ "," =
+      swap$ "+" =
+      or or
+        { #1 'multiresult := }
+        { t #2 global.max$ substring$ 't := }
+      if$
+    }
+  while$
+  multiresult
+}
+
+FUNCTION {capitalize}{ "u" change.case$ "t" change.case$ }
+
+FUNCTION {emphasize}
+{ duplicate$ empty$
+    { pop$ "" }
+    { "\emph{" swap$ * "}" * }
+  if$
+}
+
+FUNCTION {do.name.latex.cmd}
+{ name.latex.cmd
+  empty$
+    { skip$ }
+    { name.latex.cmd "{" * swap$ * "}" * }
+  if$
+}
+
+% IEEEtran.bst uses its own \BIBforeignlanguage command which directly
+% invokes the TeX hyphenation patterns without the need of the Babel
+% package. Babel does a lot more than switch hyphenation patterns and
+% its loading can cause unintended effects in many class files (such as
+% IEEEtran.cls).
+FUNCTION {select.language}
+{ duplicate$ empty$ 'pop$
+    { language empty$ 'skip$
+        { "\BIBforeignlanguage{" language * "}{" * swap$ * "}" * }
+      if$
+    }
+  if$
+}
+
+FUNCTION {tie.or.space.prefix}
+{ duplicate$ text.length$ #3 <
+    { "~" }
+    { " " }
+  if$
+  swap$
+}
+
+FUNCTION {get.bbl.editor}
+{ editor num.names$ #1 > 'bbl.editors 'bbl.editor if$ }
+
+FUNCTION {space.word}{ " " swap$ * " " * }
+
+
+% Field Conditioners, Converters, Checkers and External Interfaces
+
+FUNCTION {empty.field.to.null.string}
+{ duplicate$ empty$
+    { pop$ "" }
+    { skip$ }
+  if$
+}
+
+FUNCTION {either.or.check}
+{ empty$
+    { pop$ }
+    { "can't use both " swap$ * " fields in " * cite$ * warning$ }
+  if$
+}
+
+FUNCTION {empty.entry.warn}
+{ author empty$ title empty$ howpublished empty$
+  month empty$ year empty$ note empty$ url empty$
+  and and and and and and
+    { "all relevant fields are empty in " cite$ * warning$ }
+    'skip$
+  if$
+}
+
+
+% The bibinfo system provides a way for the electronic parsing/acquisition
+% of a bibliography's contents as is done by ReVTeX. For example, a field
+% could be entered into the bibliography as:
+% \bibinfo{volume}{2}
+% Only the "2" would show up in the document, but the LaTeX \bibinfo command
+% could do additional things with the information. IEEEtran.bst does provide
+% a \bibinfo command via "\providecommand{\bibinfo}[2]{#2}". However, it is
+% currently not used as the bogus bibinfo functions defined here output the
+% entry values directly without the \bibinfo wrapper. The bibinfo functions
+% themselves (and the calls to them) are retained for possible future use.
+% 
+% bibinfo.check avoids acting on missing fields while bibinfo.warn will
+% issue a warning message if a missing field is detected. Prior to calling
+% the bibinfo functions, the user should push the field value and then its
+% name string, in that order.
+
+FUNCTION {bibinfo.check}
+{ swap$ duplicate$ missing$
+    { pop$ pop$ "" }
+    { duplicate$ empty$
+        { swap$ pop$ }
+        { swap$ pop$ }
+      if$
+    }
+  if$
+}
+
+FUNCTION {bibinfo.warn}
+{ swap$ duplicate$ missing$
+    { swap$ "missing " swap$ * " in " * cite$ * warning$ pop$ "" }
+    { duplicate$ empty$
+        { swap$ "empty " swap$ * " in " * cite$ * warning$ }
+        { swap$ pop$ }
+      if$
+    }
+  if$
+}
+
+
+% IEEE separates large numbers with more than 4 digits into groups of
+% three. IEEE uses a small space to separate these number groups. 
+% Typical applications include patent and page numbers.
+
+% number of consecutive digits required to trigger the group separation.
+FUNCTION {large.number.trigger}{ #5 }
+
+% For numbers longer than the trigger, this is the blocksize of the groups.
+% The blocksize must be less than the trigger threshold, and 2 * blocksize
+% must be greater than the trigger threshold (can't do more than one
+% separation on the initial trigger).
+FUNCTION {large.number.blocksize}{ #3 }
+
+% What is actually inserted between the number groups.
+FUNCTION {large.number.separator}{ "\," }
+
+% So as to save on integer variables by reusing existing ones, numnames
+% holds the current number of consecutive digits read and nameptr holds
+% the number that will trigger an inserted space.
+FUNCTION {large.number.separate}
+{ 't :=
+  ""
+  #0 'numnames :=
+  large.number.trigger 'nameptr :=
+  { t empty$ not }
+  { t #-1 #1 substring$ is.num
+      { numnames #1 + 'numnames := }
+      { #0 'numnames := 
+        large.number.trigger 'nameptr :=
+      }
+    if$
+    t #-1 #1 substring$ swap$ *
+    t #-2 global.max$ substring$ 't :=
+    numnames nameptr =
+      { duplicate$ #1 nameptr large.number.blocksize - substring$ swap$
+        nameptr large.number.blocksize - #1 + global.max$ substring$
+        large.number.separator swap$ * *
+        nameptr large.number.blocksize - 'numnames :=
+        large.number.blocksize #1 + 'nameptr :=
+      }
+      { skip$ }
+    if$
+  }
+  while$
+}
+
+% Converts all single dashes "-" to double dashes "--".
+FUNCTION {n.dashify}
+{ large.number.separate
+  't :=
+  ""
+    { t empty$ not }
+    { t #1 #1 substring$ "-" =
+        { t #1 #2 substring$ "--" = not
+            { "--" *
+              t #2 global.max$ substring$ 't :=
+            }
+            {   { t #1 #1 substring$ "-" = }
+                { "-" *
+                  t #2 global.max$ substring$ 't :=
+                }
+              while$
+            }
+          if$
+        }
+        { t #1 #1 substring$ *
+          t #2 global.max$ substring$ 't :=
+        }
+      if$
+    }
+  while$
+}
+
+
+% This function detects entries with names that are identical to that of
+% the previous entry and replaces the repeated names with dashes (if the
+% "is.dash.repeated.names" user control is nonzero).
+FUNCTION {name.or.dash}
+{ 's :=
+   oldname empty$
+     { s 'oldname := s }
+     { s oldname =
+         { is.dash.repeated.names
+              { repeated.name.dashes }
+              { s 'oldname := s }
+            if$
+         }
+         { s 'oldname := s }
+       if$
+     }
+   if$
+}
+
+% Converts the number string on the top of the stack to
+% "numerical ordinal form" (e.g., "7" to "7th"). There is
+% no artificial limit to the upper bound of the numbers as the
+% two least significant digits determine the ordinal form.
+FUNCTION {num.to.ordinal}
+{ duplicate$ #-2 #1 substring$ "1" =
+      { bbl.th * }
+      { duplicate$ #-1 #1 substring$ "1" =
+          { bbl.st * }
+          { duplicate$ #-1 #1 substring$ "2" =
+              { bbl.nd * }
+              { duplicate$ #-1 #1 substring$ "3" =
+                  { bbl.rd * }
+                  { bbl.th * }
+                if$
+              }
+            if$
+          }
+        if$
+      }
+    if$
+}
+
+% If the string on the top of the stack begins with a number,
+% (e.g., 11th) then replace the string with the leading number
+% it contains. Otherwise retain the string as-is. s holds the
+% extracted number, t holds the part of the string that remains
+% to be scanned.
+FUNCTION {extract.num}
+{ duplicate$ 't :=
+  "" 's :=
+  { t empty$ not }
+  { t #1 #1 substring$
+    t #2 global.max$ substring$ 't :=
+    duplicate$ is.num
+      { s swap$ * 's := }
+      { pop$ "" 't := }
+    if$
+  }
+  while$
+  s empty$
+    'skip$
+    { pop$ s }
+  if$
+}
+
+% Converts the word number string on the top of the stack to
+% Arabic string form. Will be successful up to "tenth".
+FUNCTION {word.to.num}
+{ duplicate$ "l" change.case$ 's :=
+  s "first" =
+    { pop$ "1" }
+    { skip$ }
+  if$
+  s "second" =
+    { pop$ "2" }
+    { skip$ }
+  if$
+  s "third" =
+    { pop$ "3" }
+    { skip$ }
+  if$
+  s "fourth" =
+    { pop$ "4" }
+    { skip$ }
+  if$
+  s "fifth" =
+    { pop$ "5" }
+    { skip$ }
+  if$
+  s "sixth" =
+    { pop$ "6" }
+    { skip$ }
+  if$
+  s "seventh" =
+    { pop$ "7" }
+    { skip$ }
+  if$
+  s "eighth" =
+    { pop$ "8" }
+    { skip$ }
+  if$
+  s "ninth" =
+    { pop$ "9" }
+    { skip$ }
+  if$
+  s "tenth" =
+    { pop$ "10" }
+    { skip$ }
+  if$
+}
+
+
+% Converts the string on the top of the stack to numerical
+% ordinal (e.g., "11th") form.
+FUNCTION {convert.edition}
+{ duplicate$ empty$ 'skip$
+    { duplicate$ #1 #1 substring$ is.num
+        { extract.num
+          num.to.ordinal
+        }
+        { word.to.num
+          duplicate$ #1 #1 substring$ is.num
+            { num.to.ordinal }
+            { "edition ordinal word " quote$ * edition * quote$ *
+              " may be too high (or improper) for conversion" * " in " * cite$ * warning$
+            }
+          if$
+        }
+      if$
+    }
+  if$
+}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LATEX BIBLIOGRAPHY CODE %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FUNCTION {start.entry}
+{ newline$
+  "\bibitem{" write$
+  cite$ write$
+  "}" write$
+  newline$
+  ""
+  initialize.prev.this.status
+}
+
+% Here we write out all the LaTeX code that we will need. The most involved
+% code sequences are those that control the alternate interword spacing and
+% foreign language hyphenation patterns. The heavy use of \providecommand
+% gives users a way to override the defaults. Special thanks to Javier Bezos,
+% Johannes Braams, Robin Fairbairns, Heiko Oberdiek, Donald Arseneau and all
+% the other gurus on comp.text.tex for their help and advice on the topic of
+% \selectlanguage, Babel and BibTeX.
+FUNCTION {begin.bib}
+{ "% Generated by IEEEtran.bst, version: " bst.file.version * " (" * bst.file.date * ")" *
+  write$ newline$
+  preamble$ empty$ 'skip$
+    { preamble$ write$ newline$ }
+  if$
+  "\begin{thebibliography}{"  longest.label  * "}" *
+  write$ newline$
+  "\providecommand{\url}[1]{#1}"
+  write$ newline$
+  "\csname url@samestyle\endcsname"
+  write$ newline$
+  "\providecommand{\newblock}{\relax}"
+  write$ newline$
+  "\providecommand{\bibinfo}[2]{#2}"
+  write$ newline$
+  "\providecommand{\BIBentrySTDinterwordspacing}{\spaceskip=0pt\relax}"
+  write$ newline$
+  "\providecommand{\BIBentryALTinterwordstretchfactor}{"
+  ALTinterwordstretchfactor * "}" *
+  write$ newline$
+  "\providecommand{\BIBentryALTinterwordspacing}{\spaceskip=\fontdimen2\font plus "
+  write$ newline$
+  "\BIBentryALTinterwordstretchfactor\fontdimen3\font minus \fontdimen4\font\relax}"
+  write$ newline$
+  "\providecommand{\BIBforeignlanguage}[2]{{%"
+  write$ newline$
+  "\expandafter\ifx\csname l@#1\endcsname\relax"
+  write$ newline$
+  "\typeout{** WARNING: IEEEtran.bst: No hyphenation pattern has been}%"
+  write$ newline$
+  "\typeout{** loaded for the language `#1'. Using the pattern for}%"
+  write$ newline$
+  "\typeout{** the default language instead.}%"
+  write$ newline$
+  "\else"
+  write$ newline$
+  "\language=\csname l@#1\endcsname"
+  write$ newline$
+  "\fi"
+  write$ newline$
+  "#2}}"
+  write$ newline$
+  "\providecommand{\BIBdecl}{\relax}"
+  write$ newline$
+  "\BIBdecl"
+  write$ newline$
+}
+
+FUNCTION {end.bib}
+{ newline$ "\end{thebibliography}" write$ newline$ }
+
+FUNCTION {if.url.alt.interword.spacing}
+{ is.use.alt.interword.spacing 
+     {url empty$ 'skip$ {"\BIBentryALTinterwordspacing" write$ newline$} if$}
+     { skip$ }
+   if$
+}
+
+FUNCTION {if.url.std.interword.spacing}
+{ is.use.alt.interword.spacing 
+     {url empty$ 'skip$ {"\BIBentrySTDinterwordspacing" write$ newline$} if$}
+     { skip$ }
+   if$
+}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%
+%% LONGEST LABEL PASS %%
+%%%%%%%%%%%%%%%%%%%%%%%%
+
+FUNCTION {initialize.longest.label}
+{ "" 'longest.label :=
+  #1 'number.label :=
+  #0 'longest.label.width :=
+}
+
+FUNCTION {longest.label.pass}
+{ type$ "ieeetranbstctl" =
+    { skip$ }
+    { number.label int.to.str$ 'label :=
+      number.label #1 + 'number.label :=
+      label width$ longest.label.width >
+        { label 'longest.label :=
+          label width$ 'longest.label.width :=
+        }
+        { skip$ }
+      if$
+    }
+  if$
+}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%
+%% FORMAT HANDLERS %%
+%%%%%%%%%%%%%%%%%%%%%
+
+%% Lower Level Formats (used by higher level formats)
+
+FUNCTION {format.address.org.or.pub.date}
+{ 't :=
+  ""
+  year empty$
+    { "empty year in " cite$ * warning$ }
+    { skip$ }
+  if$
+  address empty$ t empty$ and
+  year empty$ and month empty$ and
+    { skip$ }
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+      address "address" bibinfo.check *
+      t empty$
+        { skip$ }
+        { punct.period 'prev.status.punct :=
+          space.large 'prev.status.space :=
+          address empty$
+            { skip$ }
+            { ": " * }
+          if$
+          t *
+        }
+      if$
+      year empty$ month empty$ and
+        { skip$ }
+        { t empty$ address empty$ and
+            { skip$ }
+            { ", " * }
+          if$
+          month empty$
+            { year empty$
+                { skip$ }
+                { year "year" bibinfo.check * }
+              if$
+            }
+            { month "month" bibinfo.check *
+              year empty$
+                 { skip$ }
+                 { " " * year "year" bibinfo.check * }
+              if$
+            }
+          if$
+        }
+      if$
+    }
+  if$
+}
+
+
+FUNCTION {format.names}
+{ 'bibinfo :=
+  duplicate$ empty$ 'skip$ {
+  this.to.prev.status
+  this.status.std
+  's :=
+  "" 't :=
+  #1 'nameptr :=
+  s num.names$ 'numnames :=
+  numnames 'namesleft :=
+    { namesleft #0 > }
+    { s nameptr
+      name.format.string
+      format.name$
+      bibinfo bibinfo.check
+      't :=
+      nameptr #1 >
+        { nameptr num.names.shown.with.forced.et.al #1 + =
+          numnames max.num.names.before.forced.et.al >
+          is.forced.et.al and and
+            { "others" 't :=
+              #1 'namesleft :=
+            }
+            { skip$ }
+          if$
+          namesleft #1 >
+            { ", " * t do.name.latex.cmd * }
+            { s nameptr "{ll}" format.name$ duplicate$ "others" =
+                { 't := }
+                { pop$ }
+              if$
+              t "others" =
+                { " " * bbl.etal emphasize * }
+                { numnames #2 >
+                    { "," * }
+                    { skip$ }
+                  if$
+                  bbl.and
+                  space.word * t do.name.latex.cmd *
+                }
+              if$
+            }
+          if$
+        }
+        { t do.name.latex.cmd }
+      if$
+      nameptr #1 + 'nameptr :=
+      namesleft #1 - 'namesleft :=
+    }
+  while$
+  cap.status.std
+  } if$
+}
+
+
+
+
+%% Higher Level Formats
+
+%% addresses/locations
+
+FUNCTION {format.address}
+{ address duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+    }
+  if$
+}
+
+
+
+%% author/editor names
+
+FUNCTION {format.authors}{ author "author" format.names }
+
+FUNCTION {format.editors}
+{ editor "editor" format.names duplicate$ empty$ 'skip$
+    { ", " *
+      get.bbl.editor
+      capitalize
+      *
+    }
+  if$
+}
+
+
+
+%% date
+
+FUNCTION {format.date}
+{
+  month "month" bibinfo.check duplicate$ empty$
+  year  "year" bibinfo.check duplicate$ empty$
+    { swap$ 'skip$
+        { this.to.prev.status
+          this.status.std
+          cap.status.std
+         "there's a month but no year in " cite$ * warning$ }
+      if$
+      *
+    }
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+      swap$ 'skip$
+        {
+          swap$
+          " " * swap$
+        }
+      if$
+      *
+    }
+  if$
+}
+
+FUNCTION {format.date.electronic}
+{ month "month" bibinfo.check duplicate$ empty$
+  year  "year" bibinfo.check duplicate$ empty$
+    { swap$ 
+        { pop$ }
+        { "there's a month but no year in " cite$ * warning$
+        pop$ ")" * "(" swap$ *
+        this.to.prev.status
+        punct.no 'this.status.punct :=
+        space.normal 'this.status.space :=
+        quote.no 'this.status.quote :=
+        cap.yes  'status.cap :=
+        }
+      if$
+    }
+    { swap$ 
+        { swap$ pop$ ")" * "(" swap$ * }
+        { "(" swap$ * ", " * swap$ * ")" * }
+      if$
+    this.to.prev.status
+    punct.no 'this.status.punct :=
+    space.normal 'this.status.space :=
+    quote.no 'this.status.quote :=
+    cap.yes  'status.cap :=
+    }
+  if$
+}
+
+
+
+%% edition/title
+
+% Note: IEEE considers the edition to be closely associated with
+% the title of a book. So, in IEEEtran.bst the edition is normally handled 
+% within the formatting of the title. The format.edition function is 
+% retained here for possible future use.
+FUNCTION {format.edition}
+{ edition duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      convert.edition
+      status.cap
+        { "t" }
+        { "l" }
+      if$ change.case$
+      "edition" bibinfo.check
+      "~" * bbl.edition *
+      cap.status.std
+    }
+  if$
+}
+
+% This is used to format the booktitle of a conference proceedings.
+% Here we use the "intype" field to provide the user a way to 
+% override the word "in" (e.g., with things like "presented at")
+% Use of intype stops the emphasis of the booktitle to indicate that
+% we no longer mean the written conference proceedings, but the
+% conference itself.
+FUNCTION {format.in.booktitle}
+{ booktitle "booktitle" bibinfo.check duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      select.language
+      intype missing$
+        { emphasize
+          bbl.in " " *
+        }
+        { intype " " * }
+      if$
+      swap$ *
+      cap.status.std
+    }
+  if$
+}
+
+% This is used to format the booktitle of collection.
+% Here the "intype" field is not supported, but "edition" is.
+FUNCTION {format.in.booktitle.edition}
+{ booktitle "booktitle" bibinfo.check duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      select.language
+      emphasize
+      edition empty$ 'skip$
+        { ", " *
+          edition
+          convert.edition
+          "l" change.case$
+          * "~" * bbl.edition *
+        }
+      if$
+      bbl.in " " * swap$ *
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.article.title}
+{ title duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      "t" change.case$
+    }
+  if$
+  "title" bibinfo.check
+  duplicate$ empty$ 'skip$
+    { quote.close 'this.status.quote :=
+      is.last.char.not.punct
+        { punct.std 'this.status.punct := }
+        { punct.no 'this.status.punct := }
+      if$
+      select.language
+      "``" swap$ *
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.article.title.electronic}
+{ title duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+      "t" change.case$ 
+    }
+  if$
+  "title" bibinfo.check
+  duplicate$ empty$ 
+    { skip$ } 
+    { select.language }
+  if$
+}
+
+FUNCTION {format.book.title.edition}
+{ title "title" bibinfo.check
+  duplicate$ empty$
+    { "empty title in " cite$ * warning$ }
+    { this.to.prev.status
+      this.status.std
+      select.language
+      emphasize
+      edition empty$ 'skip$
+        { ", " *
+          edition
+          convert.edition
+          status.cap
+            { "t" }
+            { "l" }
+          if$
+          change.case$
+          * "~" * bbl.edition *
+        }
+      if$
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.book.title}
+{ title "title" bibinfo.check
+  duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+      select.language
+      emphasize
+    }
+  if$
+}
+
+
+
+%% journal
+
+FUNCTION {format.journal}
+{ journal duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+      select.language
+      emphasize
+    }
+  if$
+}
+
+
+
+%% how published
+
+FUNCTION {format.howpublished}
+{ howpublished duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+    }
+  if$
+}
+
+
+
+%% institutions/organization/publishers/school
+
+FUNCTION {format.institution}
+{ institution duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.organization}
+{ organization duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.address.publisher.date}
+{ publisher "publisher" bibinfo.warn format.address.org.or.pub.date }
+
+FUNCTION {format.address.publisher.date.nowarn}
+{ publisher "publisher" bibinfo.check format.address.org.or.pub.date }
+
+FUNCTION {format.address.organization.date}
+{ organization "organization" bibinfo.check format.address.org.or.pub.date }
+
+FUNCTION {format.school}
+{ school duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      cap.status.std
+    }
+  if$
+}
+
+
+
+%% volume/number/series/chapter/pages
+
+FUNCTION {format.volume}
+{ volume empty.field.to.null.string
+  duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      bbl.volume 
+      status.cap
+        { capitalize }
+        { skip$ }
+      if$
+      swap$ tie.or.space.prefix
+      "volume" bibinfo.check
+      * *
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.number}
+{ number empty.field.to.null.string
+  duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      status.cap
+         { bbl.number capitalize }
+         { bbl.number }
+       if$
+      swap$ tie.or.space.prefix
+      "number" bibinfo.check
+      * *
+      cap.status.std
+    }
+  if$
+}
+
+FUNCTION {format.number.if.use.for.article}
+{ is.use.number.for.article 
+     { format.number }
+     { "" }
+   if$
+}
+
+% IEEE does not seem to tie the series so closely with the volume
+% and number as is done in other bibliography styles. Instead the
+% series is treated somewhat like an extension of the title.
+FUNCTION {format.series}
+{ series empty$ 
+   { "" }
+   { this.to.prev.status
+     this.status.std
+     bbl.series " " *
+     series "series" bibinfo.check *
+     cap.status.std
+   }
+ if$
+}
+
+
+FUNCTION {format.chapter}
+{ chapter empty$
+    { "" }
+    { this.to.prev.status
+      this.status.std
+      type empty$
+        { bbl.chapter }
+        { type "l" change.case$
+          "type" bibinfo.check
+        }
+      if$
+      chapter tie.or.space.prefix
+      "chapter" bibinfo.check
+      * *
+      cap.status.std
+    }
+  if$
+}
+
+
+% The intended use of format.paper is for paper numbers of inproceedings.
+% The paper type can be overridden via the type field.
+% We allow the type to be displayed even if the paper number is absent
+% for things like "postdeadline paper"
+FUNCTION {format.paper}
+{ is.use.paper
+     { paper empty$
+        { type empty$
+            { "" }
+            { this.to.prev.status
+              this.status.std
+              type "type" bibinfo.check
+              cap.status.std
+            }
+          if$
+        }
+        { this.to.prev.status
+          this.status.std
+          type empty$
+            { bbl.paper }
+            { type "type" bibinfo.check }
+          if$
+          " " * paper
+          "paper" bibinfo.check
+          *
+          cap.status.std
+        }
+      if$
+     }
+     { "" } 
+   if$
+}
+
+
+FUNCTION {format.pages}
+{ pages duplicate$ empty$ 'skip$
+    { this.to.prev.status
+      this.status.std
+      duplicate$ is.multiple.pages
+        {
+          bbl.pages swap$
+          n.dashify
+        }
+        {
+          bbl.page swap$
+        }
+      if$
+      tie.or.space.prefix
+      "pages" bibinfo.check
+      * *
+      cap.status.std
+    }
+  if$
+}
+
+
+
+%% technical report number
+
+FUNCTION {format.tech.report.number}
+{ number "number" bibinfo.check
+  this.to.prev.status
+  this.status.std
+  cap.status.std
+  type duplicate$ empty$
+    { pop$ 
+      bbl.techrep
+    }
+    { skip$ }
+  if$
+  "type" bibinfo.check 
+  swap$ duplicate$ empty$
+    { pop$ }
+    { tie.or.space.prefix * * }
+  if$
+}
+
+
+
+%% note
+
+FUNCTION {format.note}
+{ note empty$
+    { "" }
+    { this.to.prev.status
+      this.status.std
+      punct.period 'this.status.punct :=
+      note #1 #1 substring$
+      duplicate$ "{" =
+        { skip$ }
+        { status.cap
+          { "u" }
+          { "l" }
+        if$
+        change.case$
+        }
+      if$
+      note #2 global.max$ substring$ * "note" bibinfo.check
+      cap.yes  'status.cap :=
+    }
+  if$
+}
+
+
+
+%% patent
+
+FUNCTION {format.patent.date}
+{ this.to.prev.status
+  this.status.std
+  year empty$
+    { monthfiled duplicate$ empty$
+        { "monthfiled" bibinfo.check pop$ "" }
+        { "monthfiled" bibinfo.check }
+      if$
+      dayfiled duplicate$ empty$
+        { "dayfiled" bibinfo.check pop$ "" * }
+        { "dayfiled" bibinfo.check 
+          monthfiled empty$ 
+             { "dayfiled without a monthfiled in " cite$ * warning$
+               * 
+             }
+             { " " swap$ * * }
+           if$
+        }
+      if$
+      yearfiled empty$
+        { "no year or yearfiled in " cite$ * warning$ }
+        { yearfiled "yearfiled" bibinfo.check 
+          swap$
+          duplicate$ empty$
+             { pop$ }
+             { ", " * swap$ * }
+           if$
+        }
+      if$
+    }
+    { month duplicate$ empty$
+        { "month" bibinfo.check pop$ "" }
+        { "month" bibinfo.check }
+      if$
+      day duplicate$ empty$
+        { "day" bibinfo.check pop$ "" * }
+        { "day" bibinfo.check 
+          month empty$ 
+             { "day without a month in " cite$ * warning$
+               * 
+             }
+             { " " swap$ * * }
+           if$
+        }
+      if$
+      year "year" bibinfo.check 
+      swap$
+      duplicate$ empty$
+        { pop$ }
+        { ", " * swap$ * }
+      if$
+    }
+  if$
+  cap.status.std
+}
+
+FUNCTION {format.patent.nationality.type.number}
+{ this.to.prev.status
+  this.status.std
+  nationality duplicate$ empty$
+    { "nationality" bibinfo.warn pop$ "" }
+    { "nationality" bibinfo.check
+      duplicate$ "l" change.case$ "united states" =
+        { pop$ bbl.patentUS }
+        { skip$ }
+      if$
+      " " *
+    }
+  if$
+  type empty$
+    { bbl.patent "type" bibinfo.check }
+    { type "type" bibinfo.check }
+  if$  
+  *
+  number duplicate$ empty$
+    { "number" bibinfo.warn pop$ }
+    { "number" bibinfo.check
+      large.number.separate
+      swap$ " " * swap$ *
+    }
+  if$ 
+  cap.status.std
+}
+
+
+
+%% standard
+
+FUNCTION {format.organization.institution.standard.type.number}
+{ this.to.prev.status
+  this.status.std
+  organization duplicate$ empty$
+    { pop$ 
+      institution duplicate$ empty$
+        { "institution" bibinfo.warn }
+        { "institution" bibinfo.warn " " * }
+      if$
+    }
+    { "organization" bibinfo.warn " " * }
+  if$
+  type empty$
+    { bbl.standard "type" bibinfo.check }
+    { type "type" bibinfo.check }
+  if$  
+  *
+  number duplicate$ empty$
+    { "number" bibinfo.check pop$ }
+    { "number" bibinfo.check
+      large.number.separate
+      swap$ " " * swap$ *
+    }
+  if$ 
+  cap.status.std
+}
+
+FUNCTION {format.revision}
+{ revision empty$
+    { "" }
+    { this.to.prev.status
+      this.status.std
+      bbl.revision
+      revision tie.or.space.prefix
+      "revision" bibinfo.check
+      * *
+      cap.status.std
+    }
+  if$
+}
+
+
+%% thesis
+
+FUNCTION {format.master.thesis.type}
+{ this.to.prev.status
+  this.status.std
+  type empty$
+    {
+      bbl.mthesis
+    }
+    { 
+      type "type" bibinfo.check
+    }
+  if$
+cap.status.std
+}
+
+FUNCTION {format.phd.thesis.type}
+{ this.to.prev.status
+  this.status.std
+  type empty$
+    {
+      bbl.phdthesis
+    }
+    { 
+      type "type" bibinfo.check
+    }
+  if$
+cap.status.std
+}
+
+
+
+%% URL
+
+FUNCTION {format.url}
+{ url empty$
+    { "" }
+    { this.to.prev.status
+      this.status.std
+      cap.yes 'status.cap :=
+      name.url.prefix " " *
+      "\url{" * url * "}" *
+      punct.no 'this.status.punct :=
+      punct.period 'prev.status.punct :=
+      space.normal 'this.status.space :=
+      space.normal 'prev.status.space :=
+      quote.no 'this.status.quote :=
+    }
+  if$
+}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%
+%% ENTRY HANDLERS %%
+%%%%%%%%%%%%%%%%%%%%
+
+
+% Note: In many journals, IEEE (or the authors) tend not to show the number
+% for articles, so the display of the number is controlled here by the
+% switch "is.use.number.for.article"
+FUNCTION {article}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.journal "journal" bibinfo.check "journal" output.warn
+  format.volume output
+  format.number.if.use.for.article output
+  format.pages output
+  format.date "year" output.warn
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {book}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  author empty$
+    { format.editors "author and editor" output.warn }
+    { format.authors output.nonnull }
+  if$
+  name.or.dash
+  format.book.title.edition output
+  format.series output
+  author empty$
+    { skip$ }
+    { format.editors output }
+  if$
+  format.address.publisher.date output
+  format.volume output
+  format.number output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {booklet}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.article.title "title" output.warn
+  format.howpublished "howpublished" bibinfo.check output
+  format.organization "organization" bibinfo.check output
+  format.address "address" bibinfo.check output
+  format.date output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {electronic}
+{ std.status.using.period
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.date.electronic output
+  format.article.title.electronic output
+  format.howpublished "howpublished" bibinfo.check output
+  format.organization "organization" bibinfo.check output
+  format.address "address" bibinfo.check output
+  format.note output
+  format.url output
+  fin.entry
+  empty.entry.warn
+  if.url.std.interword.spacing
+}
+
+FUNCTION {inbook}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  author empty$
+    { format.editors "author and editor" output.warn }
+    { format.authors output.nonnull }
+  if$
+  name.or.dash
+  format.book.title.edition output
+  format.series output
+  format.address.publisher.date output
+  format.volume output
+  format.number output
+  format.chapter output
+  format.pages output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {incollection}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.in.booktitle.edition "booktitle" output.warn
+  format.series output
+  format.editors output
+  format.address.publisher.date.nowarn output
+  format.volume output
+  format.number output
+  format.chapter output
+  format.pages output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {inproceedings}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.in.booktitle "booktitle" output.warn
+  format.series output
+  format.editors output
+  format.volume output
+  format.number output
+  publisher empty$
+    { format.address.organization.date output }
+    { format.organization "organization" bibinfo.check output
+      format.address.publisher.date output
+    }
+  if$
+  format.paper output
+  format.pages output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {manual}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.book.title.edition "title" output.warn
+  format.howpublished "howpublished" bibinfo.check output 
+  format.organization "organization" bibinfo.check output
+  format.address "address" bibinfo.check output
+  format.date output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {mastersthesis}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.master.thesis.type output.nonnull
+  format.school "school" bibinfo.warn output
+  format.address "address" bibinfo.check output
+  format.date "year" output.warn
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {misc}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.article.title output
+  format.howpublished "howpublished" bibinfo.check output 
+  format.organization "organization" bibinfo.check output
+  format.address "address" bibinfo.check output
+  format.pages output
+  format.date output
+  format.note output
+  format.url output
+  fin.entry
+  empty.entry.warn
+  if.url.std.interword.spacing
+}
+
+FUNCTION {patent}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.article.title output
+  format.patent.nationality.type.number output
+  format.patent.date output
+  format.note output
+  format.url output
+  fin.entry
+  empty.entry.warn
+  if.url.std.interword.spacing
+}
+
+FUNCTION {periodical}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.editors output
+  name.or.dash
+  format.book.title "title" output.warn
+  format.series output
+  format.volume output
+  format.number output
+  format.organization "organization" bibinfo.check output
+  format.date "year" output.warn
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {phdthesis}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.phd.thesis.type output.nonnull
+  format.school "school" bibinfo.warn output
+  format.address "address" bibinfo.check output
+  format.date "year" output.warn
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {proceedings}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.editors output
+  name.or.dash
+  format.book.title "title" output.warn
+  format.series output
+  format.volume output
+  format.number output
+  publisher empty$
+    { format.address.organization.date output }
+    { format.organization "organization" bibinfo.check output
+      format.address.publisher.date output
+    }
+  if$
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {standard}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors output
+  name.or.dash
+  format.book.title "title" output.warn
+  format.howpublished "howpublished" bibinfo.check output 
+  format.organization.institution.standard.type.number output
+  format.revision output
+  format.date output
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {techreport}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.howpublished "howpublished" bibinfo.check output 
+  format.institution "institution" bibinfo.warn output
+  format.address "address" bibinfo.check output
+  format.tech.report.number output.nonnull
+  format.date "year" output.warn
+  format.note output
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+FUNCTION {unpublished}
+{ std.status.using.comma
+  start.entry
+  if.url.alt.interword.spacing
+  format.authors "author" output.warn
+  name.or.dash
+  format.article.title "title" output.warn
+  format.date output
+  format.note "note" output.warn
+  format.url output
+  fin.entry
+  if.url.std.interword.spacing
+}
+
+
+% The special entry type which provides the user interface to the
+% BST controls
+FUNCTION {IEEEtranBSTCTL}
+{ is.print.banners.to.terminal
+    { "** IEEEtran BST control entry " quote$ * cite$ * quote$ * " detected." *
+      top$
+    }
+    { skip$ }
+  if$
+  CTLuse_article_number
+  empty$
+    { skip$ }
+    { CTLuse_article_number
+      yes.no.to.int
+      'is.use.number.for.article :=
+    }
+  if$
+  CTLuse_paper
+  empty$
+    { skip$ }
+    { CTLuse_paper
+      yes.no.to.int
+      'is.use.paper :=
+    }
+  if$
+  CTLuse_forced_etal
+  empty$
+    { skip$ }
+    { CTLuse_forced_etal
+      yes.no.to.int
+      'is.forced.et.al :=
+    }
+  if$
+  CTLmax_names_forced_etal
+  empty$
+    { skip$ }
+    { CTLmax_names_forced_etal
+      string.to.integer
+      'max.num.names.before.forced.et.al :=
+    }
+  if$
+  CTLnames_show_etal
+  empty$
+    { skip$ }
+    { CTLnames_show_etal
+      string.to.integer
+      'num.names.shown.with.forced.et.al :=
+    }
+  if$
+  CTLuse_alt_spacing
+  empty$
+    { skip$ }
+    { CTLuse_alt_spacing
+      yes.no.to.int
+      'is.use.alt.interword.spacing :=
+    }
+  if$
+  CTLalt_stretch_factor
+  empty$
+    { skip$ }
+    { CTLalt_stretch_factor
+      'ALTinterwordstretchfactor :=
+      "\renewcommand{\BIBentryALTinterwordstretchfactor}{"
+      ALTinterwordstretchfactor * "}" *
+      write$ newline$
+    }
+  if$
+  CTLdash_repeated_names
+  empty$
+    { skip$ }
+    { CTLdash_repeated_names
+      yes.no.to.int
+      'is.dash.repeated.names :=
+    }
+  if$
+  CTLname_format_string
+  empty$
+    { skip$ }
+    { CTLname_format_string
+      'name.format.string :=
+    }
+  if$
+  CTLname_latex_cmd
+  empty$
+    { skip$ }
+    { CTLname_latex_cmd
+      'name.latex.cmd :=
+    }
+  if$
+  CTLname_url_prefix
+  missing$
+    { skip$ }
+    { CTLname_url_prefix
+      'name.url.prefix :=
+    }
+  if$
+
+
+  num.names.shown.with.forced.et.al max.num.names.before.forced.et.al >
+    { "CTLnames_show_etal cannot be greater than CTLmax_names_forced_etal in " cite$ * warning$ 
+      max.num.names.before.forced.et.al 'num.names.shown.with.forced.et.al :=
+    }
+    { skip$ }
+  if$
+}
+
+
+%%%%%%%%%%%%%%%%%%%
+%% ENTRY ALIASES %%
+%%%%%%%%%%%%%%%%%%%
+FUNCTION {conference}{inproceedings}
+FUNCTION {online}{electronic}
+FUNCTION {internet}{electronic}
+FUNCTION {webpage}{electronic}
+FUNCTION {www}{electronic}
+FUNCTION {default.type}{misc}
+
+
+
+%%%%%%%%%%%%%%%%%%
+%% MAIN PROGRAM %%
+%%%%%%%%%%%%%%%%%%
+
+READ
+
+EXECUTE {initialize.controls}
+EXECUTE {initialize.status.constants}
+EXECUTE {banner.message}
+
+EXECUTE {initialize.longest.label}
+ITERATE {longest.label.pass}
+
+EXECUTE {begin.bib}
+ITERATE {call.type$}
+EXECUTE {end.bib}
+
+EXECUTE{completed.message}
+
+
+%% That's all folks, mds.
diff --git a/src/libswift_udp/doc/mfold-article/IEEEtran.cls b/src/libswift_udp/doc/mfold-article/IEEEtran.cls
new file mode 100644 (file)
index 0000000..5681714
--- /dev/null
@@ -0,0 +1,4702 @@
+%%
+%% IEEEtran.cls 2007/03/05 version V1.7a
+%% 
+%% 
+%% This is the official IEEE LaTeX class for authors of the Institute of 
+%% Electrical and Electronics Engineers (IEEE) Transactions journals and
+%% conferences.
+%% 
+%% Support sites:
+%% http://www.michaelshell.org/tex/ieeetran/
+%% http://www.ctan.org/tex-archive/macros/latex/contrib/IEEEtran/
+%% and
+%% http://www.ieee.org/
+%%
+%% Based on the original 1993 IEEEtran.cls, but with many bug fixes
+%% and enhancements (from both JVH and MDS) over the 1996/7 version.
+%%
+%%
+%% Contributors:
+%% Gerry Murray (1993), Silvano Balemi (1993),
+%% Jon Dixon (1996), Peter N"uchter (1996),
+%% Juergen von Hagen (2000), and Michael Shell (2001-2007)
+%% 
+%% 
+%% Copyright (c) 1993-2000 by Gerry Murray, Silvano Balemi, 
+%%                         Jon Dixon, Peter N"uchter,
+%%                         Juergen von Hagen
+%%                         and
+%% Copyright (c) 2001-2007 by Michael Shell
+%%
+%% Current maintainer (V1.3 to V1.7): Michael Shell
+%%                                    See:
+%%                                    http://www.michaelshell.org/
+%%                                    for current contact information.
+%%
+%% Special thanks to Peter Wilson (CUA) and Donald Arseneau
+%% for allowing the inclusion of the \@ifmtarg command 
+%% from their ifmtarg LaTeX package. 
+%% 
+%%*************************************************************************
+%% Legal Notice:
+%% This code is offered as-is without any warranty either expressed or
+%% implied; without even the implied warranty of MERCHANTABILITY or
+%% FITNESS FOR A PARTICULAR PURPOSE! 
+%% User assumes all risk.
+%% In no event shall IEEE or any contributor to this code be liable for
+%% any damages or losses, including, but not limited to, incidental,
+%% consequential, or any other damages, resulting from the use or misuse
+%% of any information contained here.
+%%
+%% All comments are the opinions of their respective authors and are not
+%% necessarily endorsed by the IEEE.
+%%
+%% This work is distributed under the LaTeX Project Public License (LPPL)
+%% ( http://www.latex-project.org/ ) version 1.3, and may be freely used,
+%% distributed and modified. A copy of the LPPL, version 1.3, is included
+%% in the base LaTeX documentation of all distributions of LaTeX released
+%% 2003/12/01 or later.
+%% Retain all contribution notices and credits.
+%% ** Modified files should be clearly indicated as such, including  **
+%% ** renaming them and changing author support contact information. **
+%%
+%% File list of work: IEEEtran.cls, IEEEtran_HOWTO.pdf, bare_adv.tex,
+%%                    bare_conf.tex, bare_jrnl.tex, bare_jrnl_compsoc.tex
+%% 
+%% Major changes to the user interface should be indicated by an 
+%% increase in the version numbers. If a version is a beta, it will 
+%% be indicated with a BETA suffix, i.e., 1.4 BETA.
+%% Small changes can be indicated by appending letters to the version
+%% such as "IEEEtran_v14a.cls".
+%% In all cases, \Providesclass, any \typeout messages to the user,
+%% \IEEEtransversionmajor and \IEEEtransversionminor must reflect the
+%% correct version information.
+%% The changes should also be documented via source comments.
+%%*************************************************************************
+%%
+%
+% Available class options 
+% e.g., \documentclass[10pt,conference]{IEEEtran} 
+% 
+%             *** choose only one from each category ***
+%
+% 9pt, 10pt, 11pt, 12pt
+%    Sets normal font size. The default is 10pt.
+% 
+% conference, journal, technote, peerreview, peerreviewca
+%    determines format mode - conference papers, journal papers,
+%    correspondence papers (technotes), or peer review papers. The user
+%    should also select 9pt when using technote. peerreview is like
+%    journal mode, but provides for a single-column "cover" title page for
+%    anonymous peer review. The paper title (without the author names) is
+%    repeated at the top of the page after the cover page. For peer review
+%    papers, the \IEEEpeerreviewmaketitle command must be executed (will
+%    automatically be ignored for non-peerreview modes) at the place the
+%    cover page is to end, usually just after the abstract (keywords are
+%    not normally used with peer review papers). peerreviewca is like
+%    peerreview, but allows the author names to be entered and formatted
+%    as with conference mode so that author affiliation and contact
+%    information can be easily seen on the cover page.
+%    The default is journal.
+%
+% draft, draftcls, draftclsnofoot, final
+%    determines if paper is formatted as a widely spaced draft (for
+%    handwritten editor comments) or as a properly typeset final version.
+%    draftcls restricts draft mode to the class file while all other LaTeX
+%    packages (i.e., \usepackage{graphicx}) will behave as final - allows
+%    for a draft paper with visible figures, etc. draftclsnofoot is like
+%    draftcls, but does not display the date and the word "DRAFT" at the foot
+%    of the pages. If using one of the draft modes, the user will probably
+%    also want to select onecolumn.
+%    The default is final.
+%
+% letterpaper, a4paper
+%    determines paper size: 8.5in X 11in or 210mm X 297mm. CHANGING THE PAPER
+%    SIZE WILL NOT ALTER THE TYPESETTING OF THE DOCUMENT - ONLY THE MARGINS
+%    WILL BE AFFECTED. In particular, documents using the a4paper option will
+%    have reduced side margins (A4 is narrower than US letter) and a longer
+%    bottom margin (A4 is longer than US letter). For both cases, the top
+%    margins will be the same and the text will be horizontally centered. 
+%    For final submission to IEEE, authors should use US letter (8.5 X 11in)
+%    paper. Note that authors should ensure that all post-processing 
+%    (ps, pdf, etc.) uses the same paper specificiation as the .tex document.
+%    Problems here are by far the number one reason for incorrect margins.
+%    IEEEtran will automatically set the default paper size under pdflatex 
+%    (without requiring a change to pdftex.cfg), so this issue is more
+%    important to dvips users. Fix config.ps, config.pdf, or ~/.dvipsrc for
+%    dvips, or use the dvips -t papersize option instead as needed. See the
+%    testflow documentation
+%    http://www.ctan.org/tex-archive/macros/latex/contrib/IEEEtran/testflow
+%    for more details on dvips paper size configuration.
+%    The default is letterpaper.
+%
+% oneside, twoside
+%    determines if layout follows single sided or two sided (duplex)
+%    printing. The only notable change is with the headings at the top of
+%    the pages.
+%    The default is oneside.
+%
+% onecolumn, twocolumn
+%    determines if text is organized into one or two columns per page. One
+%    column mode is usually used only with draft papers.
+%    The default is twocolumn.
+%
+% compsoc
+%    Use the format of the IEEE Computer Society.
+%
+% romanappendices
+%    Use the "Appendix I" convention when numbering appendices. IEEEtran.cls
+%    now defaults to Alpha "Appendix A" convention - the opposite of what
+%    v1.6b and earlier did.
+%
+% captionsoff
+%    disables the display of the figure/table captions. Some IEEE journals
+%    request that captions be removed and figures/tables be put on pages
+%    of their own at the end of an initial paper submission. The endfloat
+%    package can be used with this class option to achieve this format.
+%
+% nofonttune
+%    turns off tuning of the font interword spacing. Maybe useful to those
+%    not using the standard Times fonts or for those who have already "tuned"
+%    their fonts.
+%    The default is to enable IEEEtran to tune font parameters.
+%
+%
+%----------
+% Available CLASSINPUTs provided (all are macros unless otherwise noted):
+% \CLASSINPUTbaselinestretch
+% \CLASSINPUTinnersidemargin
+% \CLASSINPUToutersidemargin
+% \CLASSINPUTtoptextmargin
+% \CLASSINPUTbottomtextmargin
+%
+% Available CLASSINFOs provided:
+% \ifCLASSINFOpdf                       (TeX if conditional)
+% \CLASSINFOpaperwidth                  (macro)
+% \CLASSINFOpaperheight                 (macro)
+% \CLASSINFOnormalsizebaselineskip      (length)
+% \CLASSINFOnormalsizeunitybaselineskip (length)
+%
+% Available CLASSOPTIONs provided:
+% all class option flags (TeX if conditionals) unless otherwise noted,
+% e.g., \ifCLASSOPTIONcaptionsoff
+% point size options provided as a single macro:
+% \CLASSOPTIONpt
+% which will be defined as 9, 10, 11, or 12 depending on the document's
+% normalsize point size.
+% also, class option peerreviewca implies the use of class option peerreview
+% and classoption draft implies the use of class option draftcls
+
+
+
+
+
+\ProvidesClass{IEEEtran}[2007/03/05 V1.7a by Michael Shell]
+\typeout{-- See the "IEEEtran_HOWTO" manual for usage information.}
+\typeout{-- http://www.michaelshell.org/tex/ieeetran/}
+\NeedsTeXFormat{LaTeX2e}
+
+% IEEEtran.cls version numbers, provided as of V1.3
+% These values serve as a way a .tex file can
+% determine if the new features are provided.
+% The version number of this IEEEtrans.cls can be obtained from 
+% these values. i.e., V1.4
+% KEEP THESE AS INTEGERS! i.e., NO {4a} or anything like that-
+% (no need to enumerate "a" minor changes here)
+\def\IEEEtransversionmajor{1}
+\def\IEEEtransversionminor{7}
+
+% These do nothing, but provide them like in article.cls
+\newif\if@restonecol
+\newif\if@titlepage
+
+
+% class option conditionals
+\newif\ifCLASSOPTIONonecolumn       \CLASSOPTIONonecolumnfalse
+\newif\ifCLASSOPTIONtwocolumn       \CLASSOPTIONtwocolumntrue
+
+\newif\ifCLASSOPTIONoneside         \CLASSOPTIONonesidetrue
+\newif\ifCLASSOPTIONtwoside         \CLASSOPTIONtwosidefalse
+
+\newif\ifCLASSOPTIONfinal           \CLASSOPTIONfinaltrue
+\newif\ifCLASSOPTIONdraft           \CLASSOPTIONdraftfalse
+\newif\ifCLASSOPTIONdraftcls        \CLASSOPTIONdraftclsfalse
+\newif\ifCLASSOPTIONdraftclsnofoot  \CLASSOPTIONdraftclsnofootfalse
+
+\newif\ifCLASSOPTIONpeerreview      \CLASSOPTIONpeerreviewfalse
+\newif\ifCLASSOPTIONpeerreviewca    \CLASSOPTIONpeerreviewcafalse
+
+\newif\ifCLASSOPTIONjournal         \CLASSOPTIONjournaltrue
+\newif\ifCLASSOPTIONconference      \CLASSOPTIONconferencefalse
+\newif\ifCLASSOPTIONtechnote        \CLASSOPTIONtechnotefalse
+
+\newif\ifCLASSOPTIONnofonttune      \CLASSOPTIONnofonttunefalse
+
+\newif\ifCLASSOPTIONcaptionsoff     \CLASSOPTIONcaptionsofffalse
+
+\newif\ifCLASSOPTIONcompsoc         \CLASSOPTIONcompsocfalse
+
+\newif\ifCLASSOPTIONromanappendices \CLASSOPTIONromanappendicesfalse
+
+
+% class info conditionals
+
+% indicates if pdf (via pdflatex) output
+\newif\ifCLASSINFOpdf               \CLASSINFOpdffalse
+
+
+% V1.6b internal flag to show if using a4paper
+\newif\if@IEEEusingAfourpaper       \@IEEEusingAfourpaperfalse
+
+
+
+% IEEEtran class scratch pad registers
+% dimen
+\newdimen\@IEEEtrantmpdimenA
+\newdimen\@IEEEtrantmpdimenB
+% count
+\newcount\@IEEEtrantmpcountA
+\newcount\@IEEEtrantmpcountB
+% token list
+\newtoks\@IEEEtrantmptoksA
+
+% we use \CLASSOPTIONpt so that we can ID the point size (even for 9pt docs)
+% as well as LaTeX's \@ptsize to retain some compatability with some
+% external packages
+\def\@ptsize{0}
+% LaTeX does not support 9pt, so we set \@ptsize to 0 - same as that of 10pt
+\DeclareOption{9pt}{\def\CLASSOPTIONpt{9}\def\@ptsize{0}}
+\DeclareOption{10pt}{\def\CLASSOPTIONpt{10}\def\@ptsize{0}}
+\DeclareOption{11pt}{\def\CLASSOPTIONpt{11}\def\@ptsize{1}}
+\DeclareOption{12pt}{\def\CLASSOPTIONpt{12}\def\@ptsize{2}}
+
+
+
+\DeclareOption{letterpaper}{\setlength{\paperheight}{11in}%
+                            \setlength{\paperwidth}{8.5in}%
+                            \@IEEEusingAfourpaperfalse
+                            \def\CLASSOPTIONpaper{letter}%
+                            \def\CLASSINFOpaperwidth{8.5in}%
+                            \def\CLASSINFOpaperheight{11in}}
+
+
+\DeclareOption{a4paper}{\setlength{\paperheight}{297mm}%
+                        \setlength{\paperwidth}{210mm}%
+                        \@IEEEusingAfourpapertrue
+                        \def\CLASSOPTIONpaper{a4}%
+                        \def\CLASSINFOpaperwidth{210mm}%
+                        \def\CLASSINFOpaperheight{297mm}}
+
+\DeclareOption{oneside}{\@twosidefalse\@mparswitchfalse
+                        \CLASSOPTIONonesidetrue\CLASSOPTIONtwosidefalse}
+\DeclareOption{twoside}{\@twosidetrue\@mparswitchtrue
+                        \CLASSOPTIONtwosidetrue\CLASSOPTIONonesidefalse}
+
+\DeclareOption{onecolumn}{\CLASSOPTIONonecolumntrue\CLASSOPTIONtwocolumnfalse}
+\DeclareOption{twocolumn}{\CLASSOPTIONtwocolumntrue\CLASSOPTIONonecolumnfalse}
+
+% If the user selects draft, then this class AND any packages
+% will go into draft mode.
+\DeclareOption{draft}{\CLASSOPTIONdrafttrue\CLASSOPTIONdraftclstrue
+                      \CLASSOPTIONdraftclsnofootfalse} 
+% draftcls is for a draft mode which will not affect any packages
+% used by the document.
+\DeclareOption{draftcls}{\CLASSOPTIONdraftfalse\CLASSOPTIONdraftclstrue
+                         \CLASSOPTIONdraftclsnofootfalse} 
+% draftclsnofoot is like draftcls, but without the footer.
+\DeclareOption{draftclsnofoot}{\CLASSOPTIONdraftfalse\CLASSOPTIONdraftclstrue
+                               \CLASSOPTIONdraftclsnofoottrue} 
+\DeclareOption{final}{\CLASSOPTIONdraftfalse\CLASSOPTIONdraftclsfalse
+                      \CLASSOPTIONdraftclsnofootfalse}
+
+\DeclareOption{journal}{\CLASSOPTIONpeerreviewfalse\CLASSOPTIONpeerreviewcafalse
+                        \CLASSOPTIONjournaltrue\CLASSOPTIONconferencefalse\CLASSOPTIONtechnotefalse}
+
+\DeclareOption{conference}{\CLASSOPTIONpeerreviewfalse\CLASSOPTIONpeerreviewcafalse
+                           \CLASSOPTIONjournalfalse\CLASSOPTIONconferencetrue\CLASSOPTIONtechnotefalse}
+
+\DeclareOption{technote}{\CLASSOPTIONpeerreviewfalse\CLASSOPTIONpeerreviewcafalse
+                         \CLASSOPTIONjournalfalse\CLASSOPTIONconferencefalse\CLASSOPTIONtechnotetrue}
+
+\DeclareOption{peerreview}{\CLASSOPTIONpeerreviewtrue\CLASSOPTIONpeerreviewcafalse
+                           \CLASSOPTIONjournalfalse\CLASSOPTIONconferencefalse\CLASSOPTIONtechnotefalse}
+
+\DeclareOption{peerreviewca}{\CLASSOPTIONpeerreviewtrue\CLASSOPTIONpeerreviewcatrue
+                             \CLASSOPTIONjournalfalse\CLASSOPTIONconferencefalse\CLASSOPTIONtechnotefalse}
+
+\DeclareOption{nofonttune}{\CLASSOPTIONnofonttunetrue}
+
+\DeclareOption{captionsoff}{\CLASSOPTIONcaptionsofftrue}
+
+\DeclareOption{compsoc}{\CLASSOPTIONcompsoctrue}
+
+\DeclareOption{romanappendices}{\CLASSOPTIONromanappendicestrue}
+
+
+% default to US letter paper, 10pt, twocolumn, one sided, final, journal
+\ExecuteOptions{letterpaper,10pt,twocolumn,oneside,final,journal}
+% overrride these defaults per user requests
+\ProcessOptions
+
+
+
+% Computer Society conditional execution command
+\long\def\@IEEEcompsoconly#1{\relax\ifCLASSOPTIONcompsoc\relax#1\relax\fi\relax}
+% inverse
+\long\def\@IEEEnotcompsoconly#1{\relax\ifCLASSOPTIONcompsoc\else\relax#1\relax\fi\relax}
+% compsoc conference
+\long\def\@IEEEcompsocconfonly#1{\relax\ifCLASSOPTIONcompsoc\ifCLASSOPTIONconference\relax#1\relax\fi\fi\relax}
+% compsoc not conference
+\long\def\@IEEEcompsocnotconfonly#1{\relax\ifCLASSOPTIONcompsoc\ifCLASSOPTIONconference\else\relax#1\relax\fi\fi\relax}
+
+
+% IEEE uses Times Roman font, so we'll default to Times.
+% These three commands make up the entire times.sty package.
+\renewcommand{\sfdefault}{phv}
+\renewcommand{\rmdefault}{ptm}
+\renewcommand{\ttdefault}{pcr}
+
+\@IEEEcompsoconly{\typeout{-- Using IEEE Computer Society mode.}}
+
+% V1.7 compsoc nonconference papers, use Palatino/Palladio as the main text font,
+% not Times Roman.
+\@IEEEcompsocnotconfonly{\renewcommand{\rmdefault}{ppl}}
+
+% enable Times/Palatino main text font
+\normalfont\selectfont
+
+
+
+
+
+% V1.7 conference notice message hook
+\def\@IEEEconsolenoticeconference{\typeout{}%
+\typeout{** Conference Paper **}%
+\typeout{Before submitting the final camera ready copy, remember to:}%
+\typeout{}%
+\typeout{ 1. Manually equalize the lengths of two columns on the last page}%
+\typeout{ of your paper;}%
+\typeout{}%
+\typeout{ 2. Ensure that any PostScript and/or PDF output post-processing}%
+\typeout{ uses only Type 1 fonts and that every step in the generation}%
+\typeout{ process uses the appropriate paper size.}%
+\typeout{}}
+
+
+% we can send console reminder messages to the user here
+\AtEndDocument{\ifCLASSOPTIONconference\@IEEEconsolenoticeconference\fi}
+
+
+% warn about the use of single column other than for draft mode
+\ifCLASSOPTIONtwocolumn\else%
+  \ifCLASSOPTIONdraftcls\else%
+   \typeout{** ATTENTION: Single column mode is not typically used with IEEE publications.}%
+  \fi%
+\fi
+
+
+% V1.7 improved paper size setting code.
+% Set pdfpage and dvips paper sizes. Conditional tests are similar to that
+% of ifpdf.sty. Retain within {} to ensure tested macros are never altered,
+% even if only effect is to set them to \relax.
+% if \pdfoutput is undefined or equal to relax, output a dvips special
+{\@ifundefined{pdfoutput}{\AtBeginDvi{\special{papersize=\CLASSINFOpaperwidth,\CLASSINFOpaperheight}}}{%
+% pdfoutput is defined and not equal to \relax
+% check for pdfpageheight existence just in case someone sets pdfoutput
+% under non-pdflatex. If exists, set them regardless of value of \pdfoutput.
+\@ifundefined{pdfpageheight}{\relax}{\global\pdfpagewidth\paperwidth
+\global\pdfpageheight\paperheight}%
+% if using \pdfoutput=0 under pdflatex, send dvips papersize special
+\ifcase\pdfoutput
+\AtBeginDvi{\special{papersize=\CLASSINFOpaperwidth,\CLASSINFOpaperheight}}%
+\else
+% we are using pdf output, set CLASSINFOpdf flag
+\global\CLASSINFOpdftrue
+\fi}}
+
+% let the user know the selected papersize
+\typeout{-- Using \CLASSINFOpaperwidth\space x \CLASSINFOpaperheight\space
+(\CLASSOPTIONpaper)\space paper.}
+
+\ifCLASSINFOpdf
+\typeout{-- Using PDF output.}
+\else
+\typeout{-- Using DVI output.}
+\fi
+
+
+% The idea hinted here is for LaTeX to generate markleft{} and markright{}
+% automatically for you after you enter \author{}, \journal{},
+% \journaldate{}, journalvol{}, \journalnum{}, etc.
+% However, there may be some backward compatibility issues here as
+% well as some special applications for IEEEtran.cls and special issues
+% that may require the flexible \markleft{}, \markright{} and/or \markboth{}.
+% We'll leave this as an open future suggestion.
+%\newcommand{\journal}[1]{\def\@journal{#1}}
+%\def\@journal{}
+
+
+
+% pointsize values
+% used with ifx to determine the document's normal size
+\def\@IEEEptsizenine{9}
+\def\@IEEEptsizeten{10}
+\def\@IEEEptsizeeleven{11}
+\def\@IEEEptsizetwelve{12}
+
+
+
+% FONT DEFINITIONS (No sizexx.clo file needed) 
+% V1.6 revised font sizes, displayskip values and
+%      revised normalsize baselineskip to reduce underfull vbox problems
+%      on the 58pc = 696pt = 9.5in text height we want
+%      normalsize     #lines/column  baselineskip (aka leading)
+%             9pt     63             11.0476pt (truncated down)
+%            10pt     58             12pt      (exact)
+%            11pt     52             13.3846pt (truncated down)
+%            12pt     50             13.92pt   (exact)
+%
+
+% we need to store the nominal baselineskip for the given font size
+% in case baselinestretch ever changes.
+% this is a dimen, so it will not hold stretch or shrink
+\newdimen\@IEEEnormalsizeunitybaselineskip
+\@IEEEnormalsizeunitybaselineskip\baselineskip
+
+\ifx\CLASSOPTIONpt\@IEEEptsizenine
+\typeout{-- This is a 9 point document.}
+\def\normalsize{\@setfontsize{\normalsize}{9}{11.0476pt}}%
+\setlength{\@IEEEnormalsizeunitybaselineskip}{11.0476pt}%
+\normalsize
+\abovedisplayskip 1.5ex plus3pt minus1pt%
+\belowdisplayskip \abovedisplayskip%
+\abovedisplayshortskip 0pt plus3pt%
+\belowdisplayshortskip 1.5ex plus3pt minus1pt
+\def\small{\@setfontsize{\small}{8.5}{10pt}}
+\def\footnotesize{\@setfontsize{\footnotesize}{8}{9pt}}
+\def\scriptsize{\@setfontsize{\scriptsize}{7}{8pt}}
+\def\tiny{\@setfontsize{\tiny}{5}{6pt}}
+% sublargesize is the same as large - 10pt
+\def\sublargesize{\@setfontsize{\sublargesize}{10}{12pt}}
+\def\large{\@setfontsize{\large}{10}{12pt}}
+\def\Large{\@setfontsize{\Large}{12}{14pt}}
+\def\LARGE{\@setfontsize{\LARGE}{14}{17pt}}
+\def\huge{\@setfontsize{\huge}{17}{20pt}}
+\def\Huge{\@setfontsize{\Huge}{20}{24pt}}
+\fi
+
+
+% Check if we have selected 10 points
+\ifx\CLASSOPTIONpt\@IEEEptsizeten
+\typeout{-- This is a 10 point document.}
+\def\normalsize{\@setfontsize{\normalsize}{10}{12.00pt}}%
+\setlength{\@IEEEnormalsizeunitybaselineskip}{12pt}%
+\normalsize
+\abovedisplayskip 1.5ex plus4pt minus2pt%
+\belowdisplayskip \abovedisplayskip%
+\abovedisplayshortskip 0pt plus4pt%
+\belowdisplayshortskip 1.5ex plus4pt minus2pt
+\def\small{\@setfontsize{\small}{9}{10pt}}
+\def\footnotesize{\@setfontsize{\footnotesize}{8}{9pt}}
+\def\scriptsize{\@setfontsize{\scriptsize}{7}{8pt}}
+\def\tiny{\@setfontsize{\tiny}{5}{6pt}}
+% sublargesize is a tad smaller than large - 11pt
+\def\sublargesize{\@setfontsize{\sublargesize}{11}{13.4pt}}
+\def\large{\@setfontsize{\large}{12}{14pt}}
+\def\Large{\@setfontsize{\Large}{14}{17pt}}
+\def\LARGE{\@setfontsize{\LARGE}{17}{20pt}}
+\def\huge{\@setfontsize{\huge}{20}{24pt}}
+\def\Huge{\@setfontsize{\Huge}{24}{28pt}}
+\fi
+
+
+% Check if we have selected 11 points
+\ifx\CLASSOPTIONpt\@IEEEptsizeeleven
+\typeout{-- This is an 11 point document.}
+\def\normalsize{\@setfontsize{\normalsize}{11}{13.3846pt}}%
+\setlength{\@IEEEnormalsizeunitybaselineskip}{13.3846pt}%
+\normalsize
+\abovedisplayskip 1.5ex plus5pt minus3pt%
+\belowdisplayskip \abovedisplayskip%
+\abovedisplayshortskip 0pt plus5pt%
+\belowdisplayshortskip 1.5ex plus5pt minus3pt
+\def\small{\@setfontsize{\small}{10}{12pt}}
+\def\footnotesize{\@setfontsize{\footnotesize}{9}{10.5pt}}
+\def\scriptsize{\@setfontsize{\scriptsize}{8}{9pt}}
+\def\tiny{\@setfontsize{\tiny}{6}{7pt}}
+% sublargesize is the same as large - 12pt
+\def\sublargesize{\@setfontsize{\sublargesize}{12}{14pt}}
+\def\large{\@setfontsize{\large}{12}{14pt}}
+\def\Large{\@setfontsize{\Large}{14}{17pt}}
+\def\LARGE{\@setfontsize{\LARGE}{17}{20pt}}
+\def\huge{\@setfontsize{\huge}{20}{24pt}}
+\def\Huge{\@setfontsize{\Huge}{24}{28pt}}
+\fi
+
+
+% Check if we have selected 12 points
+\ifx\CLASSOPTIONpt\@IEEEptsizetwelve
+\typeout{-- This is a 12 point document.}
+\def\normalsize{\@setfontsize{\normalsize}{12}{13.92pt}}%
+\setlength{\@IEEEnormalsizeunitybaselineskip}{13.92pt}%
+\normalsize
+\abovedisplayskip 1.5ex plus6pt minus4pt%
+\belowdisplayskip \abovedisplayskip%
+\abovedisplayshortskip 0pt plus6pt%
+\belowdisplayshortskip 1.5ex plus6pt minus4pt
+\def\small{\@setfontsize{\small}{10}{12pt}}
+\def\footnotesize{\@setfontsize{\footnotesize}{9}{10.5pt}}
+\def\scriptsize{\@setfontsize{\scriptsize}{8}{9pt}}
+\def\tiny{\@setfontsize{\tiny}{6}{7pt}}
+% sublargesize is the same as large - 14pt
+\def\sublargesize{\@setfontsize{\sublargesize}{14}{17pt}}
+\def\large{\@setfontsize{\large}{14}{17pt}}
+\def\Large{\@setfontsize{\Large}{17}{20pt}}
+\def\LARGE{\@setfontsize{\LARGE}{20}{24pt}}
+\def\huge{\@setfontsize{\huge}{22}{26pt}}
+\def\Huge{\@setfontsize{\Huge}{24}{28pt}}
+\fi
+
+
+% V1.6 The Computer Modern Fonts will issue a substitution warning for
+% 24pt titles (24.88pt is used instead) increase the substitution
+% tolerance to turn off this warning
+\def\fontsubfuzz{.9pt}
+% However, the default (and correct) Times font will scale exactly as needed.
+
+
+% warn the user in case they forget to use the 9pt option with
+% technote
+\ifCLASSOPTIONtechnote%
+ \ifx\CLASSOPTIONpt\@IEEEptsizenine\else%
+  \typeout{** ATTENTION: Technotes are normally 9pt documents.}%
+ \fi%
+\fi
+
+
+% V1.7
+% Improved \textunderscore to provide a much better fake _ when used with
+% OT1 encoding. Under OT1, detect use of pcr or cmtt \ttfamily and use
+% available true _ glyph for those two typewriter fonts.
+\def\@IEEEstringptm{ptm} % Times Roman family
+\def\@IEEEstringppl{ppl} % Palatino Roman family
+\def\@IEEEstringphv{phv} % Helvetica Sans Serif family
+\def\@IEEEstringpcr{pcr} % Courier typewriter family
+\def\@IEEEstringcmtt{cmtt} % Computer Modern typewriter family
+\DeclareTextCommandDefault{\textunderscore}{\leavevmode
+\ifx\f@family\@IEEEstringpcr\string_\else
+\ifx\f@family\@IEEEstringcmtt\string_\else
+\ifx\f@family\@IEEEstringptm\kern 0em\vbox{\hrule\@width 0.5em\@height 0.5pt\kern -0.3ex}\else
+\ifx\f@family\@IEEEstringppl\kern 0em\vbox{\hrule\@width 0.5em\@height 0.5pt\kern -0.3ex}\else
+\ifx\f@family\@IEEEstringphv\kern -0.03em\vbox{\hrule\@width 0.62em\@height 0.52pt\kern -0.33ex}\kern -0.03em\else
+\kern 0.09em\vbox{\hrule\@width 0.6em\@height 0.44pt\kern -0.63pt\kern -0.42ex}\kern 0.09em\fi\fi\fi\fi\fi\relax}
+
+
+
+
+% set the default \baselinestretch
+\def\baselinestretch{1}
+\ifCLASSOPTIONdraftcls
+  \def\baselinestretch{1.5}% default baselinestretch for draft modes
+\fi 
+
+
+% process CLASSINPUT baselinestretch
+\ifx\CLASSINPUTbaselinestretch\@IEEEundefined
+\else
+  \edef\baselinestretch{\CLASSINPUTbaselinestretch} % user CLASSINPUT override
+  \typeout{** ATTENTION: Overriding \string\baselinestretch\space to
+           \baselinestretch\space via \string\CLASSINPUT.}
+\fi
+
+\normalsize % make \baselinestretch take affect
+
+
+
+
+% store the normalsize baselineskip
+\newdimen\CLASSINFOnormalsizebaselineskip
+\CLASSINFOnormalsizebaselineskip=\baselineskip\relax
+% and the normalsize unity (baselinestretch=1) baselineskip
+% we could save a register by giving the user access to
+% \@IEEEnormalsizeunitybaselineskip. However, let's protect
+% its read only internal status
+\newdimen\CLASSINFOnormalsizeunitybaselineskip
+\CLASSINFOnormalsizeunitybaselineskip=\@IEEEnormalsizeunitybaselineskip\relax
+% store the nominal value of jot
+\newdimen\IEEEnormaljot
+\IEEEnormaljot=0.25\baselineskip\relax
+
+% set \jot
+\jot=\IEEEnormaljot\relax
+
+
+
+
+% V1.6, we are now going to fine tune the interword spacing
+% The default interword glue for Times under TeX appears to use a
+% nominal interword spacing of 25% (relative to the font size, i.e., 1em)
+% a maximum of 40% and a minimum of 19%.
+% For example, 10pt text uses an interword glue of:
+% 
+% 2.5pt plus 1.49998pt minus 0.59998pt
+% 
+% However, IEEE allows for a more generous range which reduces the need
+% for hyphenation, especially for two column text. Furthermore, IEEE
+% tends to use a little bit more nominal space between the words.
+% IEEE's interword spacing percentages appear to be:
+% 35% nominal
+% 23% minimum
+% 50% maximum
+% (They may even be using a tad more for the largest fonts such as 24pt.)
+% 
+% for bold text, IEEE increases the spacing a little more:
+% 37.5% nominal
+% 23% minimum
+% 55% maximum
+
+% here are the interword spacing ratios we'll use
+% for medium (normal weight)
+\def\@IEEEinterspaceratioM{0.35}
+\def\@IEEEinterspaceMINratioM{0.23}
+\def\@IEEEinterspaceMAXratioM{0.50}
+
+% for bold
+\def\@IEEEinterspaceratioB{0.375}
+\def\@IEEEinterspaceMINratioB{0.23}
+\def\@IEEEinterspaceMAXratioB{0.55}
+
+
+% command to revise the interword spacing for the current font under TeX:
+% \fontdimen2 = nominal interword space
+% \fontdimen3 = interword stretch
+% \fontdimen4 = interword shrink
+% since all changes to the \fontdimen are global, we can enclose these commands
+% in braces to confine any font attribute or length changes
+\def\@@@IEEEsetfontdimens#1#2#3{{%
+\setlength{\@IEEEtrantmpdimenB}{\f@size pt}% grab the font size in pt, could use 1em instead.
+\setlength{\@IEEEtrantmpdimenA}{#1\@IEEEtrantmpdimenB}%
+\fontdimen2\font=\@IEEEtrantmpdimenA\relax
+\addtolength{\@IEEEtrantmpdimenA}{-#2\@IEEEtrantmpdimenB}%
+\fontdimen3\font=-\@IEEEtrantmpdimenA\relax
+\setlength{\@IEEEtrantmpdimenA}{#1\@IEEEtrantmpdimenB}%
+\addtolength{\@IEEEtrantmpdimenA}{-#3\@IEEEtrantmpdimenB}%
+\fontdimen4\font=\@IEEEtrantmpdimenA\relax}}
+
+% revise the interword spacing for each font weight
+\def\@@IEEEsetfontdimens{{%
+\mdseries
+\@@@IEEEsetfontdimens{\@IEEEinterspaceratioM}{\@IEEEinterspaceMAXratioM}{\@IEEEinterspaceMINratioM}%
+\bfseries
+\@@@IEEEsetfontdimens{\@IEEEinterspaceratioB}{\@IEEEinterspaceMAXratioB}{\@IEEEinterspaceMINratioB}%
+}}
+
+% revise the interword spacing for each font shape
+% \slshape is not often used for IEEE work and is not altered here. The \scshape caps are
+% already a tad too large in the free LaTeX fonts (as compared to what IEEE uses) so we
+% won't alter these either.
+\def\@IEEEsetfontdimens{{%
+\normalfont
+\@@IEEEsetfontdimens
+\normalfont\itshape
+\@@IEEEsetfontdimens
+}}
+
+% command to revise the interword spacing for each font size (and shape
+% and weight). Only the \rmfamily is done here as \ttfamily uses a 
+% fixed spacing and \sffamily is not used as the main text of IEEE papers.
+\def\@IEEEtunefonts{{\selectfont\rmfamily
+\tiny\@IEEEsetfontdimens
+\scriptsize\@IEEEsetfontdimens
+\footnotesize\@IEEEsetfontdimens
+\small\@IEEEsetfontdimens
+\normalsize\@IEEEsetfontdimens
+\sublargesize\@IEEEsetfontdimens
+\large\@IEEEsetfontdimens
+\LARGE\@IEEEsetfontdimens
+\huge\@IEEEsetfontdimens
+\Huge\@IEEEsetfontdimens}}
+
+% if the nofonttune class option is not given, revise the interword spacing
+% now - in case IEEEtran makes any default length measurements, and make
+% sure all the default fonts are loaded
+\ifCLASSOPTIONnofonttune\else
+\@IEEEtunefonts
+\fi
+
+% and again at the start of the document in case the user loaded different fonts
+\AtBeginDocument{\ifCLASSOPTIONnofonttune\else\@IEEEtunefonts\fi}
+
+
+
+% V1.6 
+% LaTeX is a little to quick to use hyphenations
+% So, we increase the penalty for their use and raise
+% the badness level that triggers an underfull hbox
+% warning. The author may still have to tweak things,
+% but the appearance will be much better "right out
+% of the box" than that under V1.5 and prior.
+% TeX default is 50
+\hyphenpenalty=750
+% If we didn't adjust the interword spacing, 2200 might be better.
+% The TeX default is 1000
+\hbadness=1350
+% IEEE does not use extra spacing after punctuation
+\frenchspacing
+
+% V1.7 increase this a tad to discourage equation breaks
+\binoppenalty=1000 % default 700
+\relpenalty=800     % default 500
+
+
+% margin note stuff
+\marginparsep      10pt
+\marginparwidth    20pt
+\marginparpush     25pt
+
+
+% if things get too close, go ahead and let them touch
+\lineskip            0pt
+\normallineskip      0pt
+\lineskiplimit       0pt
+\normallineskiplimit 0pt
+
+% The distance from the lower edge of the text body to the
+% footline
+\footskip 0.4in
+
+% normally zero, should be relative to font height.
+% put in a little rubber to help stop some bad breaks (underfull vboxes)
+\parskip 0ex plus 0.2ex minus 0.1ex
+
+\parindent    1.0em
+
+\topmargin    -49.0pt
+\headheight   12pt
+\headsep      0.25in
+
+% use the normal font baselineskip
+% so that \topskip is unaffected by changes in \baselinestretch
+\topskip=\@IEEEnormalsizeunitybaselineskip
+\textheight       58pc  % 9.63in, 696pt
+% Tweak textheight to a perfect integer number of lines/page.
+% The normal baselineskip for each document point size is used 
+% to determine these values.
+\ifx\CLASSOPTIONpt\@IEEEptsizenine\textheight=63\@IEEEnormalsizeunitybaselineskip\fi      % 63 lines/page
+\ifx\CLASSOPTIONpt\@IEEEptsizeten\textheight=58\@IEEEnormalsizeunitybaselineskip\fi       % 58 lines/page
+\ifx\CLASSOPTIONpt\@IEEEptsizeeleven\textheight=52\@IEEEnormalsizeunitybaselineskip\fi    % 52 lines/page
+\ifx\CLASSOPTIONpt\@IEEEptsizetwelve\textheight=50\@IEEEnormalsizeunitybaselineskip\fi    % 50 lines/page
+
+
+\columnsep         1pc
+\textwidth        43pc   % 2 x 21pc + 1pc = 43pc
+
+% the default side margins are equal
+\if@IEEEusingAfourpaper 
+\oddsidemargin        14.32mm
+\evensidemargin       14.32mm
+\else
+\oddsidemargin        0.680in
+\evensidemargin       0.680in
+\fi
+% compensate for LaTeX's 1in offset
+\addtolength{\oddsidemargin}{-1in}
+\addtolength{\evensidemargin}{-1in}
+
+
+
+% adjust margins for conference mode
+\ifCLASSOPTIONconference
+ \topmargin        -0.25in
+ % we retain the reserved, but unused space for headers
+ \addtolength{\topmargin}{-\headheight}
+ \addtolength{\topmargin}{-\headsep}
+ \textheight        9.25in % The standard for conferences (668.4975pt)
+ % Tweak textheight to a perfect integer number of lines/page.
+ \ifx\CLASSOPTIONpt\@IEEEptsizenine\textheight=61\@IEEEnormalsizeunitybaselineskip\fi      % 61 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizeten\textheight=56\@IEEEnormalsizeunitybaselineskip\fi       % 56 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizeeleven\textheight=50\@IEEEnormalsizeunitybaselineskip\fi    % 50 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizetwelve\textheight=48\@IEEEnormalsizeunitybaselineskip\fi    % 48 lines/page
+\fi
+
+
+% compsoc conference
+\ifCLASSOPTIONcompsoc
+\ifCLASSOPTIONconference
+ % compsoc conference use a larger value for columnsep
+ \columnsep 0.375in
+ % compsoc conferences want 1in top margin, 1.125in bottom margin
+ \topmargin        0in
+ \addtolength{\topmargin}{-6pt}% we tweak this a tad to better comply with top of line stuff
+ % we retain the reserved, but unused space for headers
+ \addtolength{\topmargin}{-\headheight}
+ \addtolength{\topmargin}{-\headsep}
+ \textheight        8.875in % (641.39625pt)
+ % Tweak textheight to a perfect integer number of lines/page.
+ \ifx\CLASSOPTIONpt\@IEEEptsizenine\textheight=58\@IEEEnormalsizeunitybaselineskip\fi      % 58 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizeten\textheight=53\@IEEEnormalsizeunitybaselineskip\fi       % 53 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizeeleven\textheight=48\@IEEEnormalsizeunitybaselineskip\fi    % 48 lines/page
+ \ifx\CLASSOPTIONpt\@IEEEptsizetwelve\textheight=46\@IEEEnormalsizeunitybaselineskip\fi    % 46 lines/page 
+ \textwidth 6.5in
+ % the default side margins are equal
+ \if@IEEEusingAfourpaper 
+  \oddsidemargin        22.45mm
+  \evensidemargin       22.45mm
+ \else
+  \oddsidemargin        1in
+  \evensidemargin       1in
+ \fi
+ % compensate for LaTeX's 1in offset
+ \addtolength{\oddsidemargin}{-1in}
+ \addtolength{\evensidemargin}{-1in}
+\fi\fi
+
+
+
+% draft mode settings override that of all other modes
+% provides a nice 1in margin all around the paper and extra
+% space between the lines for editor's comments
+\ifCLASSOPTIONdraftcls 
+  % want 1in from top of paper to text
+  \setlength{\topmargin}{-\headsep}%
+  \addtolength{\topmargin}{-\headheight}%
+  % we want 1in side margins regardless of paper type
+  \oddsidemargin      0in
+  \evensidemargin     0in
+  % set the text width
+  \setlength{\textwidth}{\paperwidth}%
+  \addtolength{\textwidth}{-2.0in}%
+  \setlength{\textheight}{\paperheight}%
+  \addtolength{\textheight}{-2.0in}%
+  % digitize textheight to be an integer number of lines.
+  % this may cause the bottom margin to be off a tad
+  \addtolength{\textheight}{-1\topskip}%
+  \divide\textheight  by \baselineskip%
+  \multiply\textheight  by \baselineskip%
+  \addtolength{\textheight}{\topskip}%
+\fi
+
+
+
+% process CLASSINPUT inner/outer margin
+% if inner margin defined, but outer margin not, set outer to inner.
+\ifx\CLASSINPUTinnersidemargin\@IEEEundefined
+\else
+  \ifx\CLASSINPUToutersidemargin\@IEEEundefined
+    \edef\CLASSINPUToutersidemargin{\CLASSINPUTinnersidemargin}
+  \fi
+\fi
+
+\ifx\CLASSINPUToutersidemargin\@IEEEundefined
+\else
+  % if outer margin defined, but inner margin not, set inner to outer.
+  \ifx\CLASSINPUTinnersidemargin\@IEEEundefined
+    \edef\CLASSINPUTinnersidemargin{\CLASSINPUToutersidemargin}
+  \fi
+  \setlength{\oddsidemargin}{\CLASSINPUTinnersidemargin}
+  \ifCLASSOPTIONtwoside
+    \setlength{\evensidemargin}{\CLASSINPUToutersidemargin}
+  \else
+    \setlength{\evensidemargin}{\CLASSINPUTinnersidemargin}
+  \fi
+  \addtolength{\oddsidemargin}{-1in}
+  \addtolength{\evensidemargin}{-1in}
+  \setlength{\textwidth}{\paperwidth}
+  \addtolength{\textwidth}{-\CLASSINPUTinnersidemargin}
+  \addtolength{\textwidth}{-\CLASSINPUToutersidemargin}
+  \typeout{** ATTENTION: Overriding inner side margin to \CLASSINPUTinnersidemargin\space and 
+           outer side margin to \CLASSINPUToutersidemargin\space via \string\CLASSINPUT.}
+\fi
+
+
+
+% process CLASSINPUT top/bottom text margin
+% if toptext margin defined, but bottomtext margin not, set bottomtext to toptext margin
+\ifx\CLASSINPUTtoptextmargin\@IEEEundefined
+\else
+  \ifx\CLASSINPUTbottomtextmargin\@IEEEundefined
+    \edef\CLASSINPUTbottomtextmargin{\CLASSINPUTtoptextmargin}
+  \fi
+\fi
+
+\ifx\CLASSINPUTbottomtextmargin\@IEEEundefined
+\else
+  % if bottomtext margin defined, but toptext margin not, set toptext to bottomtext margin
+  \ifx\CLASSINPUTtoptextmargin\@IEEEundefined
+    \edef\CLASSINPUTtoptextmargin{\CLASSINPUTbottomtextmargin}
+  \fi
+  \setlength{\topmargin}{\CLASSINPUTtoptextmargin}
+  \addtolength{\topmargin}{-1in}
+  \addtolength{\topmargin}{-\headheight}
+  \addtolength{\topmargin}{-\headsep}
+  \setlength{\textheight}{\paperheight}
+  \addtolength{\textheight}{-\CLASSINPUTtoptextmargin}
+  \addtolength{\textheight}{-\CLASSINPUTbottomtextmargin}
+  % in the default format we use the normal baselineskip as topskip
+  % we only need 0.7 of this to clear typical top text and we need
+  % an extra 0.3 spacing at the bottom for descenders. This will
+  % correct for both.
+  \addtolength{\topmargin}{-0.3\@IEEEnormalsizeunitybaselineskip}
+  \typeout{** ATTENTION: Overriding top text margin to \CLASSINPUTtoptextmargin\space and 
+           bottom text margin to \CLASSINPUTbottomtextmargin\space via \string\CLASSINPUT.}
+\fi
+
+
+
+
+
+
+
+% LIST SPACING CONTROLS
+
+% Controls the amount of EXTRA spacing
+% above and below \trivlist 
+% Both \list and IED lists override this.
+% However, \trivlist will use this as will most
+% things built from \trivlist like the \center
+% environment.
+\topsep           0.5\baselineskip
+
+% Controls the additional spacing around lists preceded
+% or followed by blank lines. IEEE does not increase
+% spacing before or after paragraphs so it is set to zero.
+% \z@ is the same as zero, but faster.
+\partopsep          \z@
+
+% Controls the spacing between paragraphs in lists. 
+% IEEE does not increase spacing before or after paragraphs
+% so this is also zero. 
+% With IEEEtran.cls, global changes to
+% this value DO affect lists (but not IED lists).
+\parsep             \z@
+
+% Controls the extra spacing between list items. 
+% IEEE does not put extra spacing between items.
+% With IEEEtran.cls, global changes to this value DO affect
+% lists (but not IED lists).
+\itemsep            \z@
+
+% \itemindent is the amount to indent the FIRST line of a list
+% item. It is auto set to zero within the \list environment. To alter
+% it, you have to do so when you call the \list.
+% However, IEEE uses this for the theorem environment
+% There is an alternative value for this near \leftmargini below
+\itemindent         -1em
+
+% \leftmargin, the spacing from the left margin of the main text to
+% the left of the main body of a list item is set by \list.
+% Hence this statement does nothing for lists.
+% But, quote and verse do use it for indention.
+\leftmargin         2em
+
+% we retain this stuff from the older IEEEtran.cls so that \list
+% will work the same way as before. However, itemize, enumerate and
+% description (IED) could care less about what these are as they
+% all are overridden.
+\leftmargini        2em
+%\itemindent         2em  % Alternative values: sometimes used.
+%\leftmargini        0em
+\leftmarginii       1em
+\leftmarginiii    1.5em
+\leftmarginiv     1.5em
+\leftmarginv      1.0em
+\leftmarginvi     1.0em
+\labelsep         0.5em 
+\labelwidth         \z@
+
+
+% The old IEEEtran.cls behavior of \list is retained.
+% However, the new V1.3 IED list environments override all the
+% @list stuff (\@listX is called within \list for the
+% appropriate level just before the user's list_decl is called). 
+% \topsep is now 2pt as IEEE puts a little extra space around
+% lists - used by those non-IED macros that depend on \list.
+% Note that \parsep and \itemsep are not redefined as in 
+% the sizexx.clo \@listX (which article.cls uses) so global changes
+% of these values DO affect \list
+% 
+\def\@listi{\leftmargin\leftmargini \topsep 2pt plus 1pt minus 1pt}
+\let\@listI\@listi
+\def\@listii{\leftmargin\leftmarginii\labelwidth\leftmarginii%
+    \advance\labelwidth-\labelsep \topsep 2pt}
+\def\@listiii{\leftmargin\leftmarginiii\labelwidth\leftmarginiii%
+    \advance\labelwidth-\labelsep \topsep 2pt}
+\def\@listiv{\leftmargin\leftmarginiv\labelwidth\leftmarginiv%
+    \advance\labelwidth-\labelsep \topsep 2pt}
+\def\@listv{\leftmargin\leftmarginv\labelwidth\leftmarginv%
+    \advance\labelwidth-\labelsep \topsep 2pt}
+\def\@listvi{\leftmargin\leftmarginvi\labelwidth\leftmarginvi%
+    \advance\labelwidth-\labelsep \topsep 2pt}
+
+
+% IEEE uses 5) not 5.
+\def\labelenumi{\theenumi)}     \def\theenumi{\arabic{enumi}}
+
+% IEEE uses a) not (a)
+\def\labelenumii{\theenumii)}  \def\theenumii{\alph{enumii}}
+
+% IEEE uses iii) not iii.
+\def\labelenumiii{\theenumiii)} \def\theenumiii{\roman{enumiii}}
+
+% IEEE uses A) not A.
+\def\labelenumiv{\theenumiv)}   \def\theenumiv{\Alph{enumiv}}
+
+% exactly the same as in article.cls
+\def\p@enumii{\theenumi}
+\def\p@enumiii{\theenumi(\theenumii)}
+\def\p@enumiv{\p@enumiii\theenumiii}
+
+% itemized list label styles
+\def\labelitemi{$\scriptstyle\bullet$}
+\def\labelitemii{\textbf{--}}
+\def\labelitemiii{$\ast$}
+\def\labelitemiv{$\cdot$}
+
+
+
+% **** V1.3 ENHANCEMENTS ****
+% Itemize, Enumerate and Description (IED) List Controls
+% ***************************
+% 
+% 
+% IEEE seems to use at least two different values by
+% which ITEMIZED list labels are indented to the right
+% For The Journal of Lightwave Technology (JLT) and The Journal
+% on Selected Areas in Communications (JSAC), they tend to use
+% an indention equal to \parindent. For Transactions on Communications
+% they tend to indent ITEMIZED lists a little more--- 1.3\parindent.
+% We'll provide both values here for you so that you can choose 
+% which one you like in your document using a command such as:
+% setlength{\IEEEilabelindent}{\IEEEilabelindentB}
+\newdimen\IEEEilabelindentA
+\IEEEilabelindentA \parindent
+
+\newdimen\IEEEilabelindentB
+\IEEEilabelindentB 1.3\parindent
+% However, we'll default to using \parindent
+% which makes more sense to me
+\newdimen\IEEEilabelindent
+\IEEEilabelindent \IEEEilabelindentA
+
+
+% This controls the default amount the enumerated list labels
+% are indented to the right.
+% Normally, this is the same as the paragraph indention
+\newdimen\IEEEelabelindent
+\IEEEelabelindent \parindent
+
+% This controls the default amount the description list labels
+% are indented to the right.
+% Normally, this is the same as the paragraph indention
+\newdimen\IEEEdlabelindent
+\IEEEdlabelindent \parindent
+
+% This is the value actually used within the IED lists.
+% The IED environments automatically set its value to
+% one of the three values above, so global changes do 
+% not have any effect
+\newdimen\IEEElabelindent
+\IEEElabelindent \parindent
+
+% The actual amount labels will be indented is
+% \IEEElabelindent multiplied by the factor below
+% corresponding to the level of nesting depth
+% This provides a means by which the user can
+% alter the effective \IEEElabelindent for deeper
+% levels
+% There may not be such a thing as correct "standard IEEE"
+% values. What IEEE actually does may depend on the specific
+% circumstances.
+% The first list level almost always has full indention.
+% The second levels I've seen have only 75% of the normal indentation
+% Three level or greater nestings are very rare. I am guessing
+% that they don't use any indentation.
+\def\IEEElabelindentfactori{1.0}   % almost always one
+\def\IEEElabelindentfactorii{0.75} % 0.0 or 1.0 may be used in some cases
+\def\IEEElabelindentfactoriii{0.0} % 0.75? 0.5? 0.0?
+\def\IEEElabelindentfactoriv{0.0}
+\def\IEEElabelindentfactorv{0.0}
+\def\IEEElabelindentfactorvi{0.0}
+
+% value actually used within IED lists, it is auto
+% set to one of the 6 values above
+% global changes here have no effect
+\def\IEEElabelindentfactor{1.0}
+
+% This controls the default spacing between the end of the IED
+% list labels and the list text, when normal text is used for
+% the labels.
+\newdimen\IEEEiednormlabelsep
+\IEEEiednormlabelsep 0.6em
+
+% This controls the default spacing between the end of the IED
+% list labels and the list text, when math symbols are used for
+% the labels (nomenclature lists). IEEE usually increases the 
+% spacing in these cases
+\newdimen\IEEEiedmathlabelsep
+\IEEEiedmathlabelsep 1.2em
+
+% This controls the extra vertical separation put above and
+% below each IED list. IEEE usually puts a little extra spacing
+% around each list. However, this spacing is barely noticeable.
+\newskip\IEEEiedtopsep
+\IEEEiedtopsep 2pt plus 1pt minus 1pt
+
+
+% This command is executed within each IED list environment
+% at the beginning of the list. You can use this to set the 
+% parameters for some/all your IED list(s) without disturbing 
+% global parameters that affect things other than lists.
+% i.e., renewcommand{\IEEEiedlistdecl}{\setlength{\labelsep}{5em}}
+% will alter the \labelsep for the next list(s) until 
+% \IEEEiedlistdecl is redefined. 
+\def\IEEEiedlistdecl{\relax}
+
+% This command provides an easy way to set \leftmargin based
+% on the \labelwidth, \labelsep and the argument \IEEElabelindent
+% Usage: \IEEEcalcleftmargin{width-to-indent-the-label}
+% output is in the \leftmargin variable, i.e., effectively:
+% \leftmargin = argument + \labelwidth + \labelsep
+% Note controlled spacing here, shield end of lines with %
+\def\IEEEcalcleftmargin#1{\setlength{\leftmargin}{#1}%
+\addtolength{\leftmargin}{\labelwidth}%
+\addtolength{\leftmargin}{\labelsep}}
+
+% This command provides an easy way to set \labelwidth to the
+% width of the given text. It is the same as
+% \settowidth{\labelwidth}{label-text}
+% and useful as a shorter alternative.
+% Typically used to set \labelwidth to be the width
+% of the longest label in the list
+\def\IEEEsetlabelwidth#1{\settowidth{\labelwidth}{#1}}
+
+% When this command is executed, IED lists will use the 
+% IEEEiedmathlabelsep label separation rather than the normal
+% spacing. To have an effect, this command must be executed via
+% the \IEEEiedlistdecl or within the option of the IED list
+% environments.
+\def\IEEEusemathlabelsep{\setlength{\labelsep}{\IEEEiedmathlabelsep}}
+
+% A flag which controls whether the IED lists automatically
+% calculate \leftmargin from \IEEElabelindent, \labelwidth and \labelsep
+% Useful if you want to specify your own \leftmargin
+% This flag must be set (\IEEEnocalcleftmargintrue or \IEEEnocalcleftmarginfalse) 
+% via the \IEEEiedlistdecl or within the option of the IED list
+% environments to have an effect.
+\newif\ifIEEEnocalcleftmargin
+\IEEEnocalcleftmarginfalse
+
+% A flag which controls whether \IEEElabelindent is multiplied by
+% the \IEEElabelindentfactor for each list level.
+% This flag must be set via the \IEEEiedlistdecl or within the option 
+% of the IED list environments to have an effect.
+\newif\ifIEEEnolabelindentfactor
+\IEEEnolabelindentfactorfalse
+
+
+% internal variable to indicate type of IED label
+% justification
+% 0 - left; 1 - center; 2 - right
+\def\@IEEEiedjustify{0}
+
+
+% commands to allow the user to control IED
+% label justifications. Use these commands within
+% the IED environment option or in the \IEEEiedlistdecl
+% Note that changing the normal list justifications
+% is nonstandard and IEEE may not like it if you do so!
+% I include these commands as they may be helpful to
+% those who are using these enhanced list controls for
+% other non-IEEE related LaTeX work.
+% itemize and enumerate automatically default to right
+% justification, description defaults to left.
+\def\IEEEiedlabeljustifyl{\def\@IEEEiedjustify{0}}%left
+\def\IEEEiedlabeljustifyc{\def\@IEEEiedjustify{1}}%center
+\def\IEEEiedlabeljustifyr{\def\@IEEEiedjustify{2}}%right
+
+
+
+
+% commands to save to and restore from the list parameter copies
+% this allows us to set all the list parameters within
+% the list_decl and prevent \list (and its \@list) 
+% from overriding any of our parameters
+% V1.6 use \edefs instead of dimen's to conserve dimen registers
+% Note controlled spacing here, shield end of lines with %
+\def\@IEEEsavelistparams{\edef\@IEEEiedtopsep{\the\topsep}%
+\edef\@IEEEiedlabelwidth{\the\labelwidth}%
+\edef\@IEEEiedlabelsep{\the\labelsep}%
+\edef\@IEEEiedleftmargin{\the\leftmargin}%
+\edef\@IEEEiedpartopsep{\the\partopsep}%
+\edef\@IEEEiedparsep{\the\parsep}%
+\edef\@IEEEieditemsep{\the\itemsep}%
+\edef\@IEEEiedrightmargin{\the\rightmargin}%
+\edef\@IEEEiedlistparindent{\the\listparindent}%
+\edef\@IEEEieditemindent{\the\itemindent}}
+
+% Note controlled spacing here
+\def\@IEEErestorelistparams{\topsep\@IEEEiedtopsep\relax%
+\labelwidth\@IEEEiedlabelwidth\relax%
+\labelsep\@IEEEiedlabelsep\relax%
+\leftmargin\@IEEEiedleftmargin\relax%
+\partopsep\@IEEEiedpartopsep\relax%
+\parsep\@IEEEiedparsep\relax%
+\itemsep\@IEEEieditemsep\relax%
+\rightmargin\@IEEEiedrightmargin\relax%
+\listparindent\@IEEEiedlistparindent\relax%
+\itemindent\@IEEEieditemindent\relax}
+
+
+% v1.6b provide original LaTeX IED list environments
+% note that latex.ltx defines \itemize and \enumerate, but not \description
+% which must be created by the base classes
+% save original LaTeX itemize and enumerate
+\let\LaTeXitemize\itemize
+\let\endLaTeXitemize\enditemize
+\let\LaTeXenumerate\enumerate
+\let\endLaTeXenumerate\endenumerate
+
+% provide original LaTeX description environment from article.cls
+\newenvironment{LaTeXdescription}
+               {\list{}{\labelwidth\z@ \itemindent-\leftmargin
+                        \let\makelabel\descriptionlabel}}
+               {\endlist}
+\newcommand*\descriptionlabel[1]{\hspace\labelsep
+                                 \normalfont\bfseries #1}
+
+
+% override LaTeX's default IED lists
+\def\itemize{\@IEEEitemize}
+\def\enditemize{\@endIEEEitemize}
+\def\enumerate{\@IEEEenumerate}
+\def\endenumerate{\@endIEEEenumerate}
+\def\description{\@IEEEdescription}
+\def\enddescription{\@endIEEEdescription}
+
+% provide the user with aliases - may help those using packages that
+% override itemize, enumerate, or description
+\def\IEEEitemize{\@IEEEitemize}
+\def\endIEEEitemize{\@endIEEEitemize}
+\def\IEEEenumerate{\@IEEEenumerate}
+\def\endIEEEenumerate{\@endIEEEenumerate}
+\def\IEEEdescription{\@IEEEdescription}
+\def\endIEEEdescription{\@endIEEEdescription}
+
+
+% V1.6 we want to keep the IEEEtran IED list definitions as our own internal
+% commands so they are protected against redefinition
+\def\@IEEEitemize{\@ifnextchar[{\@@IEEEitemize}{\@@IEEEitemize[\relax]}}
+\def\@IEEEenumerate{\@ifnextchar[{\@@IEEEenumerate}{\@@IEEEenumerate[\relax]}}
+\def\@IEEEdescription{\@ifnextchar[{\@@IEEEdescription}{\@@IEEEdescription[\relax]}}
+\def\@endIEEEitemize{\endlist}
+\def\@endIEEEenumerate{\endlist}
+\def\@endIEEEdescription{\endlist}
+
+
+% DO NOT ALLOW BLANK LINES TO BE IN THESE IED ENVIRONMENTS
+% AS THIS WILL FORCE NEW PARAGRAPHS AFTER THE IED LISTS
+% IEEEtran itemized list MDS 1/2001
+% Note controlled spacing here, shield end of lines with %
+\def\@@IEEEitemize[#1]{%
+                \ifnum\@itemdepth>3\relax\@toodeep\else%
+                \ifnum\@listdepth>5\relax\@toodeep\else%
+                \advance\@itemdepth\@ne%
+                \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}%
+                % get the labelindentfactor for this level
+                \advance\@listdepth\@ne% we need to know what the level WILL be
+                \edef\IEEElabelindentfactor{\csname IEEElabelindentfactor\romannumeral\the\@listdepth\endcsname}%
+                \advance\@listdepth-\@ne% undo our increment
+                \def\@IEEEiedjustify{2}% right justified labels are default
+                % set other defaults
+                \IEEEnocalcleftmarginfalse%
+                \IEEEnolabelindentfactorfalse%
+                \topsep\IEEEiedtopsep%
+                \IEEElabelindent\IEEEilabelindent%
+                \labelsep\IEEEiednormlabelsep%
+                \partopsep 0ex%
+                \parsep 0ex%
+                \itemsep 0ex%
+                \rightmargin 0em%
+                \listparindent 0em%
+                \itemindent 0em%
+                % calculate the label width
+                % the user can override this later if
+                % they specified a \labelwidth
+                \settowidth{\labelwidth}{\csname labelitem\romannumeral\the\@itemdepth\endcsname}%
+                \@IEEEsavelistparams% save our list parameters
+                \list{\csname\@itemitem\endcsname}{%
+                \@IEEErestorelistparams% override any list{} changes
+                                       % to our globals
+                \let\makelabel\@IEEEiedmakelabel% v1.6b setup \makelabel
+                \IEEEiedlistdecl% let user alter parameters
+                #1\relax%
+                % If the user has requested not to use the
+                % labelindent factor, don't revise \labelindent
+                \ifIEEEnolabelindentfactor\relax%
+                \else\IEEElabelindent=\IEEElabelindentfactor\labelindent%
+                \fi%
+                % Unless the user has requested otherwise,
+                % calculate our left margin based
+                % on \IEEElabelindent, \labelwidth and
+                % \labelsep
+                \ifIEEEnocalcleftmargin\relax%
+                \else\IEEEcalcleftmargin{\IEEElabelindent}%
+                \fi}\fi\fi}%
+
+
+% DO NOT ALLOW BLANK LINES TO BE IN THESE IED ENVIRONMENTS
+% AS THIS WILL FORCE NEW PARAGRAPHS AFTER THE IED LISTS
+% IEEEtran enumerate list MDS 1/2001
+% Note controlled spacing here, shield end of lines with %
+\def\@@IEEEenumerate[#1]{%
+                \ifnum\@enumdepth>3\relax\@toodeep\else%
+                \ifnum\@listdepth>5\relax\@toodeep\else%
+                \advance\@enumdepth\@ne%
+                \edef\@enumctr{enum\romannumeral\the\@enumdepth}%
+                % get the labelindentfactor for this level
+                \advance\@listdepth\@ne% we need to know what the level WILL be
+                \edef\IEEElabelindentfactor{\csname IEEElabelindentfactor\romannumeral\the\@listdepth\endcsname}%
+                \advance\@listdepth-\@ne% undo our increment
+                \def\@IEEEiedjustify{2}% right justified labels are default
+                % set other defaults
+                \IEEEnocalcleftmarginfalse%
+                \IEEEnolabelindentfactorfalse%
+                \topsep\IEEEiedtopsep%
+                \IEEElabelindent\IEEEelabelindent%
+                \labelsep\IEEEiednormlabelsep%
+                \partopsep 0ex%
+                \parsep 0ex%
+                \itemsep 0ex%
+                \rightmargin 0em%
+                \listparindent 0em%
+                \itemindent 0em%
+                % calculate the label width
+                % We'll set it to the width suitable for all labels using
+                % normalfont 1) to 9)
+                % The user can override this later
+                \settowidth{\labelwidth}{9)}%
+                \@IEEEsavelistparams% save our list parameters
+                \list{\csname label\@enumctr\endcsname}{\usecounter{\@enumctr}%
+                \@IEEErestorelistparams% override any list{} changes
+                                       % to our globals
+                \let\makelabel\@IEEEiedmakelabel% v1.6b setup \makelabel
+                \IEEEiedlistdecl% let user alter parameters 
+                #1\relax%
+                % If the user has requested not to use the
+                % IEEElabelindent factor, don't revise \IEEElabelindent
+                \ifIEEEnolabelindentfactor\relax%
+                \else\IEEElabelindent=\IEEElabelindentfactor\IEEElabelindent%
+                \fi%
+                % Unless the user has requested otherwise,
+                % calculate our left margin based
+                % on \IEEElabelindent, \labelwidth and
+                % \labelsep
+                \ifIEEEnocalcleftmargin\relax%
+                \else\IEEEcalcleftmargin{\IEEElabelindent}%
+                \fi}\fi\fi}%
+
+
+% DO NOT ALLOW BLANK LINES TO BE IN THESE IED ENVIRONMENTS
+% AS THIS WILL FORCE NEW PARAGRAPHS AFTER THE IED LISTS
+% IEEEtran description list MDS 1/2001
+% Note controlled spacing here, shield end of lines with %
+\def\@@IEEEdescription[#1]{%
+                \ifnum\@listdepth>5\relax\@toodeep\else%
+                % get the labelindentfactor for this level
+                \advance\@listdepth\@ne% we need to know what the level WILL be
+                \edef\IEEElabelindentfactor{\csname IEEElabelindentfactor\romannumeral\the\@listdepth\endcsname}%
+                \advance\@listdepth-\@ne% undo our increment
+                \def\@IEEEiedjustify{0}% left justified labels are default
+                % set other defaults
+                \IEEEnocalcleftmarginfalse%
+                \IEEEnolabelindentfactorfalse%
+                \topsep\IEEEiedtopsep% 
+                \IEEElabelindent\IEEEdlabelindent%
+                % assume normal labelsep
+                \labelsep\IEEEiednormlabelsep%
+                \partopsep 0ex%
+                \parsep 0ex%
+                \itemsep 0ex%
+                \rightmargin 0em%
+                \listparindent 0em%
+                \itemindent 0em%
+                % Bogus label width in case the user forgets
+                % to set it.
+                % TIP: If you want to see what a variable's width is you
+                % can use the TeX command \showthe\width-variable to 
+                % display it on the screen during compilation 
+                % (This might be helpful to know when you need to find out
+                % which label is the widest)
+                \settowidth{\labelwidth}{Hello}%
+                \@IEEEsavelistparams% save our list parameters
+                \list{}{\@IEEErestorelistparams% override any list{} changes
+                                               % to our globals
+                \let\makelabel\@IEEEiedmakelabel% v1.6b setup \makelabel
+                \IEEEiedlistdecl% let user alter parameters 
+                #1\relax%
+                % If the user has requested not to use the
+                % labelindent factor, don't revise \IEEElabelindent
+                \ifIEEEnolabelindentfactor\relax%
+                \else\IEEElabelindent=\IEEElabelindentfactor\IEEElabelindent%
+                \fi%
+                % Unless the user has requested otherwise,
+                % calculate our left margin based
+                % on \IEEElabelindent, \labelwidth and
+                % \labelsep
+                \ifIEEEnocalcleftmargin\relax%
+                \else\IEEEcalcleftmargin{\IEEElabelindent}\relax%
+                \fi}\fi}
+
+% v1.6b we use one makelabel that does justification as needed.
+\def\@IEEEiedmakelabel#1{\relax\if\@IEEEiedjustify 0\relax
+\makebox[\labelwidth][l]{\normalfont #1}\else
+\if\@IEEEiedjustify 1\relax
+\makebox[\labelwidth][c]{\normalfont #1}\else
+\makebox[\labelwidth][r]{\normalfont #1}\fi\fi}
+
+
+% VERSE and QUOTE
+% V1.7 define environments with newenvironment
+\newenvironment{verse}{\let\\=\@centercr
+    \list{}{\itemsep\z@ \itemindent -1.5em \listparindent \itemindent
+    \rightmargin\leftmargin\advance\leftmargin 1.5em}\item\relax}
+    {\endlist}
+\newenvironment{quotation}{\list{}{\listparindent 1.5em \itemindent\listparindent
+    \rightmargin\leftmargin \parsep 0pt plus 1pt}\item\relax}
+    {\endlist}
+\newenvironment{quote}{\list{}{\rightmargin\leftmargin}\item\relax}
+    {\endlist}
+
+
+% \titlepage
+% provided only for backward compatibility. \maketitle is the correct
+% way to create the title page. 
+\newif\if@restonecol
+\def\titlepage{\@restonecolfalse\if@twocolumn\@restonecoltrue\onecolumn
+    \else \newpage \fi \thispagestyle{empty}\c@page\z@}
+\def\endtitlepage{\if@restonecol\twocolumn \else \newpage \fi}
+
+% standard values from article.cls
+\arraycolsep     5pt
+\arrayrulewidth .4pt
+\doublerulesep   2pt
+
+\tabcolsep       6pt
+\tabbingsep      0.5em
+
+
+%% FOOTNOTES
+%
+%\skip\footins 10pt plus 4pt minus 2pt
+% V1.6 respond to changes in font size
+% space added above the footnotes (if present)
+\skip\footins 0.9\baselineskip  plus 0.4\baselineskip  minus 0.2\baselineskip
+
+% V1.6, we need to make \footnotesep responsive to changes
+% in \baselineskip or strange spacings will result when in
+% draft mode. Here is a little LaTeX secret - \footnotesep
+% determines the height of an invisible strut that is placed
+% *above* the baseline of footnotes after the first. Since
+% LaTeX considers the space for characters to be 0.7/baselineskip
+% above the baseline and 0.3/baselineskip below it, we need to
+% use 0.7/baselineskip as a \footnotesep to maintain equal spacing
+% between all the lines of the footnotes. IEEE often uses a tad
+% more, so use 0.8\baselineskip. This slightly larger value also helps
+% the text to clear the footnote marks. Note that \thanks in IEEEtran
+% uses its own value of \footnotesep which is set in \maketitle.
+{\footnotesize
+\global\footnotesep 0.8\baselineskip}
+
+
+\skip\@mpfootins = \skip\footins
+\fboxsep = 3pt
+\fboxrule = .4pt
+% V1.6 use 1em, then use LaTeX2e's \@makefnmark
+% Note that IEEE normally *left* aligns the footnote marks, so we don't need
+% box resizing tricks here.
+\long\def\@makefntext#1{\parindent 1em\indent\hbox{\@makefnmark}#1}% V1.6 use 1em
+% V1.7 compsoc does not use superscipts for footnote marks
+\ifCLASSOPTIONcompsoc
+\def\@IEEEcompsocmakefnmark{\hbox{\normalfont\@thefnmark.\ }}
+\long\def\@makefntext#1{\parindent 1em\indent\hbox{\@IEEEcompsocmakefnmark}#1}
+\fi
+
+% IEEE does not use footnote rules
+\def\footnoterule{}
+
+% V1.7 for compsoc, IEEE uses a footnote rule only for \thanks. We devise a "one-shot"
+% system to implement this.
+\newif\if@IEEEenableoneshotfootnoterule
+\@IEEEenableoneshotfootnoterulefalse
+\ifCLASSOPTIONcompsoc
+\def\footnoterule{\relax\if@IEEEenableoneshotfootnoterule
+\kern-5pt
+\hbox to \columnwidth{\hfill\vrule width 0.5\columnwidth height 0.4pt\hfill}
+\kern4.6pt
+\global\@IEEEenableoneshotfootnoterulefalse
+\else
+\relax
+\fi}
+\fi
+
+% V1.6 do not allow LaTeX to break a footnote across multiple pages
+\interfootnotelinepenalty=10000
+
+% V1.6 discourage breaks within equations
+% Note that amsmath normally sets this to 10000,
+% but LaTeX2e normally uses 100.
+\interdisplaylinepenalty=2500
+
+% default allows section depth up to /paragraph
+\setcounter{secnumdepth}{4}
+
+% technotes do not allow /paragraph
+\ifCLASSOPTIONtechnote
+   \setcounter{secnumdepth}{3}
+\fi
+% neither do compsoc conferences
+\@IEEEcompsocconfonly{\setcounter{secnumdepth}{3}}
+
+
+\newcounter{section}
+\newcounter{subsection}[section]
+\newcounter{subsubsection}[subsection]
+\newcounter{paragraph}[subsubsection]
+
+% used only by IEEEtran's IEEEeqnarray as other packages may
+% have their own, different, implementations
+\newcounter{IEEEsubequation}[equation]
+
+% as shown when called by user from \ref, \label and in table of contents
+\def\theequation{\arabic{equation}}                          % 1
+\def\theIEEEsubequation{\theequation\alph{IEEEsubequation}}  % 1a (used only by IEEEtran's IEEEeqnarray)
+\ifCLASSOPTIONcompsoc
+% compsoc is all arabic
+\def\thesection{\arabic{section}}                
+\def\thesubsection{\thesection.\arabic{subsection}}
+\def\thesubsubsection{\thesubsection.\arabic{subsubsection}}
+\def\theparagraph{\thesubsubsection.\arabic{paragraph}}
+\else
+\def\thesection{\Roman{section}}                             % I
+% V1.7, \mbox prevents breaks around - 
+\def\thesubsection{\mbox{\thesection-\Alph{subsection}}}     % I-A
+% V1.7 use I-A1 format used by IEEE rather than I-A.1
+\def\thesubsubsection{\thesubsection\arabic{subsubsection}}  % I-A1
+\def\theparagraph{\thesubsubsection\alph{paragraph}}         % I-A1a
+\fi
+
+% From Heiko Oberdiek. Because of the \mbox in \thesubsection, we need to
+% tell hyperref to disable the \mbox command when making PDF bookmarks.
+% This done already with hyperref.sty version 6.74o and later, but
+% it will not hurt to do it here again for users of older versions.
+\@ifundefined{pdfstringdefPreHook}{\let\pdfstringdefPreHook\@empty}{}%
+\g@addto@macro\pdfstringdefPreHook{\let\mbox\relax}
+
+
+% Main text forms (how shown in main text headings)
+% V1.6, using \thesection in \thesectiondis allows changes
+% in the former to automatically appear in the latter
+\ifCLASSOPTIONcompsoc
+  \ifCLASSOPTIONconference% compsoc conference
+    \def\thesectiondis{\thesection.}
+    \def\thesubsectiondis{\thesectiondis\arabic{subsection}.}
+    \def\thesubsubsectiondis{\thesubsectiondis\arabic{subsubsection}.}
+    \def\theparagraphdis{\thesubsubsectiondis\arabic{paragraph}.}
+  \else% compsoc not conferencs
+    \def\thesectiondis{\thesection}
+    \def\thesubsectiondis{\thesectiondis.\arabic{subsection}}
+    \def\thesubsubsectiondis{\thesubsectiondis.\arabic{subsubsection}}
+    \def\theparagraphdis{\thesubsubsectiondis.\arabic{paragraph}}
+  \fi
+\else% not compsoc
+  \def\thesectiondis{\thesection.}                   % I.
+  \def\thesubsectiondis{\Alph{subsection}.}          % B.
+  \def\thesubsubsectiondis{\arabic{subsubsection})}  % 3)
+  \def\theparagraphdis{\alph{paragraph})}            % d)
+\fi
+
+% just like LaTeX2e's \@eqnnum
+\def\theequationdis{{\normalfont \normalcolor (\theequation)}}% (1)
+% IEEEsubequation used only by IEEEtran's IEEEeqnarray
+\def\theIEEEsubequationdis{{\normalfont \normalcolor (\theIEEEsubequation)}}% (1a)
+% redirect LaTeX2e's equation number display and all that depend on
+% it, through IEEEtran's \theequationdis
+\def\@eqnnum{\theequationdis}
+
+
+
+% V1.7 provide string macros as article.cls does
+\def\contentsname{Contents}
+\def\listfigurename{List of Figures}
+\def\listtablename{List of Tables}
+\def\refname{References}
+\def\indexname{Index}
+\def\figurename{Fig.}
+\def\tablename{TABLE}
+\@IEEEcompsocconfonly{\def\figurename{Figure}\def\tablename{Table}}
+\def\partname{Part}
+\def\appendixname{Appendix}
+\def\abstractname{Abstract}
+% IEEE specific names
+\def\IEEEkeywordsname{Index Terms}
+\def\IEEEproofname{Proof}
+
+
+% LIST OF FIGURES AND TABLES AND TABLE OF CONTENTS
+%
+\def\@pnumwidth{1.55em}
+\def\@tocrmarg{2.55em}
+\def\@dotsep{4.5}
+\setcounter{tocdepth}{3}
+
+% adjusted some spacings here so that section numbers will not easily 
+% collide with the section titles. 
+% VIII; VIII-A; and VIII-A.1 are usually the worst offenders.
+% MDS 1/2001
+\def\tableofcontents{\section*{\contentsname}\@starttoc{toc}}
+\def\l@section#1#2{\addpenalty{\@secpenalty}\addvspace{1.0em plus 1pt}%
+    \@tempdima 2.75em \begingroup \parindent \z@ \rightskip \@pnumwidth%
+    \parfillskip-\@pnumwidth {\bfseries\leavevmode #1}\hfil\hbox to\@pnumwidth{\hss #2}\par%
+    \endgroup}
+% argument format #1:level, #2:labelindent,#3:labelsep
+\def\l@subsection{\@dottedtocline{2}{2.75em}{3.75em}}
+\def\l@subsubsection{\@dottedtocline{3}{6.5em}{4.5em}}
+% must provide \l@ defs for ALL sublevels EVEN if tocdepth
+% is such as they will not appear in the table of contents
+% these defs are how TOC knows what level these things are!
+\def\l@paragraph{\@dottedtocline{4}{6.5em}{5.5em}}
+\def\l@subparagraph{\@dottedtocline{5}{6.5em}{6.5em}}
+\def\listoffigures{\section*{\listfigurename}\@starttoc{lof}}
+\def\l@figure{\@dottedtocline{1}{0em}{2.75em}}
+\def\listoftables{\section*{\listtablename}\@starttoc{lot}}
+\let\l@table\l@figure
+
+
+%% Definitions for floats
+%%
+%% Normal Floats
+\floatsep 1\baselineskip plus  0.2\baselineskip minus  0.2\baselineskip
+\textfloatsep 1.7\baselineskip plus  0.2\baselineskip minus  0.4\baselineskip
+\@fptop 0pt plus 1fil
+\@fpsep 0.75\baselineskip plus 2fil 
+\@fpbot 0pt plus 1fil
+\def\topfraction{0.9}
+\def\bottomfraction{0.4}
+\def\floatpagefraction{0.8}
+% V1.7, let top floats approach 90% of page
+\def\textfraction{0.1}
+
+%% Double Column Floats
+\dblfloatsep 1\baselineskip plus  0.2\baselineskip minus  0.2\baselineskip
+
+\dbltextfloatsep 1.7\baselineskip plus  0.2\baselineskip minus  0.4\baselineskip
+% Note that it would be nice if the rubber here actually worked in LaTeX2e.
+% There is a long standing limitation in LaTeX, first discovered (to the best
+% of my knowledge) by Alan Jeffrey in 1992. LaTeX ignores the stretchable
+% portion of \dbltextfloatsep, and as a result, double column figures can and
+% do result in an non-integer number of lines in the main text columns with
+% underfull vbox errors as a consequence. A post to comp.text.tex
+% by Donald Arseneau confirms that this had not yet been fixed in 1998.
+% IEEEtran V1.6 will fix this problem for you in the titles, but it doesn't
+% protect you from other double floats. Happy vspace'ing.
+
+\@dblfptop 0pt plus 1fil
+\@dblfpsep 0.75\baselineskip plus 2fil
+\@dblfpbot 0pt plus 1fil
+\def\dbltopfraction{0.8}
+\def\dblfloatpagefraction{0.8}
+\setcounter{dbltopnumber}{4}
+
+\intextsep 1\baselineskip plus 0.2\baselineskip minus  0.2\baselineskip
+\setcounter{topnumber}{2}
+\setcounter{bottomnumber}{2}
+\setcounter{totalnumber}{4}
+
+
+
+% article class provides these, we should too.
+\newlength\abovecaptionskip
+\newlength\belowcaptionskip
+% but only \abovecaptionskip is used above figure captions and *below* table
+% captions
+\setlength\abovecaptionskip{0.5\baselineskip}
+\setlength\belowcaptionskip{0pt}
+% V1.6 create hooks in case the caption spacing ever needs to be
+% overridden by a user
+\def\@IEEEfigurecaptionsepspace{\vskip\abovecaptionskip\relax}%
+\def\@IEEEtablecaptionsepspace{\vskip\abovecaptionskip\relax}%
+
+
+% 1.6b revise caption system so that \@makecaption uses two arguments
+% as with LaTeX2e. Otherwise, there will be problems when using hyperref.
+\def\@IEEEtablestring{table}
+
+\ifCLASSOPTIONcompsoc
+% V1.7 compsoc \@makecaption
+\ifCLASSOPTIONconference% compsoc conference
+\long\def\@makecaption#1#2{%
+% test if is a for a figure or table
+\ifx\@captype\@IEEEtablestring%
+% if a table, do table caption
+\normalsize\begin{center}{\normalfont\sffamily\normalsize {#1.}~ #2}\end{center}%
+\@IEEEtablecaptionsepspace
+% if not a table, format it as a figure
+\else
+\@IEEEfigurecaptionsepspace
+\setbox\@tempboxa\hbox{\normalfont\sffamily\normalsize {#1.}~ #2}%
+\ifdim \wd\@tempboxa >\hsize%
+% if caption is longer than a line, let it wrap around
+\setbox\@tempboxa\hbox{\normalfont\sffamily\normalsize {#1.}~ }%
+\parbox[t]{\hsize}{\normalfont\sffamily\normalsize \noindent\unhbox\@tempboxa#2}%
+% if caption is shorter than a line, center
+\else%
+\hbox to\hsize{\normalfont\sffamily\normalsize\hfil\box\@tempboxa\hfil}%
+\fi\fi}
+\else% nonconference compsoc
+\long\def\@makecaption#1#2{%
+% test if is a for a figure or table
+\ifx\@captype\@IEEEtablestring%
+% if a table, do table caption
+\normalsize\begin{center}{\normalfont\sffamily\normalsize #1}\\{\normalfont\sffamily\normalsize #2}\end{center}%
+\@IEEEtablecaptionsepspace
+% if not a table, format it as a figure
+\else
+\@IEEEfigurecaptionsepspace
+\setbox\@tempboxa\hbox{\normalfont\sffamily\normalsize {#1.}~ #2}%
+\ifdim \wd\@tempboxa >\hsize%
+% if caption is longer than a line, let it wrap around
+\setbox\@tempboxa\hbox{\normalfont\sffamily\normalsize {#1.}~ }%
+\parbox[t]{\hsize}{\normalfont\sffamily\normalsize \noindent\unhbox\@tempboxa#2}%
+% if caption is shorter than a line, left justify
+\else%
+\hbox to\hsize{\normalfont\sffamily\normalsize\box\@tempboxa\hfil}%
+\fi\fi}
+\fi
+
+\else% traditional noncompsoc \@makecaption
+\long\def\@makecaption#1#2{%
+% test if is a for a figure or table
+\ifx\@captype\@IEEEtablestring%
+% if a table, do table caption
+\footnotesize\begin{center}{\normalfont\footnotesize #1}\\{\normalfont\footnotesize\scshape #2}\end{center}%
+\@IEEEtablecaptionsepspace
+% if not a table, format it as a figure
+\else
+\@IEEEfigurecaptionsepspace
+% 3/2001 use footnotesize, not small; use two nonbreaking spaces, not one
+\setbox\@tempboxa\hbox{\normalfont\footnotesize {#1.}~~ #2}%
+\ifdim \wd\@tempboxa >\hsize%
+% if caption is longer than a line, let it wrap around
+\setbox\@tempboxa\hbox{\normalfont\footnotesize {#1.}~~ }%
+\parbox[t]{\hsize}{\normalfont\footnotesize\noindent\unhbox\@tempboxa#2}%
+% if caption is shorter than a line, center if conference, left justify otherwise
+\else%
+\ifCLASSOPTIONconference \hbox to\hsize{\normalfont\footnotesize\hfil\box\@tempboxa\hfil}%
+\else \hbox to\hsize{\normalfont\footnotesize\box\@tempboxa\hfil}%
+\fi\fi\fi}
+\fi
+
+
+
+% V1.7 disable captions class option, do so in a way that retains operation of \label
+% within \caption
+\ifCLASSOPTIONcaptionsoff
+\long\def\@makecaption#1#2{\vspace*{2em}\footnotesize\begin{center}{\footnotesize #1}\end{center}%
+\let\@IEEEtemporiglabeldefsave\label
+\let\@IEEEtemplabelargsave\relax
+\def\label##1{\gdef\@IEEEtemplabelargsave{##1}}%
+\setbox\@tempboxa\hbox{#2}%
+\let\label\@IEEEtemporiglabeldefsave
+\ifx\@IEEEtemplabelargsave\relax\else\label{\@IEEEtemplabelargsave}\fi}
+\fi
+
+
+% V1.7 define end environments with \def not \let so as to work OK with
+% preview-latex
+\newcounter{figure}
+\def\thefigure{\@arabic\c@figure}
+\def\fps@figure{tbp}
+\def\ftype@figure{1}
+\def\ext@figure{lof}
+\def\fnum@figure{\figurename~\thefigure}
+\def\figure{\@float{figure}}
+\def\endfigure{\end@float}
+\@namedef{figure*}{\@dblfloat{figure}}
+\@namedef{endfigure*}{\end@dblfloat}
+\newcounter{table}
+\ifCLASSOPTIONcompsoc
+\def\thetable{\arabic{table}}
+\else
+\def\thetable{\@Roman\c@table}
+\fi
+\def\fps@table{tbp}
+\def\ftype@table{2}
+\def\ext@table{lot}
+\def\fnum@table{\tablename~\thetable}
+% V1.6 IEEE uses 8pt text for tables
+% to default to footnotesize, we hack into LaTeX2e's \@floatboxreset and pray
+\def\table{\def\@floatboxreset{\reset@font\footnotesize\@setminipage}\@float{table}}
+\def\endtable{\end@float}
+% v1.6b double column tables need to default to footnotesize as well.
+\@namedef{table*}{\def\@floatboxreset{\reset@font\footnotesize\@setminipage}\@dblfloat{table}}
+\@namedef{endtable*}{\end@dblfloat}
+
+
+
+
+%%
+%% START OF IEEEeqnarry DEFINITIONS
+%%
+%% Inspired by the concepts, examples, and previous works of LaTeX 
+%% coders and developers such as Donald Arseneau, Fred Bartlett, 
+%% David Carlisle, Tony Liu, Frank Mittelbach, Piet van Oostrum, 
+%% Roland Winkler and Mark Wooding.
+%% I don't make the claim that my work here is even near their calibre. ;)
+
+
+% hook to allow easy changeover to IEEEtran.cls/tools.sty error reporting
+\def\@IEEEclspkgerror{\ClassError{IEEEtran}}
+
+\newif\if@IEEEeqnarraystarform% flag to indicate if the environment was called as the star form
+\@IEEEeqnarraystarformfalse
+
+\newif\if@advanceIEEEeqncolcnt% tracks if the environment should advance the col counter
+% allows a way to make an \IEEEeqnarraybox that can be used within an \IEEEeqnarray
+% used by IEEEeqnarraymulticol so that it can work properly in both
+\@advanceIEEEeqncolcnttrue
+
+\newcount\@IEEEeqnnumcols % tracks how many IEEEeqnarray cols are defined
+\newcount\@IEEEeqncolcnt  % tracks how many IEEEeqnarray cols the user actually used
+
+
+% The default math style used by the columns
+\def\IEEEeqnarraymathstyle{\displaystyle}
+% The default text style used by the columns
+% default to using the current font
+\def\IEEEeqnarraytextstyle{\relax}
+
+% like the iedlistdecl but for \IEEEeqnarray
+\def\IEEEeqnarraydecl{\relax}
+\def\IEEEeqnarrayboxdecl{\relax}
+
+% \yesnumber is the opposite of \nonumber
+% a novel concept with the same def as the equationarray package
+% However, we give IEEE versions too since some LaTeX packages such as 
+% the MDWtools mathenv.sty redefine \nonumber to something else.
+\providecommand{\yesnumber}{\global\@eqnswtrue}
+\def\IEEEyesnumber{\global\@eqnswtrue}
+\def\IEEEnonumber{\global\@eqnswfalse}
+
+
+\def\IEEEyessubnumber{\global\@IEEEissubequationtrue\global\@eqnswtrue%
+\if@IEEEeqnarrayISinner% only do something inside an IEEEeqnarray
+\if@IEEElastlinewassubequation\addtocounter{equation}{-1}\else\setcounter{IEEEsubequation}{1}\fi%
+\def\@currentlabel{\p@IEEEsubequation\theIEEEsubequation}\fi}
+
+% flag to indicate that an equation is a sub equation
+\newif\if@IEEEissubequation%
+\@IEEEissubequationfalse
+
+% allows users to "push away" equations that get too close to the equation numbers
+\def\IEEEeqnarraynumspace{\hphantom{\if@IEEEissubequation\theIEEEsubequationdis\else\theequationdis\fi}}
+
+% provides a way to span multiple columns within IEEEeqnarray environments
+% will consider \if@advanceIEEEeqncolcnt before globally advancing the
+% column counter - so as to work within \IEEEeqnarraybox
+% usage: \IEEEeqnarraymulticol{number cols. to span}{col type}{cell text}
+\long\def\IEEEeqnarraymulticol#1#2#3{\multispan{#1}%
+% check if column is defined
+\relax\expandafter\ifx\csname @IEEEeqnarraycolDEF#2\endcsname\@IEEEeqnarraycolisdefined%
+\csname @IEEEeqnarraycolPRE#2\endcsname#3\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\csname @IEEEeqnarraycolPOST#2\endcsname%
+\else% if not, error and use default type
+\@IEEEclspkgerror{Invalid column type "#2" in \string\IEEEeqnarraymulticol.\MessageBreak
+Using a default centering column instead}%
+{You must define IEEEeqnarray column types before use.}%
+\csname @IEEEeqnarraycolPRE@IEEEdefault\endcsname#3\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\csname @IEEEeqnarraycolPOST@IEEEdefault\endcsname%
+\fi%
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by #1\relax\fi}
+
+% like \omit, but maintains track of the column counter for \IEEEeqnarray
+\def\IEEEeqnarrayomit{\omit\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by 1\relax\fi}
+
+
+% provides a way to define a letter referenced column type
+% usage: \IEEEeqnarraydefcol{col. type letter/name}{pre insertion text}{post insertion text}
+\def\IEEEeqnarraydefcol#1#2#3{\expandafter\def\csname @IEEEeqnarraycolPRE#1\endcsname{#2}%
+\expandafter\def\csname @IEEEeqnarraycolPOST#1\endcsname{#3}%
+\expandafter\def\csname @IEEEeqnarraycolDEF#1\endcsname{1}}
+
+
+% provides a way to define a numerically referenced inter-column glue types
+% usage: \IEEEeqnarraydefcolsep{col. glue number}{glue definition}
+\def\IEEEeqnarraydefcolsep#1#2{\expandafter\def\csname @IEEEeqnarraycolSEP\romannumeral #1\endcsname{#2}%
+\expandafter\def\csname @IEEEeqnarraycolSEPDEF\romannumeral #1\endcsname{1}}
+
+
+\def\@IEEEeqnarraycolisdefined{1}% just a macro for 1, used for checking undefined column types
+
+
+% expands and appends the given argument to the \@IEEEtrantmptoksA token list
+% used to build up the \halign preamble
+\def\@IEEEappendtoksA#1{\edef\@@IEEEappendtoksA{\@IEEEtrantmptoksA={\the\@IEEEtrantmptoksA #1}}%
+\@@IEEEappendtoksA}
+
+% also appends to \@IEEEtrantmptoksA, but does not expand the argument
+% uses \toks8 as a scratchpad register
+\def\@IEEEappendNOEXPANDtoksA#1{\toks8={#1}%
+\edef\@@IEEEappendNOEXPANDtoksA{\@IEEEtrantmptoksA={\the\@IEEEtrantmptoksA\the\toks8}}%
+\@@IEEEappendNOEXPANDtoksA}
+
+% define some common column types for the user
+% math
+\IEEEeqnarraydefcol{l}{$\IEEEeqnarraymathstyle}{$\hfil}
+\IEEEeqnarraydefcol{c}{\hfil$\IEEEeqnarraymathstyle}{$\hfil}
+\IEEEeqnarraydefcol{r}{\hfil$\IEEEeqnarraymathstyle}{$}
+\IEEEeqnarraydefcol{L}{$\IEEEeqnarraymathstyle{}}{{}$\hfil}
+\IEEEeqnarraydefcol{C}{\hfil$\IEEEeqnarraymathstyle{}}{{}$\hfil}
+\IEEEeqnarraydefcol{R}{\hfil$\IEEEeqnarraymathstyle{}}{{}$}
+% text
+\IEEEeqnarraydefcol{s}{\IEEEeqnarraytextstyle}{\hfil}
+\IEEEeqnarraydefcol{t}{\hfil\IEEEeqnarraytextstyle}{\hfil}
+\IEEEeqnarraydefcol{u}{\hfil\IEEEeqnarraytextstyle}{}
+
+% vertical rules
+\IEEEeqnarraydefcol{v}{}{\vrule width\arrayrulewidth}
+\IEEEeqnarraydefcol{vv}{\vrule width\arrayrulewidth\hfil}{\hfil\vrule width\arrayrulewidth}
+\IEEEeqnarraydefcol{V}{}{\vrule width\arrayrulewidth\hskip\doublerulesep\vrule width\arrayrulewidth}
+\IEEEeqnarraydefcol{VV}{\vrule width\arrayrulewidth\hskip\doublerulesep\vrule width\arrayrulewidth\hfil}%
+{\hfil\vrule width\arrayrulewidth\hskip\doublerulesep\vrule width\arrayrulewidth}
+
+% horizontal rules
+\IEEEeqnarraydefcol{h}{}{\leaders\hrule height\arrayrulewidth\hfil}
+\IEEEeqnarraydefcol{H}{}{\leaders\vbox{\hrule width\arrayrulewidth\vskip\doublerulesep\hrule width\arrayrulewidth}\hfil}
+
+% plain
+\IEEEeqnarraydefcol{x}{}{}
+\IEEEeqnarraydefcol{X}{$}{$}
+
+% the default column type to use in the event a column type is not defined
+\IEEEeqnarraydefcol{@IEEEdefault}{\hfil$\IEEEeqnarraymathstyle}{$\hfil}
+
+
+% a zero tabskip (used for "-" col types)
+\def\@IEEEeqnarraycolSEPzero{0pt plus 0pt minus 0pt}
+% a centering tabskip (used for "+" col types)
+\def\@IEEEeqnarraycolSEPcenter{1000pt plus 0pt minus 1000pt}
+
+% top level default tabskip glues for the start, end, and inter-column
+% may be reset within environments not always at the top level, e.g., \IEEEeqnarraybox
+\edef\@IEEEeqnarraycolSEPdefaultstart{\@IEEEeqnarraycolSEPcenter}% default start glue
+\edef\@IEEEeqnarraycolSEPdefaultend{\@IEEEeqnarraycolSEPcenter}% default end glue
+\edef\@IEEEeqnarraycolSEPdefaultmid{\@IEEEeqnarraycolSEPzero}% default inter-column glue
+
+
+
+% creates a vertical rule that extends from the bottom to the top a a cell
+% Provided in case other packages redefine \vline some other way.
+% usage: \IEEEeqnarrayvrule[rule thickness]
+% If no argument is provided, \arrayrulewidth will be used for the rule thickness. 
+\newcommand\IEEEeqnarrayvrule[1][\arrayrulewidth]{\vrule\@width#1\relax}
+
+% creates a blank separator row
+% usage: \IEEEeqnarrayseprow[separation length][font size commands]
+% default is \IEEEeqnarrayseprow[0.25\normalbaselineskip][\relax]
+% blank arguments inherit the default values
+% uses \skip5 as a scratch register - calls \@IEEEeqnarraystrutsize which uses more scratch registers
+\def\IEEEeqnarrayseprow{\relax\@ifnextchar[{\@IEEEeqnarrayseprow}{\@IEEEeqnarrayseprow[0.25\normalbaselineskip]}}
+\def\@IEEEeqnarrayseprow[#1]{\relax\@ifnextchar[{\@@IEEEeqnarrayseprow[#1]}{\@@IEEEeqnarrayseprow[#1][\relax]}}
+\def\@@IEEEeqnarrayseprow[#1][#2]{\def\@IEEEeqnarrayseprowARGONE{#1}%
+\ifx\@IEEEeqnarrayseprowARGONE\@empty%
+% get the skip value, based on the font commands
+% use skip5 because \IEEEeqnarraystrutsize uses \skip0, \skip2, \skip3
+% assign within a bogus box to confine the font changes
+{\setbox0=\hbox{#2\relax\global\skip5=0.25\normalbaselineskip}}%
+\else%
+{\setbox0=\hbox{#2\relax\global\skip5=#1}}%
+\fi%
+\@IEEEeqnarrayhoptolastcolumn\IEEEeqnarraystrutsize{\skip5}{0pt}[\relax]\relax}
+
+% creates a blank separator row, but omits all the column templates
+% usage: \IEEEeqnarrayseprowcut[separation length][font size commands]
+% default is \IEEEeqnarrayseprowcut[0.25\normalbaselineskip][\relax]
+% blank arguments inherit the default values
+% uses \skip5 as a scratch register - calls \@IEEEeqnarraystrutsize which uses more scratch registers
+\def\IEEEeqnarrayseprowcut{\multispan{\@IEEEeqnnumcols}\relax% span all the cols
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\@ifnextchar[{\@IEEEeqnarrayseprowcut}{\@IEEEeqnarrayseprowcut[0.25\normalbaselineskip]}}
+\def\@IEEEeqnarrayseprowcut[#1]{\relax\@ifnextchar[{\@@IEEEeqnarrayseprowcut[#1]}{\@@IEEEeqnarrayseprowcut[#1][\relax]}}
+\def\@@IEEEeqnarrayseprowcut[#1][#2]{\def\@IEEEeqnarrayseprowARGONE{#1}%
+\ifx\@IEEEeqnarrayseprowARGONE\@empty%
+% get the skip value, based on the font commands
+% use skip5 because \IEEEeqnarraystrutsize uses \skip0, \skip2, \skip3
+% assign within a bogus box to confine the font changes
+{\setbox0=\hbox{#2\relax\global\skip5=0.25\normalbaselineskip}}%
+\else%
+{\setbox0=\hbox{#2\relax\global\skip5=#1}}%
+\fi%
+\IEEEeqnarraystrutsize{\skip5}{0pt}[\relax]\relax}
+
+
+
+% draws a single rule across all the columns optional
+% argument determines the rule width, \arrayrulewidth is the default
+% updates column counter as needed and turns off struts
+% usage: \IEEEeqnarrayrulerow[rule line thickness]
+\def\IEEEeqnarrayrulerow{\multispan{\@IEEEeqnnumcols}\relax% span all the cols
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\@ifnextchar[{\@IEEEeqnarrayrulerow}{\@IEEEeqnarrayrulerow[\arrayrulewidth]}}
+\def\@IEEEeqnarrayrulerow[#1]{\leaders\hrule height#1\hfil\relax% put in our rule 
+% turn off any struts
+\IEEEeqnarraystrutsize{0pt}{0pt}[\relax]\relax}
+
+
+% draws a double rule by using a single rule row, a separator row, and then
+% another single rule row 
+% first optional argument determines the rule thicknesses, \arrayrulewidth is the default
+% second optional argument determines the rule spacing, \doublerulesep is the default
+% usage: \IEEEeqnarraydblrulerow[rule line thickness][rule spacing]
+\def\IEEEeqnarraydblrulerow{\multispan{\@IEEEeqnnumcols}\relax% span all the cols
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\@ifnextchar[{\@IEEEeqnarraydblrulerow}{\@IEEEeqnarraydblrulerow[\arrayrulewidth]}}
+\def\@IEEEeqnarraydblrulerow[#1]{\relax\@ifnextchar[{\@@IEEEeqnarraydblrulerow[#1]}%
+{\@@IEEEeqnarraydblrulerow[#1][\doublerulesep]}}
+\def\@@IEEEeqnarraydblrulerow[#1][#2]{\def\@IEEEeqnarraydblrulerowARG{#1}%
+% we allow the user to say \IEEEeqnarraydblrulerow[][]
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\@IEEEeqnarrayrulerow[\arrayrulewidth]%
+\else%
+\@IEEEeqnarrayrulerow[#1]\relax%
+\fi%
+\def\@IEEEeqnarraydblrulerowARG{#2}%
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\\\IEEEeqnarrayseprow[\doublerulesep][\relax]%
+\else%
+\\\IEEEeqnarrayseprow[#2][\relax]%
+\fi%
+\\\multispan{\@IEEEeqnnumcols}%
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\def\@IEEEeqnarraydblrulerowARG{#1}%
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\@IEEEeqnarrayrulerow[\arrayrulewidth]%
+\else%
+\@IEEEeqnarrayrulerow[#1]%
+\fi%
+}
+
+% draws a double rule by using a single rule row, a separator (cutting) row, and then
+% another single rule row 
+% first optional argument determines the rule thicknesses, \arrayrulewidth is the default
+% second optional argument determines the rule spacing, \doublerulesep is the default
+% usage: \IEEEeqnarraydblrulerow[rule line thickness][rule spacing]
+\def\IEEEeqnarraydblrulerowcut{\multispan{\@IEEEeqnnumcols}\relax% span all the cols
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\@ifnextchar[{\@IEEEeqnarraydblrulerowcut}{\@IEEEeqnarraydblrulerowcut[\arrayrulewidth]}}
+\def\@IEEEeqnarraydblrulerowcut[#1]{\relax\@ifnextchar[{\@@IEEEeqnarraydblrulerowcut[#1]}%
+{\@@IEEEeqnarraydblrulerowcut[#1][\doublerulesep]}}
+\def\@@IEEEeqnarraydblrulerowcut[#1][#2]{\def\@IEEEeqnarraydblrulerowARG{#1}%
+% we allow the user to say \IEEEeqnarraydblrulerow[][]
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\@IEEEeqnarrayrulerow[\arrayrulewidth]%
+\else%
+\@IEEEeqnarrayrulerow[#1]%
+\fi%
+\def\@IEEEeqnarraydblrulerowARG{#2}%
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\\\IEEEeqnarrayseprowcut[\doublerulesep][\relax]%
+\else%
+\\\IEEEeqnarrayseprowcut[#2][\relax]%
+\fi%
+\\\multispan{\@IEEEeqnnumcols}%
+% advance column counter only if the IEEEeqnarray environment wants it
+\if@advanceIEEEeqncolcnt\global\advance\@IEEEeqncolcnt by \@IEEEeqnnumcols\relax\fi%
+\def\@IEEEeqnarraydblrulerowARG{#1}%
+\ifx\@IEEEeqnarraydblrulerowARG\@empty%
+\@IEEEeqnarrayrulerow[\arrayrulewidth]%
+\else%
+\@IEEEeqnarrayrulerow[#1]%
+\fi%
+}
+
+
+
+% inserts a full row's worth of &'s
+% relies on \@IEEEeqnnumcols to provide the correct number of columns
+% uses \@IEEEtrantmptoksA, \count0 as scratch registers
+\def\@IEEEeqnarrayhoptolastcolumn{\@IEEEtrantmptoksA={}\count0=1\relax%
+\loop% add cols if the user did not use them all
+\ifnum\count0<\@IEEEeqnnumcols\relax%
+\@IEEEappendtoksA{&}%
+\advance\count0 by 1\relax% update the col count
+\repeat%
+\the\@IEEEtrantmptoksA%execute the &'s
+}
+
+
+
+\newif\if@IEEEeqnarrayISinner % flag to indicate if we are within the lines
+\@IEEEeqnarrayISinnerfalse    % of an IEEEeqnarray - after the IEEEeqnarraydecl
+
+\edef\@IEEEeqnarrayTHEstrutheight{0pt} % height and depth of IEEEeqnarray struts
+\edef\@IEEEeqnarrayTHEstrutdepth{0pt}
+
+\edef\@IEEEeqnarrayTHEmasterstrutheight{0pt} % default height and depth of
+\edef\@IEEEeqnarrayTHEmasterstrutdepth{0pt}  % struts within an IEEEeqnarray
+
+\edef\@IEEEeqnarrayTHEmasterstrutHSAVE{0pt} % saved master strut height
+\edef\@IEEEeqnarrayTHEmasterstrutDSAVE{0pt} % and depth
+
+\newif\if@IEEEeqnarrayusemasterstrut % flag to indicate that the master strut value
+\@IEEEeqnarrayusemasterstruttrue     % is to be used
+
+
+
+% saves the strut height and depth of the master strut
+\def\@IEEEeqnarraymasterstrutsave{\relax%
+\expandafter\skip0=\@IEEEeqnarrayTHEmasterstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEmasterstrutdepth\relax%
+% remove stretchability
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% save values
+\edef\@IEEEeqnarrayTHEmasterstrutHSAVE{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEmasterstrutDSAVE{\the\dimen2}}
+
+% restores the strut height and depth of the master strut
+\def\@IEEEeqnarraymasterstrutrestore{\relax%
+\expandafter\skip0=\@IEEEeqnarrayTHEmasterstrutHSAVE\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEmasterstrutDSAVE\relax%
+% remove stretchability
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% restore values
+\edef\@IEEEeqnarrayTHEmasterstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEmasterstrutdepth{\the\dimen2}}
+
+
+% globally restores the strut height and depth to the 
+% master values and sets the master strut flag to true
+\def\@IEEEeqnarraystrutreset{\relax%
+\expandafter\skip0=\@IEEEeqnarrayTHEmasterstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEmasterstrutdepth\relax%
+% remove stretchability
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% restore values
+\xdef\@IEEEeqnarrayTHEstrutheight{\the\dimen0}%
+\xdef\@IEEEeqnarrayTHEstrutdepth{\the\dimen2}%
+\global\@IEEEeqnarrayusemasterstruttrue}
+
+
+% if the master strut is not to be used, make the current
+% values of \@IEEEeqnarrayTHEstrutheight, \@IEEEeqnarrayTHEstrutdepth
+% and the use master strut flag, global
+% this allows user strut commands issued in the last column to be carried
+% into the isolation/strut column
+\def\@IEEEeqnarrayglobalizestrutstatus{\relax%
+\if@IEEEeqnarrayusemasterstrut\else%
+\xdef\@IEEEeqnarrayTHEstrutheight{\@IEEEeqnarrayTHEstrutheight}%
+\xdef\@IEEEeqnarrayTHEstrutdepth{\@IEEEeqnarrayTHEstrutdepth}%
+\global\@IEEEeqnarrayusemasterstrutfalse%
+\fi}
+
+
+
+% usage: \IEEEeqnarraystrutsize{height}{depth}[font size commands]
+% If called outside the lines of an IEEEeqnarray, sets the height
+% and depth of both the master and local struts. If called inside
+% an IEEEeqnarray line, sets the height and depth of the local strut
+% only and sets the flag to indicate the use of the local strut
+% values. If the height or depth is left blank, 0.7\normalbaselineskip
+% and 0.3\normalbaselineskip will be used, respectively.
+% The optional argument can be used to evaluate the lengths under
+% a different font size and styles. If none is specified, the current
+% font is used.
+% uses scratch registers \skip0, \skip2, \skip3, \dimen0, \dimen2
+\def\IEEEeqnarraystrutsize#1#2{\relax\@ifnextchar[{\@IEEEeqnarraystrutsize{#1}{#2}}{\@IEEEeqnarraystrutsize{#1}{#2}[\relax]}}
+\def\@IEEEeqnarraystrutsize#1#2[#3]{\def\@IEEEeqnarraystrutsizeARG{#1}%
+\ifx\@IEEEeqnarraystrutsizeARG\@empty%
+{\setbox0=\hbox{#3\relax\global\skip3=0.7\normalbaselineskip}}%
+\skip0=\skip3\relax%
+\else% arg one present
+{\setbox0=\hbox{#3\relax\global\skip3=#1\relax}}%
+\skip0=\skip3\relax%
+\fi% if null arg
+\def\@IEEEeqnarraystrutsizeARG{#2}%
+\ifx\@IEEEeqnarraystrutsizeARG\@empty%
+{\setbox0=\hbox{#3\relax\global\skip3=0.3\normalbaselineskip}}%
+\skip2=\skip3\relax%
+\else% arg two present
+{\setbox0=\hbox{#3\relax\global\skip3=#2\relax}}%
+\skip2=\skip3\relax%
+\fi% if null arg
+% remove stretchability, just to be safe
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% dimen0 = height, dimen2 = depth
+\if@IEEEeqnarrayISinner% inner does not touch master strut size
+\edef\@IEEEeqnarrayTHEstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEstrutdepth{\the\dimen2}%
+\@IEEEeqnarrayusemasterstrutfalse% do not use master
+\else% outer, have to set master strut too
+\edef\@IEEEeqnarrayTHEmasterstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEmasterstrutdepth{\the\dimen2}%
+\edef\@IEEEeqnarrayTHEstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEstrutdepth{\the\dimen2}%
+\@IEEEeqnarrayusemasterstruttrue% use master strut
+\fi}
+
+
+% usage: \IEEEeqnarraystrutsizeadd{added height}{added depth}[font size commands]
+% If called outside the lines of an IEEEeqnarray, adds the given height
+% and depth to both the master and local struts.
+% If called inside an IEEEeqnarray line, adds the given height and depth
+% to the local strut only and sets the flag to indicate the use 
+% of the local strut values.
+% In both cases, if a height or depth is left blank, 0pt is used instead.
+% The optional argument can be used to evaluate the lengths under
+% a different font size and styles. If none is specified, the current
+% font is used.
+% uses scratch registers \skip0, \skip2, \skip3, \dimen0, \dimen2
+\def\IEEEeqnarraystrutsizeadd#1#2{\relax\@ifnextchar[{\@IEEEeqnarraystrutsizeadd{#1}{#2}}{\@IEEEeqnarraystrutsizeadd{#1}{#2}[\relax]}}
+\def\@IEEEeqnarraystrutsizeadd#1#2[#3]{\def\@IEEEeqnarraystrutsizearg{#1}%
+\ifx\@IEEEeqnarraystrutsizearg\@empty%
+\skip0=0pt\relax%
+\else% arg one present
+{\setbox0=\hbox{#3\relax\global\skip3=#1}}%
+\skip0=\skip3\relax%
+\fi% if null arg
+\def\@IEEEeqnarraystrutsizearg{#2}%
+\ifx\@IEEEeqnarraystrutsizearg\@empty%
+\skip2=0pt\relax%
+\else% arg two present
+{\setbox0=\hbox{#3\relax\global\skip3=#2}}%
+\skip2=\skip3\relax%
+\fi% if null arg
+% remove stretchability, just to be safe
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% dimen0 = height, dimen2 = depth
+\if@IEEEeqnarrayISinner% inner does not touch master strut size
+% get local strut size
+\expandafter\skip0=\@IEEEeqnarrayTHEstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEstrutdepth\relax%
+% add it to the user supplied values
+\advance\dimen0 by \skip0\relax%
+\advance\dimen2 by \skip2\relax%
+% update the local strut size
+\edef\@IEEEeqnarrayTHEstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEstrutdepth{\the\dimen2}%
+\@IEEEeqnarrayusemasterstrutfalse% do not use master
+\else% outer, have to set master strut too
+% get master strut size
+\expandafter\skip0=\@IEEEeqnarrayTHEmasterstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEmasterstrutdepth\relax%
+% add it to the user supplied values
+\advance\dimen0 by \skip0\relax%
+\advance\dimen2 by \skip2\relax%
+% update the local and master strut sizes
+\edef\@IEEEeqnarrayTHEmasterstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEmasterstrutdepth{\the\dimen2}%
+\edef\@IEEEeqnarrayTHEstrutheight{\the\dimen0}%
+\edef\@IEEEeqnarrayTHEstrutdepth{\the\dimen2}%
+\@IEEEeqnarrayusemasterstruttrue% use master strut
+\fi}
+
+
+% allow user a way to see the struts
+\newif\ifIEEEvisiblestruts
+\IEEEvisiblestrutsfalse
+
+% inserts an invisible strut using the master or local strut values
+% uses scratch registers \skip0, \skip2, \dimen0, \dimen2
+\def\@IEEEeqnarrayinsertstrut{\relax%
+\if@IEEEeqnarrayusemasterstrut
+% get master strut size
+\expandafter\skip0=\@IEEEeqnarrayTHEmasterstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEmasterstrutdepth\relax%
+\else%
+% get local strut size
+\expandafter\skip0=\@IEEEeqnarrayTHEstrutheight\relax%
+\expandafter\skip2=\@IEEEeqnarrayTHEstrutdepth\relax%
+\fi%
+% remove stretchability, probably not needed
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+% dimen0 = height, dimen2 = depth
+% allow user to see struts if desired
+\ifIEEEvisiblestruts%
+\vrule width0.2pt height\dimen0 depth\dimen2\relax%
+\else%
+\vrule width0pt height\dimen0 depth\dimen2\relax\fi}
+
+
+% creates an invisible strut, useable even outside \IEEEeqnarray
+% if \IEEEvisiblestrutstrue, the strut will be visible and 0.2pt wide. 
+% usage: \IEEEstrut[height][depth][font size commands]
+% default is \IEEEstrut[0.7\normalbaselineskip][0.3\normalbaselineskip][\relax]
+% blank arguments inherit the default values
+% uses \dimen0, \dimen2, \skip0, \skip2
+\def\IEEEstrut{\relax\@ifnextchar[{\@IEEEstrut}{\@IEEEstrut[0.7\normalbaselineskip]}}
+\def\@IEEEstrut[#1]{\relax\@ifnextchar[{\@@IEEEstrut[#1]}{\@@IEEEstrut[#1][0.3\normalbaselineskip]}}
+\def\@@IEEEstrut[#1][#2]{\relax\@ifnextchar[{\@@@IEEEstrut[#1][#2]}{\@@@IEEEstrut[#1][#2][\relax]}}
+\def\@@@IEEEstrut[#1][#2][#3]{\mbox{#3\relax%
+\def\@IEEEstrutARG{#1}%
+\ifx\@IEEEstrutARG\@empty%
+\skip0=0.7\normalbaselineskip\relax%
+\else%
+\skip0=#1\relax%
+\fi%
+\def\@IEEEstrutARG{#2}%
+\ifx\@IEEEstrutARG\@empty%
+\skip2=0.3\normalbaselineskip\relax%
+\else%
+\skip2=#2\relax%
+\fi%
+% remove stretchability, probably not needed
+\dimen0\skip0\relax%
+\dimen2\skip2\relax%
+\ifIEEEvisiblestruts%
+\vrule width0.2pt height\dimen0 depth\dimen2\relax%
+\else%
+\vrule width0.0pt height\dimen0 depth\dimen2\relax\fi}}
+
+
+% enables strut mode by setting a default strut size and then zeroing the
+% \baselineskip, \lineskip, \lineskiplimit and \jot
+\def\IEEEeqnarraystrutmode{\IEEEeqnarraystrutsize{0.7\normalbaselineskip}{0.3\normalbaselineskip}[\relax]%
+\baselineskip=0pt\lineskip=0pt\lineskiplimit=0pt\jot=0pt}
+
+
+
+\def\IEEEeqnarray{\@IEEEeqnarraystarformfalse\@IEEEeqnarray}
+\def\endIEEEeqnarray{\end@IEEEeqnarray}
+
+\@namedef{IEEEeqnarray*}{\@IEEEeqnarraystarformtrue\@IEEEeqnarray}
+\@namedef{endIEEEeqnarray*}{\end@IEEEeqnarray}
+
+
+% \IEEEeqnarray is an enhanced \eqnarray. 
+% The star form defaults to not putting equation numbers at the end of each row.
+% usage: \IEEEeqnarray[decl]{cols}
+\def\@IEEEeqnarray{\relax\@ifnextchar[{\@@IEEEeqnarray}{\@@IEEEeqnarray[\relax]}}
+\def\@@IEEEeqnarray[#1]#2{%
+   % default to showing the equation number or not based on whether or not
+   % the star form was involked
+   \if@IEEEeqnarraystarform\global\@eqnswfalse
+   \else% not the star form
+   \global\@eqnswtrue
+   \fi% if star form
+   \@IEEEissubequationfalse% default to no subequations
+   \@IEEElastlinewassubequationfalse% assume last line is not a sub equation
+   \@IEEEeqnarrayISinnerfalse% not yet within the lines of the halign
+   \@IEEEeqnarraystrutsize{0pt}{0pt}[\relax]% turn off struts by default
+   \@IEEEeqnarrayusemasterstruttrue% use master strut till user asks otherwise
+   \IEEEvisiblestrutsfalse% diagnostic mode defaults to off
+   % no extra space unless the user specifically requests it
+   \lineskip=0pt\relax
+   \lineskiplimit=0pt\relax
+   \baselineskip=\normalbaselineskip\relax%
+   \jot=\IEEEnormaljot\relax%
+   \mathsurround\z@\relax% no extra spacing around math
+   \@advanceIEEEeqncolcnttrue% advance the col counter for each col the user uses, 
+                             % used in \IEEEeqnarraymulticol and in the preamble build
+   \stepcounter{equation}% advance equation counter before first line
+   \setcounter{IEEEsubequation}{0}% no subequation yet 
+   \def\@currentlabel{\p@equation\theequation}% redefine the ref label
+   \IEEEeqnarraydecl\relax% allow a way for the user to make global overrides
+   #1\relax% allow user to override defaults
+   \let\\\@IEEEeqnarraycr% replace newline with one that can put in eqn. numbers
+   \global\@IEEEeqncolcnt\z@% col. count = 0 for first line
+   \@IEEEbuildpreamble #2\end\relax% build the preamble and put it into \@IEEEtrantmptoksA 
+   % put in the column for the equation number
+   \ifnum\@IEEEeqnnumcols>0\relax\@IEEEappendtoksA{&}\fi% col separator for those after the first
+   \toks0={##}%
+   % advance the \@IEEEeqncolcnt for the isolation col, this helps with error checking
+   \@IEEEappendtoksA{\global\advance\@IEEEeqncolcnt by 1\relax}%
+   % add the isolation column
+   \@IEEEappendtoksA{\tabskip\z@skip\bgroup\the\toks0\egroup}%
+   % advance the \@IEEEeqncolcnt for the equation number col, this helps with error checking
+   \@IEEEappendtoksA{&\global\advance\@IEEEeqncolcnt by 1\relax}%
+   % add the equation number col to the preamble
+   \@IEEEappendtoksA{\tabskip\z@skip\hb@xt@\z@\bgroup\hss\the\toks0\egroup}%
+   % note \@IEEEeqnnumcols does not count the equation col or isolation col
+   % set the starting tabskip glue as determined by the preamble build
+   \tabskip=\@IEEEBPstartglue\relax
+   % begin the display alignment
+   \@IEEEeqnarrayISinnertrue% commands are now within the lines
+   $$\everycr{}\halign to\displaywidth\bgroup
+   % "exspand" the preamble
+   \span\the\@IEEEtrantmptoksA\cr}
+
+% enter isolation/strut column (or the next column if the user did not use
+% every column), record the strut status, complete the columns, do the strut if needed,
+% restore counters to correct values and exit
+\def\end@IEEEeqnarray{\@IEEEeqnarrayglobalizestrutstatus&\@@IEEEeqnarraycr\egroup%
+\if@IEEElastlinewassubequation\global\advance\c@IEEEsubequation\m@ne\fi%
+\global\advance\c@equation\m@ne%
+$$\@ignoretrue}
+
+% need a way to remember if last line is a subequation
+\newif\if@IEEElastlinewassubequation%
+\@IEEElastlinewassubequationfalse
+
+% IEEEeqnarray uses a modifed \\ instead of the plain \cr to
+% end rows. This allows for things like \\*[vskip amount]
+% This "cr" macros are modified versions those for LaTeX2e's eqnarray
+% the {\ifnum0=`} braces must be kept away from the last column to avoid
+% altering spacing of its math, so we use & to advance to the next column
+% as there is an isolation/strut column after the user's columns
+\def\@IEEEeqnarraycr{\@IEEEeqnarrayglobalizestrutstatus&% save strut status and advance to next column
+   {\ifnum0=`}\fi
+   \@ifstar{%
+      \global\@eqpen\@M\@IEEEeqnarrayYCR
+   }{%
+      \global\@eqpen\interdisplaylinepenalty \@IEEEeqnarrayYCR
+   }%
+}
+
+\def\@IEEEeqnarrayYCR{\@testopt\@IEEEeqnarrayXCR\z@skip}
+
+\def\@IEEEeqnarrayXCR[#1]{%
+   \ifnum0=`{\fi}%
+   \@@IEEEeqnarraycr
+   \noalign{\penalty\@eqpen\vskip\jot\vskip #1\relax}}%
+
+\def\@@IEEEeqnarraycr{\@IEEEtrantmptoksA={}% clear token register
+    \advance\@IEEEeqncolcnt by -1\relax% adjust col count because of the isolation column
+    \ifnum\@IEEEeqncolcnt>\@IEEEeqnnumcols\relax
+    \@IEEEclspkgerror{Too many columns within the IEEEeqnarray\MessageBreak
+                          environment}%
+    {Use fewer \string &'s or put more columns in the IEEEeqnarry column\MessageBreak 
+     specifications.}\relax%
+    \else
+    \loop% add cols if the user did not use them all
+    \ifnum\@IEEEeqncolcnt<\@IEEEeqnnumcols\relax
+    \@IEEEappendtoksA{&}%
+    \advance\@IEEEeqncolcnt by 1\relax% update the col count
+    \repeat
+    % this number of &'s will take us the the isolation column
+    \fi
+    % execute the &'s
+    \the\@IEEEtrantmptoksA%
+    % handle the strut/isolation column
+    \@IEEEeqnarrayinsertstrut% do the strut if needed
+    \@IEEEeqnarraystrutreset% reset the strut system for next line or IEEEeqnarray
+    &% and enter the equation number column
+    % is this line needs an equation number, display it and advance the
+    % (sub)equation counters, record what type this line was
+    \if@eqnsw%
+     \if@IEEEissubequation\theIEEEsubequationdis\addtocounter{equation}{1}\stepcounter{IEEEsubequation}%
+     \global\@IEEElastlinewassubequationtrue%
+     \else% display a standard equation number, initialize the IEEEsubequation counter
+     \theequationdis\stepcounter{equation}\setcounter{IEEEsubequation}{0}%
+     \global\@IEEElastlinewassubequationfalse\fi%
+    \fi%
+    % reset the eqnsw flag to indicate default preference of the display of equation numbers
+    \if@IEEEeqnarraystarform\global\@eqnswfalse\else\global\@eqnswtrue\fi
+    \global\@IEEEissubequationfalse% reset the subequation flag
+    % reset the number of columns the user actually used
+    \global\@IEEEeqncolcnt\z@\relax
+    % the real end of the line
+    \cr}
+
+
+
+
+
+% \IEEEeqnarraybox is like \IEEEeqnarray except the box form puts everything
+% inside a vtop, vbox, or vcenter box depending on the letter in the second
+% optional argument (t,b,c). Vbox is the default. Unlike \IEEEeqnarray,
+% equation numbers are not displayed and \IEEEeqnarraybox can be nested.
+% \IEEEeqnarrayboxm is for math mode (like \array) and does not put the vbox
+% within an hbox.
+% \IEEEeqnarrayboxt is for text mode (like \tabular) and puts the vbox within
+% a \hbox{$ $} construct.
+% \IEEEeqnarraybox will auto detect whether to use \IEEEeqnarrayboxm or 
+% \IEEEeqnarrayboxt depending on the math mode.
+% The third optional argument specifies the width this box is to be set to -
+% natural width is the default.
+% The * forms do not add \jot line spacing
+% usage: \IEEEeqnarraybox[decl][pos][width]{cols}
+\def\IEEEeqnarrayboxm{\@IEEEeqnarraystarformfalse\@IEEEeqnarrayboxHBOXSWfalse\@IEEEeqnarraybox}
+\def\endIEEEeqnarrayboxm{\end@IEEEeqnarraybox}
+\@namedef{IEEEeqnarrayboxm*}{\@IEEEeqnarraystarformtrue\@IEEEeqnarrayboxHBOXSWfalse\@IEEEeqnarraybox}
+\@namedef{endIEEEeqnarrayboxm*}{\end@IEEEeqnarraybox}
+
+\def\IEEEeqnarrayboxt{\@IEEEeqnarraystarformfalse\@IEEEeqnarrayboxHBOXSWtrue\@IEEEeqnarraybox}
+\def\endIEEEeqnarrayboxt{\end@IEEEeqnarraybox}
+\@namedef{IEEEeqnarrayboxt*}{\@IEEEeqnarraystarformtrue\@IEEEeqnarrayboxHBOXSWtrue\@IEEEeqnarraybox}
+\@namedef{endIEEEeqnarrayboxt*}{\end@IEEEeqnarraybox}
+
+\def\IEEEeqnarraybox{\@IEEEeqnarraystarformfalse\ifmmode\@IEEEeqnarrayboxHBOXSWfalse\else\@IEEEeqnarrayboxHBOXSWtrue\fi%
+\@IEEEeqnarraybox}
+\def\endIEEEeqnarraybox{\end@IEEEeqnarraybox}
+
+\@namedef{IEEEeqnarraybox*}{\@IEEEeqnarraystarformtrue\ifmmode\@IEEEeqnarrayboxHBOXSWfalse\else\@IEEEeqnarrayboxHBOXSWtrue\fi%
+\@IEEEeqnarraybox}
+\@namedef{endIEEEeqnarraybox*}{\end@IEEEeqnarraybox}
+
+% flag to indicate if the \IEEEeqnarraybox needs to put things into an hbox{$ $} 
+% for \vcenter in non-math mode
+\newif\if@IEEEeqnarrayboxHBOXSW%
+\@IEEEeqnarrayboxHBOXSWfalse
+
+\def\@IEEEeqnarraybox{\relax\@ifnextchar[{\@@IEEEeqnarraybox}{\@@IEEEeqnarraybox[\relax]}}
+\def\@@IEEEeqnarraybox[#1]{\relax\@ifnextchar[{\@@@IEEEeqnarraybox[#1]}{\@@@IEEEeqnarraybox[#1][b]}}
+\def\@@@IEEEeqnarraybox[#1][#2]{\relax\@ifnextchar[{\@@@@IEEEeqnarraybox[#1][#2]}{\@@@@IEEEeqnarraybox[#1][#2][\relax]}}
+
+% #1 = decl; #2 = t,b,c; #3 = width, #4 = col specs
+\def\@@@@IEEEeqnarraybox[#1][#2][#3]#4{\@IEEEeqnarrayISinnerfalse % not yet within the lines of the halign
+   \@IEEEeqnarraymasterstrutsave% save current master strut values
+   \@IEEEeqnarraystrutsize{0pt}{0pt}[\relax]% turn off struts by default
+   \@IEEEeqnarrayusemasterstruttrue% use master strut till user asks otherwise
+   \IEEEvisiblestrutsfalse% diagnostic mode defaults to off
+   % no extra space unless the user specifically requests it
+   \lineskip=0pt\relax%
+   \lineskiplimit=0pt\relax%
+   \baselineskip=\normalbaselineskip\relax%
+   \jot=\IEEEnormaljot\relax%
+   \mathsurround\z@\relax% no extra spacing around math
+   % the default end glues are zero for an \IEEEeqnarraybox
+   \edef\@IEEEeqnarraycolSEPdefaultstart{\@IEEEeqnarraycolSEPzero}% default start glue
+   \edef\@IEEEeqnarraycolSEPdefaultend{\@IEEEeqnarraycolSEPzero}% default end glue
+   \edef\@IEEEeqnarraycolSEPdefaultmid{\@IEEEeqnarraycolSEPzero}% default inter-column glue
+   \@advanceIEEEeqncolcntfalse% do not advance the col counter for each col the user uses, 
+                              % used in \IEEEeqnarraymulticol and in the preamble build
+   \IEEEeqnarrayboxdecl\relax% allow a way for the user to make global overrides
+   #1\relax% allow user to override defaults
+   \let\\\@IEEEeqnarrayboxcr% replace newline with one that allows optional spacing
+   \@IEEEbuildpreamble #4\end\relax% build the preamble and put it into \@IEEEtrantmptoksA
+   % add an isolation column to the preamble to stop \\'s {} from getting into the last col
+   \ifnum\@IEEEeqnnumcols>0\relax\@IEEEappendtoksA{&}\fi% col separator for those after the first
+   \toks0={##}%
+   % add the isolation column to the preamble
+   \@IEEEappendtoksA{\tabskip\z@skip\bgroup\the\toks0\egroup}% 
+   % set the starting tabskip glue as determined by the preamble build
+   \tabskip=\@IEEEBPstartglue\relax
+   % begin the alignment
+   \everycr{}%
+   % use only the very first token to determine the positioning
+   % this stops some problems when the user uses more than one letter,
+   % but is probably not worth the effort
+   % \noindent is used as a delimiter
+   \def\@IEEEgrabfirstoken##1##2\noindent{\let\@IEEEgrabbedfirstoken=##1}%
+   \@IEEEgrabfirstoken#2\relax\relax\noindent
+   % \@IEEEgrabbedfirstoken has the first token, the rest are discarded
+   % if we need to put things into and hbox and go into math mode, do so now
+   \if@IEEEeqnarrayboxHBOXSW \leavevmode \hbox \bgroup $\fi%
+   % use the appropriate vbox type
+   \if\@IEEEgrabbedfirstoken t\relax\vtop\else\if\@IEEEgrabbedfirstoken c\relax%
+   \vcenter\else\vbox\fi\fi\bgroup%
+   \@IEEEeqnarrayISinnertrue% commands are now within the lines
+   \ifx#3\relax\halign\else\halign to #3\relax\fi%
+   \bgroup
+   % "exspand" the preamble
+   \span\the\@IEEEtrantmptoksA\cr}
+
+% carry strut status and enter the isolation/strut column, 
+% exit from math mode if needed, and exit
+\def\end@IEEEeqnarraybox{\@IEEEeqnarrayglobalizestrutstatus% carry strut status
+&% enter isolation/strut column
+\@IEEEeqnarrayinsertstrut% do strut if needed
+\@IEEEeqnarraymasterstrutrestore% restore the previous master strut values
+% reset the strut system for next IEEEeqnarray
+% (sets local strut values back to previous master strut values)
+\@IEEEeqnarraystrutreset%
+% ensure last line, exit from halign, close vbox
+\crcr\egroup\egroup%
+% exit from math mode and close hbox if needed
+\if@IEEEeqnarrayboxHBOXSW $\egroup\fi}
+
+
+
+% IEEEeqnarraybox uses a modifed \\ instead of the plain \cr to
+% end rows. This allows for things like \\[vskip amount]
+% This "cr" macros are modified versions those for LaTeX2e's eqnarray
+% For IEEEeqnarraybox, \\* is the same as \\
+% the {\ifnum0=`} braces must be kept away from the last column to avoid
+% altering spacing of its math, so we use & to advance to the isolation/strut column
+% carry strut status into isolation/strut column
+\def\@IEEEeqnarrayboxcr{\@IEEEeqnarrayglobalizestrutstatus% carry strut status
+&% enter isolation/strut column
+\@IEEEeqnarrayinsertstrut% do strut if needed
+% reset the strut system for next line or IEEEeqnarray
+\@IEEEeqnarraystrutreset%
+{\ifnum0=`}\fi%
+\@ifstar{\@IEEEeqnarrayboxYCR}{\@IEEEeqnarrayboxYCR}}
+
+% test and setup the optional argument to \\[]
+\def\@IEEEeqnarrayboxYCR{\@testopt\@IEEEeqnarrayboxXCR\z@skip}
+
+% IEEEeqnarraybox does not automatically increase line spacing by \jot
+\def\@IEEEeqnarrayboxXCR[#1]{\ifnum0=`{\fi}%
+\cr\noalign{\if@IEEEeqnarraystarform\else\vskip\jot\fi\vskip#1\relax}}
+
+
+
+% starts the halign preamble build
+\def\@IEEEbuildpreamble{\@IEEEtrantmptoksA={}% clear token register
+\let\@IEEEBPcurtype=u%current column type is not yet known
+\let\@IEEEBPprevtype=s%the previous column type was the start
+\let\@IEEEBPnexttype=u%next column type is not yet known
+% ensure these are valid
+\def\@IEEEBPcurglue={0pt plus 0pt minus 0pt}%
+\def\@IEEEBPcurcolname{@IEEEdefault}% name of current column definition
+% currently acquired numerically referenced glue
+% use a name that is easier to remember
+\let\@IEEEBPcurnum=\@IEEEtrantmpcountA%
+\@IEEEBPcurnum=0%
+% tracks number of columns in the preamble
+\@IEEEeqnnumcols=0%
+% record the default end glues
+\edef\@IEEEBPstartglue{\@IEEEeqnarraycolSEPdefaultstart}%
+\edef\@IEEEBPendglue{\@IEEEeqnarraycolSEPdefaultend}%
+% now parse the user's column specifications
+\@@IEEEbuildpreamble}
+
+
+% parses and builds the halign preamble
+\def\@@IEEEbuildpreamble#1#2{\let\@@nextIEEEbuildpreamble=\@@IEEEbuildpreamble%
+% use only the very first token to check the end
+% \noindent is used as a delimiter as \end can be present here
+\def\@IEEEgrabfirstoken##1##2\noindent{\let\@IEEEgrabbedfirstoken=##1}%
+\@IEEEgrabfirstoken#1\relax\relax\noindent
+\ifx\@IEEEgrabbedfirstoken\end\let\@@nextIEEEbuildpreamble=\@@IEEEfinishpreamble\else%
+% identify current and next token type
+\@IEEEgetcoltype{#1}{\@IEEEBPcurtype}{1}% current, error on invalid
+\@IEEEgetcoltype{#2}{\@IEEEBPnexttype}{0}% next, no error on invalid next
+% if curtype is a glue, get the glue def
+\if\@IEEEBPcurtype g\@IEEEgetcurglue{#1}{\@IEEEBPcurglue}\fi%
+% if curtype is a column, get the column def and set the current column name
+\if\@IEEEBPcurtype c\@IEEEgetcurcol{#1}\fi%
+% if curtype is a numeral, acquire the user defined glue
+\if\@IEEEBPcurtype n\@IEEEprocessNcol{#1}\fi%
+% process the acquired glue 
+\if\@IEEEBPcurtype g\@IEEEprocessGcol\fi%
+% process the acquired col 
+\if\@IEEEBPcurtype c\@IEEEprocessCcol\fi%
+% ready prevtype for next col spec.
+\let\@IEEEBPprevtype=\@IEEEBPcurtype%
+% be sure and put back the future token(s) as a group
+\fi\@@nextIEEEbuildpreamble{#2}}
+
+
+% executed just after preamble build is completed
+% warn about zero cols, and if prevtype type = u, put in end tabskip glue
+\def\@@IEEEfinishpreamble#1{\ifnum\@IEEEeqnnumcols<1\relax
+\@IEEEclspkgerror{No column specifiers declared for IEEEeqnarray}%
+{At least one column type must be declared for each IEEEeqnarray.}%
+\fi%num cols less than 1
+%if last type undefined, set default end tabskip glue
+\if\@IEEEBPprevtype u\@IEEEappendtoksA{\tabskip=\@IEEEBPendglue}\fi}
+
+
+% Identify and return the column specifier's type code
+\def\@IEEEgetcoltype#1#2#3{%
+% use only the very first token to determine the type
+% \noindent is used as a delimiter as \end can be present here
+\def\@IEEEgrabfirstoken##1##2\noindent{\let\@IEEEgrabbedfirstoken=##1}%
+\@IEEEgrabfirstoken#1\relax\relax\noindent
+% \@IEEEgrabfirstoken has the first token, the rest are discarded
+% n = number
+% g = glue (any other char in catagory 12)
+% c = letter
+% e = \end
+% u = undefined
+% third argument: 0 = no error message, 1 = error on invalid char
+\let#2=u\relax% assume invalid until know otherwise
+\ifx\@IEEEgrabbedfirstoken\end\let#2=e\else
+\ifcat\@IEEEgrabbedfirstoken\relax\else% screen out control sequences
+\if0\@IEEEgrabbedfirstoken\let#2=n\else
+\if1\@IEEEgrabbedfirstoken\let#2=n\else
+\if2\@IEEEgrabbedfirstoken\let#2=n\else
+\if3\@IEEEgrabbedfirstoken\let#2=n\else
+\if4\@IEEEgrabbedfirstoken\let#2=n\else
+\if5\@IEEEgrabbedfirstoken\let#2=n\else
+\if6\@IEEEgrabbedfirstoken\let#2=n\else
+\if7\@IEEEgrabbedfirstoken\let#2=n\else
+\if8\@IEEEgrabbedfirstoken\let#2=n\else
+\if9\@IEEEgrabbedfirstoken\let#2=n\else
+\ifcat,\@IEEEgrabbedfirstoken\let#2=g\relax
+\else\ifcat a\@IEEEgrabbedfirstoken\let#2=c\relax\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+\if#2u\relax
+\if0\noexpand#3\relax\else\@IEEEclspkgerror{Invalid character in column specifications}%
+{Only letters, numerals and certain other symbols are allowed \MessageBreak
+as IEEEeqnarray column specifiers.}\fi\fi}
+
+
+% identify the current letter referenced column
+% if invalid, use a default column
+\def\@IEEEgetcurcol#1{\expandafter\ifx\csname @IEEEeqnarraycolDEF#1\endcsname\@IEEEeqnarraycolisdefined%
+\def\@IEEEBPcurcolname{#1}\else% invalid column name
+\@IEEEclspkgerror{Invalid column type "#1" in column specifications.\MessageBreak
+Using a default centering column instead}%
+{You must define IEEEeqnarray column types before use.}%
+\def\@IEEEBPcurcolname{@IEEEdefault}\fi}
+
+
+% identify and return the predefined (punctuation) glue value
+\def\@IEEEgetcurglue#1#2{%
+% ! = \! (neg small)  -0.16667em (-3/18 em)
+% , = \, (small)       0.16667em ( 3/18 em)
+% : = \: (med)         0.22222em ( 4/18 em)
+% ; = \; (large)       0.27778em ( 5/18 em)
+% ' = \quad            1em
+% " = \qquad           2em
+% . = 0.5\arraycolsep
+% / = \arraycolsep
+% ? = 2\arraycolsep
+% * = 1fil
+% + = \@IEEEeqnarraycolSEPcenter
+% - = \@IEEEeqnarraycolSEPzero
+% Note that all em values are referenced to the math font (textfont2) fontdimen6
+% value for 1em.
+% 
+% use only the very first token to determine the type
+% this prevents errant tokens from getting in the main text
+% \noindent is used as a delimiter here
+\def\@IEEEgrabfirstoken##1##2\noindent{\let\@IEEEgrabbedfirstoken=##1}%
+\@IEEEgrabfirstoken#1\relax\relax\noindent
+% get the math font 1em value
+% LaTeX2e's NFSS2 does not preload the fonts, but \IEEEeqnarray needs
+% to gain access to the math (\textfont2) font's spacing parameters.
+% So we create a bogus box here that uses the math font to ensure
+% that \textfont2 is loaded and ready. If this is not done,
+% the \textfont2 stuff here may not work.
+% Thanks to Bernd Raichle for his 1997 post on this topic.
+{\setbox0=\hbox{$\displaystyle\relax$}}%
+% fontdimen6 has the width of 1em (a quad).
+\@IEEEtrantmpdimenA=\fontdimen6\textfont2\relax%
+% identify the glue value based on the first token
+% we discard anything after the first
+\if!\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=-0.16667\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if,\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=0.16667\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if:\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=0.22222\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if;\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=0.27778\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if'\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=1\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if"\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=2\@IEEEtrantmpdimenA\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if.\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=0.5\arraycolsep\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if/\@IEEEgrabbedfirstoken\edef#2{\the\arraycolsep}\else
+\if?\@IEEEgrabbedfirstoken\@IEEEtrantmpdimenA=2\arraycolsep\edef#2{\the\@IEEEtrantmpdimenA}\else
+\if *\@IEEEgrabbedfirstoken\edef#2{0pt plus 1fil minus 0pt}\else
+\if+\@IEEEgrabbedfirstoken\edef#2{\@IEEEeqnarraycolSEPcenter}\else
+\if-\@IEEEgrabbedfirstoken\edef#2{\@IEEEeqnarraycolSEPzero}\else
+\edef#2{\@IEEEeqnarraycolSEPzero}%
+\@IEEEclspkgerror{Invalid predefined inter-column glue type "#1" in\MessageBreak
+column specifications. Using a default value of\MessageBreak
+0pt instead}%
+{Only !,:;'"./?*+ and - are valid predefined glue types in the\MessageBreak 
+IEEEeqnarray column specifications.}\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+
+
+% process a numerical digit from the column specification
+% and look up the corresponding user defined glue value
+% can transform current type from n to g or a as the user defined glue is acquired
+\def\@IEEEprocessNcol#1{\if\@IEEEBPprevtype g%
+\@IEEEclspkgerror{Back-to-back inter-column glue specifiers in column\MessageBreak
+specifications. Ignoring consecutive glue specifiers\MessageBreak
+after the first}%
+{You cannot have two or more glue types next to each other\MessageBreak 
+in the IEEEeqnarray column specifications.}%
+\let\@IEEEBPcurtype=a% abort this glue, future digits will be discarded
+\@IEEEBPcurnum=0\relax%
+\else% if we previously aborted a glue
+\if\@IEEEBPprevtype a\@IEEEBPcurnum=0\let\@IEEEBPcurtype=a%maintain digit abortion
+\else%acquire this number
+% save the previous type before the numerical digits started
+\if\@IEEEBPprevtype n\else\let\@IEEEBPprevsavedtype=\@IEEEBPprevtype\fi%
+\multiply\@IEEEBPcurnum by 10\relax%
+\advance\@IEEEBPcurnum by #1\relax% add in number, \relax is needed to stop TeX's number scan
+\if\@IEEEBPnexttype n\else%close acquisition
+\expandafter\ifx\csname @IEEEeqnarraycolSEPDEF\expandafter\romannumeral\number\@IEEEBPcurnum\endcsname\@IEEEeqnarraycolisdefined%
+\edef\@IEEEBPcurglue{\csname @IEEEeqnarraycolSEP\expandafter\romannumeral\number\@IEEEBPcurnum\endcsname}%
+\else%user glue not defined
+\@IEEEclspkgerror{Invalid user defined inter-column glue type "\number\@IEEEBPcurnum" in\MessageBreak
+column specifications. Using a default value of\MessageBreak
+0pt instead}%
+{You must define all IEEEeqnarray numerical inter-column glue types via\MessageBreak
+\string\IEEEeqnarraydefcolsep \space before they are used in column specifications.}%
+\edef\@IEEEBPcurglue{\@IEEEeqnarraycolSEPzero}%
+\fi% glue defined or not
+\let\@IEEEBPcurtype=g% change the type to reflect the acquired glue
+\let\@IEEEBPprevtype=\@IEEEBPprevsavedtype% restore the prev type before this number glue
+\@IEEEBPcurnum=0\relax%ready for next acquisition
+\fi%close acquisition, get glue
+\fi%discard or acquire number
+\fi%prevtype glue or not
+}
+
+
+% process an acquired glue
+% add any acquired column/glue pair to the preamble
+\def\@IEEEprocessGcol{\if\@IEEEBPprevtype a\let\@IEEEBPcurtype=a%maintain previous glue abortions
+\else
+% if this is the start glue, save it, but do nothing else 
+% as this is not used in the preamble, but before
+\if\@IEEEBPprevtype s\edef\@IEEEBPstartglue{\@IEEEBPcurglue}%
+\else%not the start glue
+\if\@IEEEBPprevtype g%ignore if back to back glues
+\@IEEEclspkgerror{Back-to-back inter-column glue specifiers in column\MessageBreak
+specifications. Ignoring consecutive glue specifiers\MessageBreak
+after the first}%
+{You cannot have two or more glue types next to each other\MessageBreak 
+in the IEEEeqnarray column specifications.}%
+\let\@IEEEBPcurtype=a% abort this glue
+\else% not a back to back glue
+\if\@IEEEBPprevtype c\relax% if the previoustype was a col, add column/glue pair to preamble
+\ifnum\@IEEEeqnnumcols>0\relax\@IEEEappendtoksA{&}\fi
+\toks0={##}%
+% make preamble advance col counter if this environment needs this
+\if@advanceIEEEeqncolcnt\@IEEEappendtoksA{\global\advance\@IEEEeqncolcnt by 1\relax}\fi
+% insert the column defintion into the preamble, being careful not to expand
+% the column definition
+\@IEEEappendtoksA{\tabskip=\@IEEEBPcurglue}%
+\@IEEEappendNOEXPANDtoksA{\begingroup\csname @IEEEeqnarraycolPRE}%
+\@IEEEappendtoksA{\@IEEEBPcurcolname}%
+\@IEEEappendNOEXPANDtoksA{\endcsname}%
+\@IEEEappendtoksA{\the\toks0}%
+\@IEEEappendNOEXPANDtoksA{\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\csname @IEEEeqnarraycolPOST}%
+\@IEEEappendtoksA{\@IEEEBPcurcolname}%
+\@IEEEappendNOEXPANDtoksA{\endcsname\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\endgroup}%
+\advance\@IEEEeqnnumcols by 1\relax%one more column in the preamble
+\else% error: non-start glue with no pending column
+\@IEEEclspkgerror{Inter-column glue specifier without a prior column\MessageBreak
+type in the column specifications. Ignoring this glue\MessageBreak 
+specifier}%
+{Except for the first and last positions, glue can be placed only\MessageBreak
+between column types.}%
+\let\@IEEEBPcurtype=a% abort this glue
+\fi% previous was a column
+\fi% back-to-back glues
+\fi% is start column glue
+\fi% prev type not a
+}
+
+
+% process an acquired letter referenced column and, if necessary, add it to the preamble
+\def\@IEEEprocessCcol{\if\@IEEEBPnexttype g\else
+\if\@IEEEBPnexttype n\else
+% we have a column followed by something other than a glue (or numeral glue)
+% so we must add this column to the preamble now
+\ifnum\@IEEEeqnnumcols>0\relax\@IEEEappendtoksA{&}\fi%col separator for those after the first
+\if\@IEEEBPnexttype e\@IEEEappendtoksA{\tabskip=\@IEEEBPendglue\relax}\else%put in end glue
+\@IEEEappendtoksA{\tabskip=\@IEEEeqnarraycolSEPdefaultmid\relax}\fi% or default mid glue
+\toks0={##}%
+% make preamble advance col counter if this environment needs this
+\if@advanceIEEEeqncolcnt\@IEEEappendtoksA{\global\advance\@IEEEeqncolcnt by 1\relax}\fi
+% insert the column definition into the preamble, being careful not to expand
+% the column definition
+\@IEEEappendNOEXPANDtoksA{\begingroup\csname @IEEEeqnarraycolPRE}%
+\@IEEEappendtoksA{\@IEEEBPcurcolname}%
+\@IEEEappendNOEXPANDtoksA{\endcsname}%
+\@IEEEappendtoksA{\the\toks0}%
+\@IEEEappendNOEXPANDtoksA{\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\csname @IEEEeqnarraycolPOST}%
+\@IEEEappendtoksA{\@IEEEBPcurcolname}%
+\@IEEEappendNOEXPANDtoksA{\endcsname\relax\relax\relax\relax\relax%
+\relax\relax\relax\relax\relax\endgroup}%
+\advance\@IEEEeqnnumcols by 1\relax%one more column in the preamble
+\fi%next type not numeral
+\fi%next type not glue
+}
+
+
+%%
+%% END OF IEEEeqnarry DEFINITIONS
+%%
+
+
+
+
+% set up the running headings, this complex because of all the different
+% modes IEEEtran supports
+\if@twoside
+ \ifCLASSOPTIONtechnote
+   \def\ps@headings{%
+       \def\@oddhead{\hbox{}\scriptsize\leftmark \hfil \thepage}
+       \def\@evenhead{\scriptsize\thepage \hfil \leftmark\hbox{}}
+       \ifCLASSOPTIONdraftcls
+            \ifCLASSOPTIONdraftclsnofoot
+               \def\@oddfoot{}\def\@evenfoot{}%
+            \else
+               \def\@oddfoot{\scriptsize\@date\hfil DRAFT}
+               \def\@evenfoot{\scriptsize DRAFT\hfil\@date}
+            \fi
+       \else
+            \def\@oddfoot{}\def\@evenfoot{}
+       \fi}
+ \else % not a technote
+   \def\ps@headings{%
+       \ifCLASSOPTIONconference
+        \def\@oddhead{}
+        \def\@evenhead{}
+       \else
+        \def\@oddhead{\hbox{}\scriptsize\rightmark \hfil \thepage}
+        \def\@evenhead{\scriptsize\thepage \hfil \leftmark\hbox{}}
+       \fi
+       \ifCLASSOPTIONdraftcls
+            \def\@oddhead{\hbox{}\scriptsize\rightmark \hfil \thepage}
+            \def\@evenhead{\scriptsize\thepage \hfil \leftmark\hbox{}}
+            \ifCLASSOPTIONdraftclsnofoot
+               \def\@oddfoot{}\def\@evenfoot{}%
+            \else
+               \def\@oddfoot{\scriptsize\@date\hfil DRAFT}
+               \def\@evenfoot{\scriptsize DRAFT\hfil\@date}
+            \fi
+       \else
+            \def\@oddfoot{}\def\@evenfoot{}%
+       \fi}
+ \fi
+\else % single side
+\def\ps@headings{%
+    \ifCLASSOPTIONconference
+     \def\@oddhead{}
+     \def\@evenhead{}
+    \else
+     \def\@oddhead{\hbox{}\scriptsize\leftmark \hfil \thepage}
+     \def\@evenhead{}
+    \fi
+    \ifCLASSOPTIONdraftcls
+          \def\@oddhead{\hbox{}\scriptsize\leftmark \hfil \thepage}
+          \def\@evenhead{}
+          \ifCLASSOPTIONdraftclsnofoot
+             \def\@oddfoot{}
+          \else
+             \def\@oddfoot{\scriptsize \@date \hfil DRAFT}
+          \fi
+    \else
+         \def\@oddfoot{}
+    \fi
+    \def\@evenfoot{}}
+\fi
+
+
+% title page style
+\def\ps@IEEEtitlepagestyle{\def\@oddfoot{}\def\@evenfoot{}%
+\ifCLASSOPTIONconference
+   \def\@oddhead{}%
+   \def\@evenhead{}%
+\else
+   \def\@oddhead{\hbox{}\scriptsize\leftmark \hfil \thepage}%
+   \def\@evenhead{\scriptsize\thepage \hfil \leftmark\hbox{}}%
+\fi
+\ifCLASSOPTIONdraftcls
+   \def\@oddhead{\hbox{}\scriptsize\leftmark \hfil \thepage}%
+   \def\@evenhead{\scriptsize\thepage \hfil \leftmark\hbox{}}%
+   \ifCLASSOPTIONdraftclsnofoot\else
+      \def\@oddfoot{\scriptsize \@date\hfil DRAFT}%
+      \def\@evenfoot{\scriptsize DRAFT\hfil \@date}%
+   \fi
+\else
+   % all non-draft mode footers
+   \if@IEEEusingpubid
+      % for title pages that are using a pubid
+      % do not repeat pubid if using peer review option
+      \ifCLASSOPTIONpeerreview
+      \else
+         \footskip 0pt%
+         \ifCLASSOPTIONcompsoc
+           \def\@oddfoot{\hss\normalfont\scriptsize\raisebox{-1.5\@IEEEnormalsizeunitybaselineskip}[0ex][0ex]{\@IEEEpubid}\hss}%
+           \def\@evenfoot{\hss\normalfont\scriptsize\raisebox{-1.5\@IEEEnormalsizeunitybaselineskip}[0ex][0ex]{\@IEEEpubid}\hss}%
+         \else
+           \def\@oddfoot{\hss\normalfont\footnotesize\raisebox{1.5ex}[1.5ex]{\@IEEEpubid}\hss}%
+           \def\@evenfoot{\hss\normalfont\footnotesize\raisebox{1.5ex}[1.5ex]{\@IEEEpubid}\hss}%
+         \fi
+      \fi
+   \fi
+\fi}
+
+
+% peer review cover page style
+\def\ps@IEEEpeerreviewcoverpagestyle{%
+\def\@oddhead{}\def\@evenhead{}%
+\def\@oddfoot{}\def\@evenfoot{}%
+\ifCLASSOPTIONdraftcls
+   \ifCLASSOPTIONdraftclsnofoot\else
+      \def\@oddfoot{\scriptsize \@date\hfil DRAFT}%
+      \def\@evenfoot{\scriptsize DRAFT\hfil \@date}%
+   \fi
+\else
+   % non-draft mode footers
+   \if@IEEEusingpubid
+      \footskip 0pt%
+      \ifCLASSOPTIONcompsoc
+        \def\@oddfoot{\hss\normalfont\scriptsize\raisebox{-1.5\@IEEEnormalsizeunitybaselineskip}[0ex][0ex]{\@IEEEpubid}\hss}%
+        \def\@evenfoot{\hss\normalfont\scriptsize\raisebox{-1.5\@IEEEnormalsizeunitybaselineskip}[0ex][0ex]{\@IEEEpubid}\hss}%
+      \else
+        \def\@oddfoot{\hss\normalfont\footnotesize\raisebox{1.5ex}[1.5ex]{\@IEEEpubid}\hss}%
+        \def\@evenfoot{\hss\normalfont\footnotesize\raisebox{1.5ex}[1.5ex]{\@IEEEpubid}\hss}%
+      \fi
+   \fi
+\fi}
+
+
+% start with empty headings
+\def\rightmark{}\def\leftmark{}
+
+
+%% Defines the command for putting the header. \footernote{TEXT} is the same
+%% as \markboth{TEXT}{TEXT}. 
+%% Note that all the text is forced into uppercase, if you have some text
+%% that needs to be in lower case, for instance et. al., then either manually
+%% set \leftmark and \rightmark or use \MakeLowercase{et. al.} within the
+%% arguments to \markboth.
+\def\markboth#1#2{\def\leftmark{\@IEEEcompsoconly{\sffamily}\MakeUppercase{#1}}%
+\def\rightmark{\@IEEEcompsoconly{\sffamily}\MakeUppercase{#2}}}
+\def\footernote#1{\markboth{#1}{#1}}
+
+\def\today{\ifcase\month\or
+    January\or February\or March\or April\or May\or June\or
+    July\or August\or September\or October\or November\or December\fi
+    \space\number\day, \number\year}
+
+
+
+
+%% CITATION AND BIBLIOGRAPHY COMMANDS
+%% 
+%% V1.6 no longer supports the older, nonstandard \shortcite and \citename setup stuff
+% 
+% 
+% Modify Latex2e \@citex to separate citations with "], ["
+\def\@citex[#1]#2{%
+  \let\@citea\@empty
+  \@cite{\@for\@citeb:=#2\do
+    {\@citea\def\@citea{], [}%
+     \edef\@citeb{\expandafter\@firstofone\@citeb\@empty}%
+     \if@filesw\immediate\write\@auxout{\string\citation{\@citeb}}\fi
+     \@ifundefined{b@\@citeb}{\mbox{\reset@font\bfseries ?}%
+       \G@refundefinedtrue
+       \@latex@warning
+         {Citation `\@citeb' on page \thepage \space undefined}}%
+       {\hbox{\csname b@\@citeb\endcsname}}}}{#1}}
+
+% V1.6 we create hooks for the optional use of Donald Arseneau's
+% cite.sty package. cite.sty is "smart" and will notice that the
+% following format controls are already defined and will not
+% redefine them. The result will be the proper sorting of the
+% citation numbers and auto detection of 3 or more entry "ranges" -
+% all in IEEE style:  [1], [2], [5]--[7], [12]
+% This also allows for an optional note, i.e., \cite[mynote]{..}.
+% If the \cite with note has more than one reference, the note will
+% be applied to the last of the listed references. It is generally
+% desired that if a note is given, only one reference is listed in
+% that \cite.
+% Thanks to Mr. Arseneau for providing the required format arguments
+% to produce the IEEE style.
+\def\citepunct{], [}
+\def\citedash{]--[}
+
+% V1.7 default to using same font for urls made by url.sty
+\AtBeginDocument{\csname url@samestyle\endcsname}
+
+% V1.6 class files should always provide these
+\def\newblock{\hskip .11em\@plus.33em\@minus.07em}
+\let\@openbib@code\@empty
+
+
+% Provide support for the control entries of IEEEtran.bst V1.00 and later.
+% V1.7 optional argument allows for a different aux file to be specified in
+% order to handle multiple bibliographies. For example, with multibib.sty:
+% \newcites{sec}{Secondary Literature}
+% \bstctlcite[@auxoutsec]{BSTcontrolhak}
+\def\bstctlcite{\@ifnextchar[{\@bstctlcite}{\@bstctlcite[@auxout]}}
+\def\@bstctlcite[#1]#2{\@bsphack
+  \@for\@citeb:=#2\do{%
+    \edef\@citeb{\expandafter\@firstofone\@citeb}%
+    \if@filesw\immediate\write\csname #1\endcsname{\string\citation{\@citeb}}\fi}%
+  \@esphack}
+
+% V1.6 provide a way for a user to execute a command just before 
+% a given reference number - used to insert a \newpage to balance
+% the columns on the last page
+\edef\@IEEEtriggerrefnum{0}   % the default of zero means that
+                              % the command is not executed
+\def\@IEEEtriggercmd{\newpage}
+
+% allow the user to alter the triggered command
+\long\def\IEEEtriggercmd#1{\long\def\@IEEEtriggercmd{#1}}
+
+% allow user a way to specify the reference number just before the
+% command is executed
+\def\IEEEtriggeratref#1{\@IEEEtrantmpcountA=#1%
+\edef\@IEEEtriggerrefnum{\the\@IEEEtrantmpcountA}}%
+
+% trigger command at the given reference
+\def\@IEEEbibitemprefix{\@IEEEtrantmpcountA=\@IEEEtriggerrefnum\relax%
+\advance\@IEEEtrantmpcountA by -1\relax%
+\ifnum\c@enumiv=\@IEEEtrantmpcountA\relax\@IEEEtriggercmd\relax\fi}
+
+
+\def\@biblabel#1{[#1]}
+
+% compsoc journals left align the reference numbers
+\@IEEEcompsocnotconfonly{\def\@biblabel#1{[#1]\hfill}}
+
+% controls bib item spacing
+\def\IEEEbibitemsep{0pt plus .5pt}
+
+\@IEEEcompsocconfonly{\def\IEEEbibitemsep{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}}
+
+
+\def\thebibliography#1{\section*{\refname}%
+    \addcontentsline{toc}{section}{\refname}%
+    % V1.6 add some rubber space here and provide a command trigger
+    \footnotesize\@IEEEcompsocconfonly{\small}\vskip 0.3\baselineskip plus 0.1\baselineskip minus 0.1\baselineskip%
+    \list{\@biblabel{\@arabic\c@enumiv}}%
+    {\settowidth\labelwidth{\@biblabel{#1}}%
+    \leftmargin\labelwidth
+    \advance\leftmargin\labelsep\relax
+    \itemsep \IEEEbibitemsep\relax
+    \usecounter{enumiv}%
+    \let\p@enumiv\@empty
+    \renewcommand\theenumiv{\@arabic\c@enumiv}}%
+    \let\@IEEElatexbibitem\bibitem%
+    \def\bibitem{\@IEEEbibitemprefix\@IEEElatexbibitem}%
+\def\newblock{\hskip .11em plus .33em minus .07em}%
+% originally:
+%   \sloppy\clubpenalty4000\widowpenalty4000%
+% by adding the \interlinepenalty here, we make it more
+% difficult, but not impossible, for LaTeX to break within a reference.
+% IEEE almost never breaks a reference (but they do it more often with
+% technotes). You may get an underfull vbox warning around the bibliography, 
+% but the final result will be much more like what IEEE will publish. 
+% MDS 11/2000
+\ifCLASSOPTIONtechnote\sloppy\clubpenalty4000\widowpenalty4000\interlinepenalty100%
+\else\sloppy\clubpenalty4000\widowpenalty4000\interlinepenalty500\fi%
+    \sfcode`\.=1000\relax}
+\let\endthebibliography=\endlist
+
+
+
+
+% TITLE PAGE COMMANDS
+% 
+% 
+% \IEEEmembership is used to produce the sublargesize italic font used to indicate author 
+% IEEE membership. compsoc uses a large size sans slant font
+\def\IEEEmembership#1{{\@IEEEnotcompsoconly{\sublargesize}\normalfont\@IEEEcompsoconly{\sffamily}\textit{#1}}}
+
+% \IEEEauthorrefmark{} produces a footnote type symbol to indicate author affiliation.
+% When given an argument of 1 to 9, \IEEEauthorrefmark{} follows the standard LaTeX footnote
+% symbol sequence convention. However, for arguments 10 and above, \IEEEauthorrefmark{} 
+% reverts to using lower case roman numerals, so it cannot overflow. Do note that you 
+% cannot use \footnotemark[] in place of \IEEEauthorrefmark{} within \author as the footnote
+% symbols will have been turned off to prevent \thanks from creating footnote marks.
+% \IEEEauthorrefmark{} produces a symbol that appears to LaTeX as having zero vertical
+% height - this allows for a more compact line packing, but the user must ensure that
+% the interline spacing is large enough to prevent \IEEEauthorrefmark{} from colliding
+% with the text above.
+% V1.7 make this a robust command
+\DeclareRobustCommand*{\IEEEauthorrefmark}[1]{\raisebox{0pt}[0pt][0pt]{\textsuperscript{\footnotesize\ensuremath{\ifcase#1\or *\or \dagger\or \ddagger\or%
+    \mathsection\or \mathparagraph\or \|\or **\or \dagger\dagger%
+    \or \ddagger\ddagger \else\textsuperscript{\expandafter\romannumeral#1}\fi}}}}
+
+
+% FONT CONTROLS AND SPACINGS FOR CONFERENCE MODE AUTHOR NAME AND AFFILIATION BLOCKS
+% 
+% The default font styles for the author name and affiliation blocks (confmode)
+\def\@IEEEauthorblockNstyle{\normalfont\@IEEEcompsocnotconfonly{\sffamily}\sublargesize\@IEEEcompsocconfonly{\large}}
+\def\@IEEEauthorblockAstyle{\normalfont\@IEEEcompsocnotconfonly{\sffamily}\@IEEEcompsocconfonly{\itshape}\normalsize\@IEEEcompsocconfonly{\large}}
+% The default if the user does not use an author block
+\def\@IEEEauthordefaulttextstyle{\normalfont\@IEEEcompsocnotconfonly{\sffamily}\sublargesize}
+
+% spacing from title (or special paper notice) to author name blocks (confmode)
+% can be negative
+\def\@IEEEauthorblockconfadjspace{-0.25em}
+% compsoc conferences need more space here
+\@IEEEcompsocconfonly{\def\@IEEEauthorblockconfadjspace{0.75\@IEEEnormalsizeunitybaselineskip}}
+
+% spacing between name and affiliation blocks (confmode)
+% This can be negative.
+% IEEE doesn't want any added spacing here, but I will leave these
+% controls in place in case they ever change their mind.
+% Personally, I like 0.75ex.
+%\def\@IEEEauthorblockNtopspace{0.75ex}
+%\def\@IEEEauthorblockAtopspace{0.75ex}
+\def\@IEEEauthorblockNtopspace{0.0ex}
+\def\@IEEEauthorblockAtopspace{0.0ex}
+% baseline spacing within name and affiliation blocks (confmode)
+% must be positive, spacings below certain values will make 
+% the position of line of text sensitive to the contents of the
+% line above it i.e., whether or not the prior line has descenders, 
+% subscripts, etc. For this reason it is a good idea to keep
+% these above 2.6ex
+\def\@IEEEauthorblockNinterlinespace{2.6ex}
+\def\@IEEEauthorblockAinterlinespace{2.75ex}
+
+% This tracks the required strut size.
+% See the \@IEEEauthorhalign command for the actual default value used.
+\def\@IEEEauthorblockXinterlinespace{2.7ex}
+
+% variables to retain font size and style across groups
+% values given here have no effect as they will be overwritten later
+\gdef\@IEEESAVESTATEfontsize{10}
+\gdef\@IEEESAVESTATEfontbaselineskip{12}
+\gdef\@IEEESAVESTATEfontencoding{OT1}
+\gdef\@IEEESAVESTATEfontfamily{ptm}
+\gdef\@IEEESAVESTATEfontseries{m}
+\gdef\@IEEESAVESTATEfontshape{n}
+
+% saves the current font attributes
+\def\@IEEEcurfontSAVE{\global\let\@IEEESAVESTATEfontsize\f@size%
+\global\let\@IEEESAVESTATEfontbaselineskip\f@baselineskip%
+\global\let\@IEEESAVESTATEfontencoding\f@encoding%
+\global\let\@IEEESAVESTATEfontfamily\f@family%
+\global\let\@IEEESAVESTATEfontseries\f@series%
+\global\let\@IEEESAVESTATEfontshape\f@shape}
+
+% restores the saved font attributes
+\def\@IEEEcurfontRESTORE{\fontsize{\@IEEESAVESTATEfontsize}{\@IEEESAVESTATEfontbaselineskip}%
+\fontencoding{\@IEEESAVESTATEfontencoding}%
+\fontfamily{\@IEEESAVESTATEfontfamily}%
+\fontseries{\@IEEESAVESTATEfontseries}%
+\fontshape{\@IEEESAVESTATEfontshape}%
+\selectfont}
+
+
+% variable to indicate if the current block is the first block in the column
+\newif\if@IEEEprevauthorblockincol   \@IEEEprevauthorblockincolfalse
+
+
+% the command places a strut with height and depth = \@IEEEauthorblockXinterlinespace
+% we use this technique to have complete manual control over the spacing of the lines
+% within the halign environment.
+% We set the below baseline portion at 30%, the above
+% baseline portion at 70% of the total length.
+% Responds to changes in the document's \baselinestretch
+\def\@IEEEauthorstrutrule{\@IEEEtrantmpdimenA\@IEEEauthorblockXinterlinespace%
+\@IEEEtrantmpdimenA=\baselinestretch\@IEEEtrantmpdimenA%
+\rule[-0.3\@IEEEtrantmpdimenA]{0pt}{\@IEEEtrantmpdimenA}}
+
+
+% blocks to hold the authors' names and affilations. 
+% Makes formatting easy for conferences
+%
+% use real definitions in conference mode
+% name block
+\def\IEEEauthorblockN#1{\relax\@IEEEauthorblockNstyle% set the default text style
+\gdef\@IEEEauthorblockXinterlinespace{0pt}% disable strut for spacer row
+% the \expandafter hides the \cr in conditional tex, see the array.sty docs
+% for details, probably not needed here as the \cr is in a macro
+% do a spacer row if needed
+\if@IEEEprevauthorblockincol\expandafter\@IEEEauthorblockNtopspaceline\fi
+\global\@IEEEprevauthorblockincoltrue% we now have a block in this column
+%restore the correct strut value
+\gdef\@IEEEauthorblockXinterlinespace{\@IEEEauthorblockNinterlinespace}%
+% input the author names
+#1%
+% end the row if the user did not already
+\crcr}
+% spacer row for names
+\def\@IEEEauthorblockNtopspaceline{\cr\noalign{\vskip\@IEEEauthorblockNtopspace}}
+%
+% affiliation block
+\def\IEEEauthorblockA#1{\relax\@IEEEauthorblockAstyle% set the default text style
+\gdef\@IEEEauthorblockXinterlinespace{0pt}%disable strut for spacer row
+% the \expandafter hides the \cr in conditional tex, see the array.sty docs
+% for details, probably not needed here as the \cr is in a macro
+% do a spacer row if needed
+\if@IEEEprevauthorblockincol\expandafter\@IEEEauthorblockAtopspaceline\fi
+\global\@IEEEprevauthorblockincoltrue% we now have a block in this column
+%restore the correct strut value
+\gdef\@IEEEauthorblockXinterlinespace{\@IEEEauthorblockAinterlinespace}%
+% input the author affiliations
+#1%
+% end the row if the user did not already
+\crcr}
+% spacer row for affiliations
+\def\@IEEEauthorblockAtopspaceline{\cr\noalign{\vskip\@IEEEauthorblockAtopspace}}
+
+
+% allow papers to compile even if author blocks are used in modes other
+% than conference or peerreviewca. For such cases, we provide dummy blocks.
+\ifCLASSOPTIONconference
+\else
+   \ifCLASSOPTIONpeerreviewca\else
+      % not conference or peerreviewca mode
+      \def\IEEEauthorblockN#1{#1}%
+      \def\IEEEauthorblockA#1{#1}%
+   \fi
+\fi
+
+
+
+% we provide our own halign so as not to have to depend on tabular
+\def\@IEEEauthorhalign{\@IEEEauthordefaulttextstyle% default text style
+   \lineskip=0pt\relax% disable line spacing
+   \lineskiplimit=0pt\relax%
+   \baselineskip=0pt\relax%
+   \@IEEEcurfontSAVE% save the current font
+   \mathsurround\z@\relax% no extra spacing around math
+   \let\\\@IEEEauthorhaligncr% replace newline with halign friendly one
+   \tabskip=0pt\relax% no column spacing
+   \everycr{}% ensure no problems here
+   \@IEEEprevauthorblockincolfalse% no author blocks yet
+   \def\@IEEEauthorblockXinterlinespace{2.7ex}% default interline space
+   \vtop\bgroup%vtop box
+   \halign\bgroup&\relax\hfil\@IEEEcurfontRESTORE\relax ##\relax
+   \hfil\@IEEEcurfontSAVE\@IEEEauthorstrutrule\cr}
+
+% ensure last line, exit from halign, close vbox
+\def\end@IEEEauthorhalign{\crcr\egroup\egroup}
+
+% handle bogus star form
+\def\@IEEEauthorhaligncr{{\ifnum0=`}\fi\@ifstar{\@@IEEEauthorhaligncr}{\@@IEEEauthorhaligncr}}
+
+% test and setup the optional argument to \\[]
+\def\@@IEEEauthorhaligncr{\@testopt\@@@IEEEauthorhaligncr\z@skip}
+
+% end the line and do the optional spacer
+\def\@@@IEEEauthorhaligncr[#1]{\ifnum0=`{\fi}\cr\noalign{\vskip#1\relax}}
+
+
+
+% flag to prevent multiple \and warning messages
+\newif\if@IEEEWARNand
+\@IEEEWARNandtrue
+
+% if in conference or peerreviewca modes, we support the use of \and as \author is a
+% tabular environment, otherwise we warn the user that \and is invalid
+% outside of conference or peerreviewca modes.
+\def\and{\relax} % provide a bogus \and that we will then override
+
+\renewcommand{\and}[1][\relax]{\if@IEEEWARNand\typeout{** WARNING: \noexpand\and is valid only
+                               when in conference or peerreviewca}\typeout{modes (line \the\inputlineno).}\fi\global\@IEEEWARNandfalse}
+
+\ifCLASSOPTIONconference%
+\renewcommand{\and}[1][\hfill]{\end{@IEEEauthorhalign}#1\begin{@IEEEauthorhalign}}%
+\fi
+\ifCLASSOPTIONpeerreviewca
+\renewcommand{\and}[1][\hfill]{\end{@IEEEauthorhalign}#1\begin{@IEEEauthorhalign}}%
+\fi
+
+
+% page clearing command
+% based on LaTeX2e's \cleardoublepage, but allows different page styles
+% for the inserted blank pages
+\def\@IEEEcleardoublepage#1{\clearpage\if@twoside\ifodd\c@page\else
+\hbox{}\thispagestyle{#1}\newpage\if@twocolumn\hbox{}\thispagestyle{#1}\newpage\fi\fi\fi}
+
+
+% user command to invoke the title page
+\def\maketitle{\par%
+  \begingroup%
+  \normalfont%
+  \def\thefootnote{}%  the \thanks{} mark type is empty
+  \def\footnotemark{}% and kill space from \thanks within author
+  \let\@makefnmark\relax% V1.7, must *really* kill footnotemark to remove all \textsuperscript spacing as well.
+  \footnotesize%       equal spacing between thanks lines
+  \footnotesep 0.7\baselineskip%see global setting of \footnotesep for more info
+  % V1.7 disable \thanks note indention for compsoc
+  \@IEEEcompsoconly{\long\def\@makefntext##1{\parindent 1em\noindent\hbox{\@makefnmark}##1}}%
+  \normalsize%
+  \ifCLASSOPTIONpeerreview
+     \newpage\global\@topnum\z@ \@maketitle\@IEEEstatictitlevskip\@IEEEaftertitletext%
+     \thispagestyle{IEEEpeerreviewcoverpagestyle}\@thanks%
+  \else
+     \if@twocolumn%
+        \ifCLASSOPTIONtechnote%
+           \newpage\global\@topnum\z@ \@maketitle\@IEEEstatictitlevskip\@IEEEaftertitletext%
+        \else
+           \twocolumn[\@maketitle\@IEEEdynamictitlevspace\@IEEEaftertitletext]%
+        \fi
+     \else
+        \newpage\global\@topnum\z@ \@maketitle\@IEEEstatictitlevskip\@IEEEaftertitletext%
+     \fi
+     \thispagestyle{IEEEtitlepagestyle}\@thanks%
+  \fi
+  % pullup page for pubid if used.
+  \if@IEEEusingpubid
+     \enlargethispage{-\@IEEEpubidpullup}%
+  \fi 
+  \endgroup
+  \setcounter{footnote}{0}\let\maketitle\relax\let\@maketitle\relax
+  \gdef\@thanks{}%
+  % v1.6b do not clear these as we will need the title again for peer review papers
+  % \gdef\@author{}\gdef\@title{}%
+  \let\thanks\relax}
+
+
+
+% V1.7 parbox to format \@IEEEcompsoctitleabstractindextext
+\long\def\@IEEEcompsoctitleabstractindextextbox#1{\parbox{0.915\textwidth}{#1}}
+
+% formats the Title, authors names, affiliations and special paper notice
+% THIS IS A CONTROLLED SPACING COMMAND! Do not allow blank lines or unintentional
+% spaces to enter the definition - use % at the end of each line
+\def\@maketitle{\newpage
+\begin{center}%
+\ifCLASSOPTIONtechnote% technotes
+   {\bfseries\large\@IEEEcompsoconly{\sffamily}\@title\par}\vskip 1.3em{\lineskip .5em\@IEEEcompsoconly{\sffamily}\@author
+   \@IEEEspecialpapernotice\par{\@IEEEcompsoconly{\vskip 1.5em\relax
+   \@IEEEcompsoctitleabstractindextextbox{\@IEEEcompsoctitleabstractindextext}\par
+   \hfill\@IEEEcompsocdiamondline\hfill\hbox{}\par}}}\relax
+\else% not a technote
+   \vskip0.2em{\Huge\@IEEEcompsoconly{\sffamily}\@IEEEcompsocconfonly{\normalfont\normalsize\vskip 2\@IEEEnormalsizeunitybaselineskip
+   \bfseries\Large}\@title\par}\vskip1.0em\par%
+   % V1.6 handle \author differently if in conference mode
+   \ifCLASSOPTIONconference%
+      {\@IEEEspecialpapernotice\mbox{}\vskip\@IEEEauthorblockconfadjspace%
+       \mbox{}\hfill\begin{@IEEEauthorhalign}\@author\end{@IEEEauthorhalign}\hfill\mbox{}\par}\relax
+   \else% peerreviewca, peerreview or journal
+      \ifCLASSOPTIONpeerreviewca
+         % peerreviewca handles author names just like conference mode
+         {\@IEEEcompsoconly{\sffamily}\@IEEEspecialpapernotice\mbox{}\vskip\@IEEEauthorblockconfadjspace%
+          \mbox{}\hfill\begin{@IEEEauthorhalign}\@author\end{@IEEEauthorhalign}\hfill\mbox{}\par
+          {\@IEEEcompsoconly{\vskip 1.5em\relax
+           \@IEEEcompsoctitleabstractindextextbox{\@IEEEcompsoctitleabstractindextext}\par\hfill
+           \@IEEEcompsocdiamondline\hfill\hbox{}\par}}}\relax
+      \else% journal or peerreview
+         {\lineskip.5em\@IEEEcompsoconly{\sffamily}\sublargesize\@author\@IEEEspecialpapernotice\par
+          {\@IEEEcompsoconly{\vskip 1.5em\relax
+           \@IEEEcompsoctitleabstractindextextbox{\@IEEEcompsoctitleabstractindextext}\par\hfill
+           \@IEEEcompsocdiamondline\hfill\hbox{}\par}}}\relax
+      \fi
+   \fi
+\fi\end{center}}
+
+
+
+% V1.7 Computer Society "diamond line" which follows index terms for nonconference papers
+\def\@IEEEcompsocdiamondline{\vrule depth 0pt height 0.5pt width 4cm\hspace{7.5pt}%
+\raisebox{-3.5pt}{\fontfamily{pzd}\fontencoding{U}\fontseries{m}\fontshape{n}\fontsize{11}{12}\selectfont\char70}%
+\hspace{7.5pt}\vrule depth 0pt height 0.5pt width 4cm\relax}
+
+% V1.7 standard LateX2e \thanks, but with \itshape under compsoc. Also make it a \long\def
+% We also need to trigger the one-shot footnote rule
+\def\@IEEEtriggeroneshotfootnoterule{\global\@IEEEenableoneshotfootnoteruletrue}
+
+
+\long\def\thanks#1{\footnotemark
+    \protected@xdef\@thanks{\@thanks
+        \protect\footnotetext[\the\c@footnote]{\@IEEEcompsoconly{\itshape
+        \protect\@IEEEtriggeroneshotfootnoterule\relax}\ignorespaces#1}}}
+\let\@thanks\@empty
+
+% V1.7 allow \author to contain \par's. This is needed to allow \thanks to contain \par.
+\long\def\author#1{\gdef\@author{#1}}
+
+
+% in addition to setting up IEEEitemize, we need to remove a baselineskip space above and
+% below it because \list's \pars introduce blank lines because of the footnote struts.
+\def\@IEEEsetupcompsocitemizelist{\def\labelitemi{$\bullet$}%
+\setlength{\IEEElabelindent}{0pt}\setlength{\parskip}{0pt}%
+\setlength{\partopsep}{0pt}\setlength{\topsep}{0.5\baselineskip}\vspace{-1\baselineskip}\relax}
+
+
+% flag for fake non-compsoc \IEEEcompsocthanksitem - prevents line break on very first item
+\newif\if@IEEEbreakcompsocthanksitem \@IEEEbreakcompsocthanksitemfalse
+
+\ifCLASSOPTIONcompsoc
+% V1.7 compsoc bullet item \thanks
+% also, we need to redefine this to destroy the argument in \@IEEEdynamictitlevspace
+\long\def\IEEEcompsocitemizethanks#1{\relax\@IEEEbreakcompsocthanksitemfalse\footnotemark
+    \protected@xdef\@thanks{\@thanks
+        \protect\footnotetext[\the\c@footnote]{\itshape\protect\@IEEEtriggeroneshotfootnoterule
+        {\let\IEEEiedlistdecl\relax\protect\begin{IEEEitemize}[\protect\@IEEEsetupcompsocitemizelist]\ignorespaces#1\relax
+        \protect\end{IEEEitemize}}\protect\vspace{-1\baselineskip}}}}
+\DeclareRobustCommand*{\IEEEcompsocthanksitem}{\item}
+\else
+% non-compsoc, allow for dual compilation via rerouting to normal \thanks
+\long\def\IEEEcompsocitemizethanks#1{\thanks{#1}}
+% redirect to "pseudo-par" \hfil\break\indent after swallowing [] from \IEEEcompsocthanksitem[]
+\DeclareRobustCommand{\IEEEcompsocthanksitem}{\@ifnextchar [{\@IEEEthanksswallowoptionalarg}%
+{\@IEEEthanksswallowoptionalarg[\relax]}}
+% be sure and break only after first item, be sure and ignore spaces after optional argument
+\def\@IEEEthanksswallowoptionalarg[#1]{\relax\if@IEEEbreakcompsocthanksitem\hfil\break
+\indent\fi\@IEEEbreakcompsocthanksitemtrue\ignorespaces}
+\fi
+
+
+% V1.6b define the \IEEEpeerreviewmaketitle as needed
+\ifCLASSOPTIONpeerreview
+\def\IEEEpeerreviewmaketitle{\@IEEEcleardoublepage{empty}%
+\ifCLASSOPTIONtwocolumn
+\twocolumn[\@IEEEpeerreviewmaketitle\@IEEEdynamictitlevspace]
+\else
+\newpage\@IEEEpeerreviewmaketitle\@IEEEstatictitlevskip
+\fi
+\thispagestyle{IEEEtitlepagestyle}}
+\else
+% \IEEEpeerreviewmaketitle does nothing if peer review option has not been selected
+\def\IEEEpeerreviewmaketitle{\relax}
+\fi
+
+% peerreview formats the repeated title like the title in journal papers.
+\def\@IEEEpeerreviewmaketitle{\begin{center}\@IEEEcompsoconly{\sffamily}%
+\normalfont\normalsize\vskip0.2em{\Huge\@title\par}\vskip1.0em\par
+\end{center}}
+
+
+
+% V1.6 
+% this is a static rubber spacer between the title/authors and the main text
+% used for single column text, or when the title appears in the first column
+% of two column text (technotes). 
+\def\@IEEEstatictitlevskip{{\normalfont\normalsize
+% adjust spacing to next text
+% v1.6b handle peer review papers
+\ifCLASSOPTIONpeerreview
+% for peer review papers, the same value is used for both title pages
+% regardless of the other paper modes
+   \vskip 1\baselineskip plus 0.375\baselineskip minus 0.1875\baselineskip
+\else
+   \ifCLASSOPTIONconference% conference
+      \vskip 1\baselineskip plus 0.375\baselineskip minus 0.1875\baselineskip%
+   \else%
+      \ifCLASSOPTIONtechnote% technote
+         \vskip 1\baselineskip plus 0.375\baselineskip minus 0.1875\baselineskip%
+      \else% journal uses more space
+         \vskip 2.5\baselineskip plus 0.75\baselineskip minus 0.375\baselineskip%
+      \fi
+   \fi
+\fi}}
+
+
+% V1.6
+% This is a dynamically determined rigid spacer between the title/authors 
+% and the main text. This is used only for single column titles over two 
+% column text (most common)
+% This is bit tricky because we have to ensure that the textheight of the
+% main text is an integer multiple of \baselineskip
+% otherwise underfull vbox problems may develop in the second column of the
+% text on the titlepage
+% The possible use of \IEEEpubid must also be taken into account.
+\def\@IEEEdynamictitlevspace{{%
+    % we run within a group so that all the macros can be forgotten when we are done
+    \long\def\thanks##1{\relax}%don't allow \thanks to run when we evaluate the vbox height
+    \long\def\IEEEcompsocitemizethanks##1{\relax}%don't allow \IEEEcompsocitemizethanks to run when we evaluate the vbox height
+    \normalfont\normalsize% we declare more descriptive variable names
+    \let\@IEEEmaintextheight=\@IEEEtrantmpdimenA%height of the main text columns
+    \let\@IEEEINTmaintextheight=\@IEEEtrantmpdimenB%height of the main text columns with integer # lines
+    % set the nominal and minimum values for the title spacer
+    % the dynamic algorithm will not allow the spacer size to
+    % become less than \@IEEEMINtitlevspace - instead it will be
+    % lengthened
+    % default to journal values
+    \def\@IEEENORMtitlevspace{2.5\baselineskip}%
+    \def\@IEEEMINtitlevspace{2\baselineskip}%
+    % conferences and technotes need tighter spacing
+    \ifCLASSOPTIONconference%conference
+     \def\@IEEENORMtitlevspace{1\baselineskip}%
+     \def\@IEEEMINtitlevspace{0.75\baselineskip}%
+    \fi
+    \ifCLASSOPTIONtechnote%technote
+      \def\@IEEENORMtitlevspace{1\baselineskip}%
+      \def\@IEEEMINtitlevspace{0.75\baselineskip}%
+    \fi%
+    % get the height that the title will take up
+    \ifCLASSOPTIONpeerreview
+       \settoheight{\@IEEEmaintextheight}{\vbox{\hsize\textwidth \@IEEEpeerreviewmaketitle}}%
+    \else
+       \settoheight{\@IEEEmaintextheight}{\vbox{\hsize\textwidth \@maketitle}}%
+    \fi
+    \@IEEEmaintextheight=-\@IEEEmaintextheight% title takes away from maintext, so reverse sign
+    % add the height of the page textheight
+    \advance\@IEEEmaintextheight by \textheight%
+    % correct for title pages using pubid
+    \ifCLASSOPTIONpeerreview\else
+       % peerreview papers use the pubid on the cover page only.
+       % And the cover page uses a static spacer.
+       \if@IEEEusingpubid\advance\@IEEEmaintextheight by -\@IEEEpubidpullup\fi
+    \fi%
+    % subtract off the nominal value of the title bottom spacer
+    \advance\@IEEEmaintextheight by -\@IEEENORMtitlevspace%
+    % \topskip takes away some too
+    \advance\@IEEEmaintextheight by -\topskip%
+    % calculate the column height of the main text for lines
+    % now we calculate the main text height as if holding
+    % an integer number of \normalsize lines after the first
+    % and discard any excess fractional remainder
+    % we subtracted the first line, because the first line
+    % is placed \topskip into the maintext, not \baselineskip like the
+    % rest of the lines.
+    \@IEEEINTmaintextheight=\@IEEEmaintextheight%
+    \divide\@IEEEINTmaintextheight  by \baselineskip%
+    \multiply\@IEEEINTmaintextheight  by \baselineskip%
+    % now we calculate how much the title spacer height will
+    % have to be reduced from nominal (\@IEEEREDUCEmaintextheight is always
+    % a positive value) so that the maintext area will contain an integer
+    % number of normal size lines
+    % we change variable names here (to avoid confusion) as we no longer
+    % need \@IEEEINTmaintextheight and can reuse its dimen register
+    \let\@IEEEREDUCEmaintextheight=\@IEEEINTmaintextheight%
+    \advance\@IEEEREDUCEmaintextheight by -\@IEEEmaintextheight%
+    \advance\@IEEEREDUCEmaintextheight by \baselineskip%
+    % this is the calculated height of the spacer
+    % we change variable names here (to avoid confusion) as we no longer
+    % need \@IEEEmaintextheight and can reuse its dimen register
+    \let\@IEEECOMPENSATElen=\@IEEEmaintextheight%
+    \@IEEECOMPENSATElen=\@IEEENORMtitlevspace% set the nominal value
+    % we go with the reduced length if it is smaller than an increase
+    \ifdim\@IEEEREDUCEmaintextheight < 0.5\baselineskip\relax%
+     \advance\@IEEECOMPENSATElen by -\@IEEEREDUCEmaintextheight%
+     % if the resulting spacer is too small back out and go with an increase instead
+     \ifdim\@IEEECOMPENSATElen<\@IEEEMINtitlevspace\relax%
+      \advance\@IEEECOMPENSATElen by \baselineskip%
+     \fi%
+    \else%
+     % go with an increase because it is closer to the nominal than a decrease
+     \advance\@IEEECOMPENSATElen by -\@IEEEREDUCEmaintextheight%
+     \advance\@IEEECOMPENSATElen by \baselineskip%
+    \fi%
+    % set the calculated rigid spacer
+    \vspace{\@IEEECOMPENSATElen}}}
+
+
+
+% V1.6
+% we allow the user access to the last part of the title area
+% useful in emergencies such as when a different spacing is needed
+% This text is NOT compensated for in the dynamic sizer.
+\let\@IEEEaftertitletext=\relax
+\long\def\IEEEaftertitletext#1{\def\@IEEEaftertitletext{#1}}
+
+% V1.7 provide a way for users to enter abstract and keywords
+% into the onecolumn title are. This text is compensated for
+% in the dynamic sizer.
+\let\@IEEEcompsoctitleabstractindextext=\relax
+\long\def\IEEEcompsoctitleabstractindextext#1{\def\@IEEEcompsoctitleabstractindextext{#1}}
+% V1.7 provide a way for users to get the \@IEEEcompsoctitleabstractindextext if
+% not in compsoc journal mode - this way abstract and keywords can be placed
+% in their conventional position if not in compsoc mode.
+\def\IEEEdisplaynotcompsoctitleabstractindextext{%
+\ifCLASSOPTIONcompsoc% display if compsoc conf
+\ifCLASSOPTIONconference\@IEEEcompsoctitleabstractindextext\fi
+\else% or if not compsoc
+\@IEEEcompsoctitleabstractindextext\fi}
+
+
+% command to allow alteration of baselinestretch, but only if the current
+% baselineskip is unity. Used to tweak the compsoc abstract and keywords line spacing.
+\def\@IEEEtweakunitybaselinestretch#1{{\def\baselinestretch{1}\selectfont
+\global\@tempskipa\baselineskip}\ifnum\@tempskipa=\baselineskip%
+\def\baselinestretch{#1}\selectfont\fi\relax}
+
+
+% abstract and keywords are in \small, except 
+% for 9pt docs in which they are in \footnotesize
+% Because 9pt docs use an 8pt footnotesize, \small
+% becomes a rather awkward 8.5pt
+\def\@IEEEabskeysecsize{\small}
+\ifx\CLASSOPTIONpt\@IEEEptsizenine
+ \def\@IEEEabskeysecsize{\footnotesize}
+\fi
+
+% compsoc journals use \footnotesize, compsoc conferences use normalsize
+\@IEEEcompsoconly{\def\@IEEEabskeysecsize{\footnotesize}}
+\@IEEEcompsocconfonly{\def\@IEEEabskeysecsize{\normalsize}}
+
+
+
+
+% V1.6 have abstract and keywords strip leading spaces, pars and newlines
+% so that spacing is more tightly controlled.
+\def\abstract{\normalfont
+    \if@twocolumn
+      \@IEEEabskeysecsize\bfseries\textit{\abstractname}---\relax
+    \else
+      \begin{center}\vspace{-1.78ex}\@IEEEabskeysecsize\textbf{\abstractname}\end{center}\quotation\@IEEEabskeysecsize
+    \fi\@IEEEgobbleleadPARNLSP}
+% V1.6 IEEE wants only 1 pica from end of abstract to introduction heading when in 
+% conference mode (the heading already has this much above it)
+\def\endabstract{\relax\ifCLASSOPTIONconference\vspace{0ex}\else\vspace{1.34ex}\fi\par\if@twocolumn\else\endquotation\fi
+    \normalfont\normalsize}
+
+\def\IEEEkeywords{\normalfont
+    \if@twocolumn
+      \@IEEEabskeysecsize\bfseries\textit{\IEEEkeywordsname}---\relax
+    \else
+      \begin{center}\@IEEEabskeysecsize\textbf{\IEEEkeywordsname}\end{center}\quotation\@IEEEabskeysecsize
+    \fi\@IEEEgobbleleadPARNLSP}
+\def\endIEEEkeywords{\relax\ifCLASSOPTIONtechnote\vspace{1.34ex}\else\vspace{0.67ex}\fi
+    \par\if@twocolumn\else\endquotation\fi%
+    \normalfont\normalsize}
+
+% V1.7 compsoc keywords index terms
+\ifCLASSOPTIONcompsoc
+  \ifCLASSOPTIONconference% compsoc conference
+\def\abstract{\normalfont
+      \begin{center}\@IEEEabskeysecsize\textbf{\large\abstractname}\end{center}\vskip 0.5\baselineskip plus 0.1\baselineskip minus 0.1\baselineskip
+      \if@twocolumn\else\quotation\fi\itshape\@IEEEabskeysecsize%
+      \par\@IEEEgobbleleadPARNLSP}
+\def\IEEEkeywords{\normalfont\vskip 1.5\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip
+      \begin{center}\@IEEEabskeysecsize\textbf{\large\IEEEkeywordsname}\end{center}\vskip 0.5\baselineskip plus 0.1\baselineskip minus 0.1\baselineskip
+      \if@twocolumn\else\quotation\fi\itshape\@IEEEabskeysecsize%
+      \par\@IEEEgobbleleadPARNLSP}
+  \else% compsoc not conference
+\def\abstract{\normalfont\@IEEEtweakunitybaselinestretch{1.15}\sffamily
+    \if@twocolumn
+      \@IEEEabskeysecsize\noindent\textbf{\abstractname}---\relax
+    \else
+      \begin{center}\vspace{-1.78ex}\@IEEEabskeysecsize\textbf{\abstractname}\end{center}\quotation\@IEEEabskeysecsize%
+    \fi\@IEEEgobbleleadPARNLSP}
+\def\IEEEkeywords{\normalfont\@IEEEtweakunitybaselinestretch{1.15}\sffamily
+    \if@twocolumn
+      \@IEEEabskeysecsize\vskip 0.5\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip\noindent
+      \textbf{\IEEEkeywordsname}---\relax
+    \else
+      \begin{center}\@IEEEabskeysecsize\textbf{\IEEEkeywordsname}\end{center}\quotation\@IEEEabskeysecsize%
+    \fi\@IEEEgobbleleadPARNLSP}
+  \fi
+\fi
+
+
+
+% gobbles all leading \, \\ and \par, upon finding first token that
+% is not a \ , \\ or a \par, it ceases and returns that token
+% 
+% used to strip leading \, \\ and \par from the input
+% so that such things in the beginning of an environment will not
+% affect the formatting of the text
+\long\def\@IEEEgobbleleadPARNLSP#1{\let\@IEEEswallowthistoken=0%
+\let\@IEEEgobbleleadPARNLSPtoken#1%
+\let\@IEEEgobbleleadPARtoken=\par%
+\let\@IEEEgobbleleadNLtoken=\\%
+\let\@IEEEgobbleleadSPtoken=\ %
+\def\@IEEEgobbleleadSPMACRO{\ }%
+\ifx\@IEEEgobbleleadPARNLSPtoken\@IEEEgobbleleadPARtoken%
+\let\@IEEEswallowthistoken=1%
+\fi%
+\ifx\@IEEEgobbleleadPARNLSPtoken\@IEEEgobbleleadNLtoken%
+\let\@IEEEswallowthistoken=1%
+\fi%
+\ifx\@IEEEgobbleleadPARNLSPtoken\@IEEEgobbleleadSPtoken%
+\let\@IEEEswallowthistoken=1%
+\fi%
+% a control space will come in as a macro
+% when it is the last one on a line
+\ifx\@IEEEgobbleleadPARNLSPtoken\@IEEEgobbleleadSPMACRO%
+\let\@IEEEswallowthistoken=1%
+\fi%
+% if we have to swallow this token, do so and taste the next one
+% else spit it out and stop gobbling
+\ifx\@IEEEswallowthistoken 1\let\@IEEEnextgobbleleadPARNLSP=\@IEEEgobbleleadPARNLSP\else%
+\let\@IEEEnextgobbleleadPARNLSP=#1\fi%
+\@IEEEnextgobbleleadPARNLSP}%
+
+
+
+
+% TITLING OF SECTIONS
+\def\@IEEEsectpunct{:\ \,}  % Punctuation after run-in section heading  (headings which are
+                            % part of the paragraphs), need little bit more than a single space
+                            % spacing from section number to title
+% compsoc conferences use regular period/space punctuation
+\ifCLASSOPTIONcompsoc
+\ifCLASSOPTIONconference
+\def\@IEEEsectpunct{.\ }
+\fi\fi
+
+
+\def\@seccntformat#1{\csname the#1dis\endcsname\hskip 0.5em\relax}
+
+\ifCLASSOPTIONcompsoc
+% compsoc journals need extra spacing
+\ifCLASSOPTIONconference\else
+\def\@seccntformat#1{\csname the#1dis\endcsname\hskip 1em\relax}
+\fi\fi
+
+%v1.7 put {} after #6 to allow for some types of user font control
+%and use \@@par rather than \par
+\def\@sect#1#2#3#4#5#6[#7]#8{%
+  \ifnum #2>\c@secnumdepth
+     \let\@svsec\@empty
+  \else
+     \refstepcounter{#1}%
+     % load section label and spacer into \@svsec
+     \protected@edef\@svsec{\@seccntformat{#1}\relax}%
+  \fi%
+  \@tempskipa #5\relax
+  \ifdim \@tempskipa>\z@% tempskipa determines whether is treated as a high
+     \begingroup #6{\relax% or low level heading
+      \noindent % subsections are NOT indented
+       % print top level headings. \@svsec is label, #8 is heading title
+       % IEEE does not block indent the section title text, it flows like normal
+       {\hskip #3\relax\@svsec}{\interlinepenalty \@M #8\@@par}}%
+     \endgroup
+     \addcontentsline{toc}{#1}{\ifnum #2>\c@secnumdepth\relax\else
+               \protect\numberline{\csname the#1\endcsname}\fi#7}%
+  \else % printout low level headings
+     % svsechd seems to swallow the trailing space, protect it with \mbox{}
+     % got rid of sectionmark stuff
+     \def\@svsechd{#6{\hskip #3\relax\@svsec #8\@IEEEsectpunct\mbox{}}%
+     \addcontentsline{toc}{#1}{\ifnum #2>\c@secnumdepth\relax\else
+               \protect\numberline{\csname the#1\endcsname}\fi#7}}%
+  \fi%skip down
+  \@xsect{#5}}
+
+
+% section* handler
+%v1.7 put {} after #4 to allow for some types of user font control
+%and use \@@par rather than \par
+\def\@ssect#1#2#3#4#5{\@tempskipa #3\relax
+  \ifdim \@tempskipa>\z@
+     %\begingroup #4\@hangfrom{\hskip #1}{\interlinepenalty \@M #5\par}\endgroup
+     % IEEE does not block indent the section title text, it flows like normal
+     \begingroup \noindent #4{\relax{\hskip #1}{\interlinepenalty \@M #5\@@par}}\endgroup
+  % svsechd swallows the trailing space, protect it with \mbox{}
+  \else \def\@svsechd{#4{\hskip #1\relax #5\@IEEEsectpunct\mbox{}}}\fi
+  \@xsect{#3}}
+
+
+%% SECTION heading spacing and font
+%%
+% arguments are: #1 - sectiontype name
+% (for \@sect)   #2 - section level
+%                #3 - section heading indent
+%                #4 - top separation (absolute value used, neg indicates not to indent main text)
+%                     If negative, make stretch parts negative too!
+%                #5 - (absolute value used) positive: bottom separation after heading,
+%                      negative: amount to indent main text after heading
+%                Both #4 and #5 negative means to indent main text and use negative top separation
+%                #6 - font control
+% You've got to have \normalfont\normalsize in the font specs below to prevent
+% trouble when you do something like:
+% \section{Note}{\ttfamily TT-TEXT} is known to ... 
+% IEEE sometimes REALLY stretches the area before a section
+% heading by up to about 0.5in. However, it may not be a good
+% idea to let LaTeX have quite this much rubber.
+\ifCLASSOPTIONconference%
+% IEEE wants section heading spacing to decrease for conference mode
+\def\section{\@startsection{section}{1}{\z@}{1.5ex plus 1.5ex minus 0.5ex}%
+{0.7ex plus 1ex minus 0ex}{\normalfont\normalsize\centering\scshape}}%
+\def\subsection{\@startsection{subsection}{2}{\z@}{1.5ex plus 1.5ex minus 0.5ex}%
+{0.7ex plus .5ex minus 0ex}{\normalfont\normalsize\itshape}}%
+\else % for journals
+\def\section{\@startsection{section}{1}{\z@}{3.0ex plus 1.5ex minus 1.5ex}% V1.6 3.0ex from 3.5ex
+{0.7ex plus 1ex minus 0ex}{\normalfont\normalsize\centering\scshape}}%
+\def\subsection{\@startsection{subsection}{2}{\z@}{3.5ex plus 1.5ex minus 1.5ex}%
+{0.7ex plus .5ex minus 0ex}{\normalfont\normalsize\itshape}}%
+\fi
+
+% for both journals and conferences
+% decided to put in a little rubber above the section, might help somebody
+\def\subsubsection{\@startsection{subsubsection}{3}{\parindent}{0ex plus 0.1ex minus 0.1ex}%
+{0ex}{\normalfont\normalsize\itshape}}%
+\def\paragraph{\@startsection{paragraph}{4}{2\parindent}{0ex plus 0.1ex minus 0.1ex}%
+{0ex}{\normalfont\normalsize\itshape}}%
+
+
+% compsoc
+\ifCLASSOPTIONcompsoc
+\ifCLASSOPTIONconference
+% compsoc conference
+\def\section{\@startsection{section}{1}{\z@}{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}%
+{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}{\normalfont\large\bfseries}}%
+\def\subsection{\@startsection{subsection}{2}{\z@}{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}%
+{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}{\normalfont\sublargesize\bfseries}}%
+\def\subsubsection{\@startsection{subsubsection}{3}{\z@}{1\baselineskip plus 0.25\baselineskip minus 0.25\baselineskip}%
+{0ex}{\normalfont\normalsize\bfseries}}%
+\def\paragraph{\@startsection{paragraph}{4}{2\parindent}{0ex plus 0.1ex minus 0.1ex}%
+{0ex}{\normalfont\normalsize}}%
+\else% compsoc journals
+% use negative top separation as compsoc journals do not indent paragraphs after section titles
+\def\section{\@startsection{section}{1}{\z@}{-3ex plus -2ex minus -1.5ex}%
+{0.7ex plus 1ex minus 0ex}{\normalfont\large\sffamily\bfseries\scshape}}%
+% Note that subsection and smaller may not be correct for the Computer Society,
+% I have to look up an example.
+\def\subsection{\@startsection{subsection}{2}{\z@}{-3.5ex plus -1.5ex minus -1.5ex}%
+{0.7ex plus .5ex minus 0ex}{\normalfont\normalsize\sffamily\bfseries}}%
+\def\subsubsection{\@startsection{subsubsection}{3}{\z@}{-2.5ex plus -1ex minus -1ex}%
+{0.5ex plus 0.5ex minus 0ex}{\normalfont\normalsize\sffamily\itshape}}%
+\def\paragraph{\@startsection{paragraph}{4}{2\parindent}{-0ex plus -0.1ex minus -0.1ex}%
+{0ex}{\normalfont\normalsize}}%
+\fi\fi
+
+
+
+
+%% ENVIRONMENTS
+% "box" symbols at end of proofs
+\def\IEEEQEDclosed{\mbox{\rule[0pt]{1.3ex}{1.3ex}}} % for a filled box
+% V1.6 some journals use an open box instead that will just fit around a closed one
+\def\IEEEQEDopen{{\setlength{\fboxsep}{0pt}\setlength{\fboxrule}{0.2pt}\fbox{\rule[0pt]{0pt}{1.3ex}\rule[0pt]{1.3ex}{0pt}}}}
+\ifCLASSOPTIONcompsoc
+\def\IEEEQED{\IEEEQEDopen}   % default to open for compsoc
+\else
+\def\IEEEQED{\IEEEQEDclosed} % otherwise default to closed
+\fi
+
+% v1.7 name change to avoid namespace collision with amsthm. Also add support
+% for an optional argument.
+\def\IEEEproof{\@ifnextchar[{\@IEEEproof}{\@IEEEproof[\IEEEproofname]}}
+\def\@IEEEproof[#1]{\par\noindent\hspace{2em}{\itshape #1: }}
+\def\endIEEEproof{\hspace*{\fill}~\IEEEQED\par}
+
+
+%\itemindent is set to \z@ by list, so define new temporary variable
+\newdimen\@IEEEtmpitemindent
+\def\@begintheorem#1#2{\@IEEEtmpitemindent\itemindent\topsep 0pt\rmfamily\trivlist%
+    \item[\hskip \labelsep{\indent\itshape #1\ #2:}]\itemindent\@IEEEtmpitemindent}
+\def\@opargbegintheorem#1#2#3{\@IEEEtmpitemindent\itemindent\topsep 0pt\rmfamily \trivlist%
+% V1.6 IEEE is back to using () around theorem names which are also in italics
+% Thanks to Christian Peel for reporting this.
+    \item[\hskip\labelsep{\indent\itshape #1\ #2\ (#3):}]\itemindent\@IEEEtmpitemindent}
+% V1.7 remove bogus \unskip that caused equations in theorems to collide with
+% lines below.
+\def\@endtheorem{\endtrivlist}
+
+% V1.6
+% display command for the section the theorem is in - so that \thesection
+% is not used as this will be in Roman numerals when we want arabic.
+% LaTeX2e uses \def\@thmcounter#1{\noexpand\arabic{#1}} for the theorem number
+% (second part) display and \def\@thmcountersep{.} as a separator.
+% V1.7 intercept calls to the section counter and reroute to \@IEEEthmcounterinsection
+% to allow \appendix(ices} to override as needed.
+%
+% special handler for sections, allows appendix(ices) to override
+\gdef\@IEEEthmcounterinsection#1{\arabic{#1}}
+% string macro
+\edef\@IEEEstringsection{section}
+
+% redefine the #1#2[#3] form of newtheorem to use a hook to \@IEEEthmcounterinsection
+% if section in_counter is used
+\def\@xnthm#1#2[#3]{%
+  \expandafter\@ifdefinable\csname #1\endcsname
+    {\@definecounter{#1}\@newctr{#1}[#3]%
+     \edef\@IEEEstringtmp{#3}
+     \ifx\@IEEEstringtmp\@IEEEstringsection
+     \expandafter\xdef\csname the#1\endcsname{%
+     \noexpand\@IEEEthmcounterinsection{#3}\@thmcountersep
+          \@thmcounter{#1}}%
+     \else
+     \expandafter\xdef\csname the#1\endcsname{%
+       \expandafter\noexpand\csname the#3\endcsname \@thmcountersep
+          \@thmcounter{#1}}%
+     \fi
+     \global\@namedef{#1}{\@thm{#1}{#2}}%
+     \global\@namedef{end#1}{\@endtheorem}}}
+
+
+
+%% SET UP THE DEFAULT PAGESTYLE
+\ps@headings
+\pagenumbering{arabic}
+
+% normally the page counter starts at 1
+\setcounter{page}{1}
+% however, for peerreview the cover sheet is page 0 or page -1
+% (for duplex printing)
+\ifCLASSOPTIONpeerreview
+   \if@twoside
+      \setcounter{page}{-1}
+   \else
+      \setcounter{page}{0}
+   \fi
+\fi
+
+% standard book class behavior - let bottom line float up and down as
+% needed when single sided
+\ifCLASSOPTIONtwoside\else\raggedbottom\fi
+% if two column - turn on twocolumn, allow word spacings to stretch more and
+% enforce a rigid position for the last lines
+\ifCLASSOPTIONtwocolumn
+% the peer review option delays invoking twocolumn
+   \ifCLASSOPTIONpeerreview\else
+      \twocolumn
+   \fi
+\sloppy 
+\flushbottom
+\fi
+
+
+
+
+% \APPENDIX and \APPENDICES definitions
+
+% This is the \@ifmtarg command from the LaTeX ifmtarg package
+% by Peter Wilson (CUA) and Donald Arseneau
+% \@ifmtarg is used to determine if an argument to a command
+% is present or not.
+% For instance:
+% \@ifmtarg{#1}{\typeout{empty}}{\typeout{has something}}
+% \@ifmtarg is used with our redefined \section command if
+% \appendices is invoked.
+% The command \section will behave slightly differently depending
+% on whether the user specifies a title: 
+% \section{My appendix title}
+% or not:
+% \section{}
+% This way, we can eliminate the blank lines where the title
+% would be, and the unneeded : after Appendix in the table of
+% contents 
+\begingroup
+\catcode`\Q=3
+\long\gdef\@ifmtarg#1{\@xifmtarg#1QQ\@secondoftwo\@firstoftwo\@nil}
+\long\gdef\@xifmtarg#1#2Q#3#4#5\@nil{#4}
+\endgroup
+% end of \@ifmtarg defs
+
+
+% V1.7
+% command that allows the one time saving of the original definition
+% of section to \@IEEEappendixsavesection for \appendix or \appendices 
+% we don't save \section here as it may be redefined later by other
+% packages (hyperref.sty, etc.)
+\def\@IEEEsaveoriginalsectiononce{\let\@IEEEappendixsavesection\section
+\let\@IEEEsaveoriginalsectiononce\relax}
+
+% neat trick to grab and process the argument from \section{argument}
+% we process differently if the user invoked \section{} with no
+% argument (title)
+% note we reroute the call to the old \section*
+\def\@IEEEprocessthesectionargument#1{%
+\@ifmtarg{#1}{%
+\@IEEEappendixsavesection*{\appendixname~\thesectiondis}%
+\addcontentsline{toc}{section}{\appendixname~\thesection}}{%
+\@IEEEappendixsavesection*{\appendixname~\thesectiondis \\* #1}%
+\addcontentsline{toc}{section}{\appendixname~\thesection: #1}}}
+
+% we use this if the user calls \section{} after
+% \appendix-- which has no meaning. So, we ignore the
+% command and its argument. Then, warn the user.
+\def\@IEEEdestroythesectionargument#1{\typeout{** WARNING: Ignoring useless
+\protect\section\space in Appendix (line \the\inputlineno).}}
+
+
+% remember \thesection forms will be displayed in \ref calls
+% and in the Table of Contents.
+% The \sectiondis form is used in the actual heading itself
+
+% appendix command for one single appendix
+% normally has no heading. However, if you want a 
+% heading, you can do so via the optional argument:
+% \appendix[Optional Heading]
+\def\appendix{\relax}
+\renewcommand{\appendix}[1][]{\@IEEEsaveoriginalsectiononce\par
+    % v1.6 keep hyperref's identifiers unique
+    \gdef\theHsection{Appendix.A}%
+    % v1.6 adjust hyperref's string name for the section
+    \xdef\Hy@chapapp{appendix}%
+    \setcounter{section}{0}%
+    \setcounter{subsection}{0}%
+    \setcounter{subsubsection}{0}%
+    \setcounter{paragraph}{0}%
+    \gdef\thesection{A}%
+    \gdef\thesectiondis{}% 
+    \gdef\thesubsection{\Alph{subsection}}%
+    \gdef\@IEEEthmcounterinsection##1{A}
+    \refstepcounter{section}% update the \ref counter
+    \@ifmtarg{#1}{\@IEEEappendixsavesection*{\appendixname}%
+                  \addcontentsline{toc}{section}{\appendixname}}{%
+             \@IEEEappendixsavesection*{\appendixname~\\* #1}%
+             \addcontentsline{toc}{section}{\appendixname: #1}}%
+    % redefine \section command for appendix
+    % leave \section* as is
+    \def\section{\@ifstar{\@IEEEappendixsavesection*}{%
+                    \@IEEEdestroythesectionargument}}% throw out the argument
+                                                     % of the normal form
+}
+
+
+
+% appendices command for multiple appendices
+% user then calls \section with an argument (possibly empty) to
+% declare the individual appendices
+\def\appendices{\@IEEEsaveoriginalsectiononce\par
+    % v1.6 keep hyperref's identifiers unique
+    \gdef\theHsection{Appendix.\Alph{section}}%
+    % v1.6 adjust hyperref's string name for the section
+    \xdef\Hy@chapapp{appendix}%
+    \setcounter{section}{-1}% we want \refstepcounter to use section 0
+    \setcounter{subsection}{0}%
+    \setcounter{subsubsection}{0}%
+    \setcounter{paragraph}{0}%
+    \ifCLASSOPTIONromanappendices%
+    \gdef\thesection{\Roman{section}}%
+    \gdef\thesectiondis{\Roman{section}}%
+    \@IEEEcompsocconfonly{\gdef\thesectiondis{\Roman{section}.}}%
+    \gdef\@IEEEthmcounterinsection##1{A\arabic{##1}}
+    \else%
+    \gdef\thesection{\Alph{section}}%
+    \gdef\thesectiondis{\Alph{section}}%
+    \@IEEEcompsocconfonly{\gdef\thesectiondis{\Alph{section}.}}%
+    \gdef\@IEEEthmcounterinsection##1{\Alph{##1}}
+    \fi%
+    \refstepcounter{section}% update the \ref counter
+    \setcounter{section}{0}% NEXT \section will be the FIRST appendix
+    % redefine \section command for appendices
+    % leave \section* as is
+    \def\section{\@ifstar{\@IEEEappendixsavesection*}{% process the *-form
+                    \refstepcounter{section}% or is a new section so,
+                    \@IEEEprocessthesectionargument}}% process the argument 
+                                                 % of the normal form
+}
+
+
+
+% \IEEEPARstart
+% Definition for the big two line drop cap letter at the beginning of the
+% first paragraph of journal papers. The first argument is the first letter
+% of the first word, the second argument is the remaining letters of the
+% first word which will be rendered in upper case.
+% In V1.6 this has been completely rewritten to:
+% 
+% 1. no longer have problems when the user begins an environment
+%    within the paragraph that uses \IEEEPARstart.
+% 2. auto-detect and use the current font family
+% 3. revise handling of the space at the end of the first word so that
+%    interword glue will now work as normal.
+% 4. produce correctly aligned edges for the (two) indented lines.
+% 
+% We generalize things via control macros - playing with these is fun too.
+% 
+% V1.7 added more control macros to make it easy for IEEEtrantools.sty users
+% to change the font style.
+% 
+% the number of lines that are indented to clear it
+% may need to increase if using decenders
+\def\@IEEEPARstartDROPLINES{2}
+% minimum number of lines left on a page to allow a \@IEEEPARstart
+% Does not take into consideration rubber shrink, so it tends to
+% be overly cautious
+\def\@IEEEPARstartMINPAGELINES{2}
+% V1.7 the height of the drop cap is adjusted to match the height of this text
+% in the current font (when \IEEEPARstart is called).
+\def\@IEEEPARstartHEIGHTTEXT{T}
+% the depth the letter is lowered below the baseline
+% the height (and size) of the letter is determined by the sum
+% of this value and the height of the \@IEEEPARstartHEIGHTTEXT in the current
+% font. It is a good idea to set this value in terms of the baselineskip
+% so that it can respond to changes therein.
+\def\@IEEEPARstartDROPDEPTH{1.1\baselineskip}
+% V1.7 the font the drop cap will be rendered in,
+% can take zero or one argument.
+\def\@IEEEPARstartFONTSTYLE{\bfseries}
+% V1.7 any additional, non-font related commands needed to modify
+% the drop cap letter, can take zero or one argument.
+\def\@IEEEPARstartCAPSTYLE{\MakeUppercase}
+% V1.7 the font that will be used to render the rest of the word,
+% can take zero or one argument.
+\def\@IEEEPARstartWORDFONTSTYLE{\relax}
+% V1.7 any additional, non-font related commands needed to modify
+% the rest of the word, can take zero or one argument.
+\def\@IEEEPARstartWORDCAPSTYLE{\MakeUppercase}
+% This is the horizontal separation distance from the drop letter to the main text.
+% Lengths that depend on the font (e.g., ex, em, etc.) will be referenced
+% to the font that is active when \IEEEPARstart is called. 
+\def\@IEEEPARstartSEP{0.15em}
+% V1.7 horizontal offset applied to the left of the drop cap.
+\def\@IEEEPARstartHOFFSET{0em}
+% V1.7 Italic correction command applied at the end of the drop cap.
+\def\@IEEEPARstartITLCORRECT{\/}
+
+% V1.7 compoc uses nonbold drop cap and small caps word style
+\ifCLASSOPTIONcompsoc
+\def\@IEEEPARstartFONTSTYLE{\mdseries}
+\def\@IEEEPARstartWORDFONTSTYLE{\scshape}
+\def\@IEEEPARstartWORDCAPSTYLE{\relax}
+\fi
+
+% definition of \IEEEPARstart
+% THIS IS A CONTROLLED SPACING AREA, DO NOT ALLOW SPACES WITHIN THESE LINES
+% 
+% The token \@IEEEPARstartfont will be globally defined after the first use
+% of \IEEEPARstart and will be a font command which creates the big letter
+% The first argument is the first letter of the first word and the second
+% argument is the rest of the first word(s).
+\def\IEEEPARstart#1#2{\par{%
+% if this page does not have enough space, break it and lets start
+% on a new one
+\@IEEEtranneedspace{\@IEEEPARstartMINPAGELINES\baselineskip}{\relax}%
+% V1.7 move this up here in case user uses \textbf for \@IEEEPARstartFONTSTYLE
+% which uses command \leavevmode which causes an unwanted \indent to be issued
+\noindent
+% calculate the desired height of the big letter
+% it extends from the top of \@IEEEPARstartHEIGHTTEXT in the current font
+% down to \@IEEEPARstartDROPDEPTH below the current baseline
+\settoheight{\@IEEEtrantmpdimenA}{\@IEEEPARstartHEIGHTTEXT}%
+\addtolength{\@IEEEtrantmpdimenA}{\@IEEEPARstartDROPDEPTH}%
+% extract the name of the current font in bold
+% and place it in \@IEEEPARstartFONTNAME
+\def\@IEEEPARstartGETFIRSTWORD##1 ##2\relax{##1}%
+{\@IEEEPARstartFONTSTYLE{\selectfont\edef\@IEEEPARstartFONTNAMESPACE{\fontname\font\space}%
+\xdef\@IEEEPARstartFONTNAME{\expandafter\@IEEEPARstartGETFIRSTWORD\@IEEEPARstartFONTNAMESPACE\relax}}}%
+% define a font based on this name with a point size equal to the desired
+% height of the drop letter
+\font\@IEEEPARstartsubfont\@IEEEPARstartFONTNAME\space at \@IEEEtrantmpdimenA\relax%
+% save this value as a counter (integer) value (sp points)
+\@IEEEtrantmpcountA=\@IEEEtrantmpdimenA%
+% now get the height of the actual letter produced by this font size
+\settoheight{\@IEEEtrantmpdimenB}{\@IEEEPARstartsubfont\@IEEEPARstartCAPSTYLE{#1}}%
+% If something bogus happens like the first argument is empty or the
+% current font is strange, do not allow a zero height.
+\ifdim\@IEEEtrantmpdimenB=0pt\relax%
+\typeout{** WARNING: IEEEPARstart drop letter has zero height! (line \the\inputlineno)}%
+\typeout{ Forcing the drop letter font size to 10pt.}%
+\@IEEEtrantmpdimenB=10pt%
+\fi%
+% and store it as a counter
+\@IEEEtrantmpcountB=\@IEEEtrantmpdimenB%
+% Since a font size doesn't exactly correspond to the height of the capital
+% letters in that font, the actual height of the letter, \@IEEEtrantmpcountB,
+% will be less than that desired, \@IEEEtrantmpcountA
+% we need to raise the font size, \@IEEEtrantmpdimenA 
+% by \@IEEEtrantmpcountA / \@IEEEtrantmpcountB
+% But, TeX doesn't have floating point division, so we have to use integer
+% division. Hence the use of the counters.
+% We need to reduce the denominator so that the loss of the remainder will
+% have minimal affect on the accuracy of the result
+\divide\@IEEEtrantmpcountB by 200%
+\divide\@IEEEtrantmpcountA by \@IEEEtrantmpcountB%
+% Then reequalize things when we use TeX's ability to multiply by
+% floating point values
+\@IEEEtrantmpdimenB=0.005\@IEEEtrantmpdimenA%
+\multiply\@IEEEtrantmpdimenB by \@IEEEtrantmpcountA%
+% \@IEEEPARstartfont is globaly set to the calculated font of the big letter
+% We need to carry this out of the local calculation area to to create the
+% big letter.
+\global\font\@IEEEPARstartfont\@IEEEPARstartFONTNAME\space at \@IEEEtrantmpdimenB%
+% Now set \@IEEEtrantmpdimenA to the width of the big letter
+% We need to carry this out of the local calculation area to set the
+% hanging indent
+\settowidth{\global\@IEEEtrantmpdimenA}{\@IEEEPARstartfont
+\@IEEEPARstartCAPSTYLE{#1\@IEEEPARstartITLCORRECT}}}%
+% end of the isolated calculation environment
+% add in the extra clearance we want
+\advance\@IEEEtrantmpdimenA by \@IEEEPARstartSEP\relax%
+% add in the optional offset
+\advance\@IEEEtrantmpdimenA by \@IEEEPARstartHOFFSET\relax%
+% V1.7 don't allow negative offsets to produce negative hanging indents
+\@IEEEtrantmpdimenB\@IEEEtrantmpdimenA
+\ifnum\@IEEEtrantmpdimenB < 0 \@IEEEtrantmpdimenB 0pt\fi
+% \@IEEEtrantmpdimenA has the width of the big letter plus the
+% separation space and \@IEEEPARstartfont is the font we need to use
+% Now, we make the letter and issue the hanging indent command
+% The letter is placed in a box of zero width and height so that other
+% text won't be displaced by it.
+\hangindent\@IEEEtrantmpdimenB\hangafter=-\@IEEEPARstartDROPLINES%
+\makebox[0pt][l]{\hspace{-\@IEEEtrantmpdimenA}%
+\raisebox{-\@IEEEPARstartDROPDEPTH}[0pt][0pt]{\hspace{\@IEEEPARstartHOFFSET}%
+\@IEEEPARstartfont\@IEEEPARstartCAPSTYLE{#1\@IEEEPARstartITLCORRECT}%
+\hspace{\@IEEEPARstartSEP}}}%
+{\@IEEEPARstartWORDFONTSTYLE{\@IEEEPARstartWORDCAPSTYLE{\selectfont#2}}}}
+
+
+
+
+
+
+% determines if the space remaining on a given page is equal to or greater
+% than the specified space of argument one
+% if not, execute argument two (only if the remaining space is greater than zero)
+% and issue a \newpage
+% 
+% example: \@IEEEtranneedspace{2in}{\vfill}
+% 
+% Does not take into consideration rubber shrinkage, so it tends to
+% be overly cautious
+% Based on an example posted by Donald Arseneau
+% Note this macro uses \@IEEEtrantmpdimenB internally for calculations,
+% so DO NOT PASS \@IEEEtrantmpdimenB to this routine
+% if you need a dimen register, import with \@IEEEtrantmpdimenA instead
+\def\@IEEEtranneedspace#1#2{\penalty-100\begingroup%shield temp variable
+\@IEEEtrantmpdimenB\pagegoal\advance\@IEEEtrantmpdimenB-\pagetotal% space left
+\ifdim #1>\@IEEEtrantmpdimenB\relax% not enough space left
+\ifdim\@IEEEtrantmpdimenB>\z@\relax #2\fi%
+\newpage%
+\fi\endgroup}
+
+
+
+% IEEEbiography ENVIRONMENT
+% Allows user to enter biography leaving place for picture (adapts to font size)
+% As of V1.5, a new optional argument allows you to have a real graphic!
+% V1.5 and later also fixes the "colliding biographies" which could happen when a 
+% biography's text was shorter than the space for the photo.
+% MDS 7/2001
+% V1.6 prevent multiple biographies from making multiple TOC entries
+\newif\if@IEEEbiographyTOCentrynotmade
+\global\@IEEEbiographyTOCentrynotmadetrue
+
+% biography counter so hyperref can jump directly to the biographies
+% and not just the previous section
+\newcounter{IEEEbiography}
+\setcounter{IEEEbiography}{0}
+
+% photo area size
+\def\@IEEEBIOphotowidth{1.0in}    % width of the biography photo area
+\def\@IEEEBIOphotodepth{1.25in}   % depth (height) of the biography photo area
+% area cleared for photo
+\def\@IEEEBIOhangwidth{1.14in}    % width cleared for the biography photo area
+\def\@IEEEBIOhangdepth{1.25in}    % depth cleared for the biography photo area
+                                  % actual depth will be a multiple of 
+                                  % \baselineskip, rounded up
+\def\@IEEEBIOskipN{4\baselineskip}% nominal value of the vskip above the biography
+
+\newenvironment{IEEEbiography}[2][]{\normalfont\@IEEEcompsoconly{\sffamily}\footnotesize%
+\unitlength 1in\parskip=0pt\par\parindent 1em\interlinepenalty500%
+% we need enough space to support the hanging indent
+% the nominal value of the spacer
+% and one extra line for good measure
+\@IEEEtrantmpdimenA=\@IEEEBIOhangdepth%
+\advance\@IEEEtrantmpdimenA by \@IEEEBIOskipN%
+\advance\@IEEEtrantmpdimenA by 1\baselineskip%
+% if this page does not have enough space, break it and lets start
+% with a new one
+\@IEEEtranneedspace{\@IEEEtrantmpdimenA}{\relax}%
+% nominal spacer can strech, not shrink use 1fil so user can out stretch with \vfill
+\vskip \@IEEEBIOskipN plus 1fil minus 0\baselineskip%
+% the default box for where the photo goes
+\def\@IEEEtempbiographybox{{\setlength{\fboxsep}{0pt}\framebox{%
+\begin{minipage}[b][\@IEEEBIOphotodepth][c]{\@IEEEBIOphotowidth}\centering PLACE\\ PHOTO\\ HERE \end{minipage}}}}%
+%
+% detect if the optional argument was supplied, this requires the
+% \@ifmtarg command as defined in the appendix section above
+% and if so, override the default box with what they want
+\@ifmtarg{#1}{\relax}{\def\@IEEEtempbiographybox{\mbox{\begin{minipage}[b][\@IEEEBIOphotodepth][c]{\@IEEEBIOphotowidth}%
+\centering%
+#1%
+\end{minipage}}}}% end if optional argument supplied
+% Make an entry into the table of contents only if we have not done so before
+\if@IEEEbiographyTOCentrynotmade%
+% link labels to the biography counter so hyperref will jump
+% to the biography, not the previous section
+\setcounter{IEEEbiography}{-1}%
+\refstepcounter{IEEEbiography}%
+\addcontentsline{toc}{section}{Biographies}%
+\global\@IEEEbiographyTOCentrynotmadefalse%
+\fi%
+% one more biography
+\refstepcounter{IEEEbiography}%
+% Make an entry for this name into the table of contents 
+\addcontentsline{toc}{subsection}{#2}%
+% V1.6 properly handle if a new paragraph should occur while the
+% hanging indent is still active. Do this by redefining \par so
+% that it will not start a new paragraph. (But it will appear to the
+% user as if it did.) Also, strip any leading pars, newlines, or spaces.
+\let\@IEEEBIOORGparCMD=\par% save the original \par command
+\edef\par{\hfil\break\indent}% the new \par will not be a "real" \par
+\settoheight{\@IEEEtrantmpdimenA}{\@IEEEtempbiographybox}% get height of biography box
+\@IEEEtrantmpdimenB=\@IEEEBIOhangdepth%
+\@IEEEtrantmpcountA=\@IEEEtrantmpdimenB% countA has the hang depth
+\divide\@IEEEtrantmpcountA by \baselineskip%  calculates lines needed to produce the hang depth
+\advance\@IEEEtrantmpcountA by 1% ensure we overestimate
+% set the hanging indent
+\hangindent\@IEEEBIOhangwidth%
+\hangafter-\@IEEEtrantmpcountA%
+% reference the top of the photo area to the top of a capital T
+\settoheight{\@IEEEtrantmpdimenB}{\mbox{T}}%
+% set the photo box, give it zero width and height so as not to disturb anything
+\noindent\makebox[0pt][l]{\hspace{-\@IEEEBIOhangwidth}\raisebox{\@IEEEtrantmpdimenB}[0pt][0pt]{%
+\raisebox{-\@IEEEBIOphotodepth}[0pt][0pt]{\@IEEEtempbiographybox}}}%
+% now place the author name and begin the bio text
+\noindent\textbf{#2\ }\@IEEEgobbleleadPARNLSP}{\relax\let\par=\@IEEEBIOORGparCMD\par%
+% 7/2001 V1.5 detect when the biography text is shorter than the photo area
+% and pad the unused area - preventing a collision from the next biography entry
+% MDS
+\ifnum \prevgraf <\@IEEEtrantmpcountA\relax% detect when the biography text is shorter than the photo
+    \advance\@IEEEtrantmpcountA by -\prevgraf% calculate how many lines we need to pad
+    \advance\@IEEEtrantmpcountA by -1\relax% we compensate for the fact that we indented an extra line
+    \@IEEEtrantmpdimenA=\baselineskip% calculate the length of the padding
+    \multiply\@IEEEtrantmpdimenA by \@IEEEtrantmpcountA%
+    \noindent\rule{0pt}{\@IEEEtrantmpdimenA}% insert an invisible support strut
+\fi%
+\par\normalfont}
+
+
+
+% V1.6
+% added biography without a photo environment
+\newenvironment{IEEEbiographynophoto}[1]{%
+% Make an entry into the table of contents only if we have not done so before
+\if@IEEEbiographyTOCentrynotmade%
+% link labels to the biography counter so hyperref will jump
+% to the biography, not the previous section
+\setcounter{IEEEbiography}{-1}%
+\refstepcounter{IEEEbiography}%
+\addcontentsline{toc}{section}{Biographies}%
+\global\@IEEEbiographyTOCentrynotmadefalse%
+\fi%
+% one more biography
+\refstepcounter{IEEEbiography}%
+% Make an entry for this name into the table of contents 
+\addcontentsline{toc}{subsection}{#1}%
+\normalfont\@IEEEcompsoconly{\sffamily}\footnotesize\interlinepenalty500%
+\vskip 4\baselineskip plus 1fil minus 0\baselineskip%
+\parskip=0pt\par%
+\noindent\textbf{#1\ }\@IEEEgobbleleadPARNLSP}{\relax\par\normalfont}
+
+
+% provide the user with some old font commands
+% got this from article.cls
+\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
+\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
+\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
+\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
+\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
+\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
+\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
+\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
+\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
+
+
+% SPECIAL PAPER NOTICE COMMANDS
+% 
+% holds the special notice text
+\def\@IEEEspecialpapernotice{\relax}
+% for special papers, like invited papers, the user can do:
+% \IEEEspecialpapernotice{(Invited Paper)} before \maketitle
+\def\IEEEspecialpapernotice#1{\ifCLASSOPTIONconference%
+\def\@IEEEspecialpapernotice{{\sublargesize\textit{#1}\vspace*{1em}}}%
+\else%
+\def\@IEEEspecialpapernotice{{\\*[1.5ex]\sublargesize\textit{#1}}\vspace*{-2ex}}%
+\fi}
+
+
+
+
+% PUBLISHER ID COMMANDS
+% to insert a publisher's ID footer
+% V1.6 \IEEEpubid has been changed so that the change in page size and style
+% occurs in \maketitle. \IEEEpubid must now be issued prior to \maketitle
+% use \IEEEpubidadjcol as before - in the second column of the title page
+% These changes allow \maketitle to take the reduced page height into
+% consideration when dynamically setting the space between the author 
+% names and the maintext.
+%
+% the amount the main text is pulled up to make room for the
+% publisher's ID footer
+% IEEE uses about 1.3\baselineskip for journals, 
+% dynamic title spacing will clean up the fraction
+\def\@IEEEpubidpullup{1.3\baselineskip}
+\ifCLASSOPTIONtechnote
+% for technotes it must be an integer of baselineskip as there can be no
+% dynamic title spacing for two column mode technotes (the title is in the
+% in first column) and we should maintain an integer number of lines in the
+% second column
+% There are some examples (such as older issues of "Transactions on
+% Information Theory") in which IEEE really pulls the text off the ID for
+% technotes - about 0.55in (or 4\baselineskip). We'll use 2\baselineskip
+% and call it even.
+\def\@IEEEpubidpullup{2\baselineskip}
+\fi
+
+% V1.7 compsoc does not use a pullup
+\ifCLASSOPTIONcompsoc
+\def\@IEEEpubidpullup{0pt}
+\fi
+
+% holds the ID text
+\def\@IEEEpubid{\relax}
+
+% flag so \maketitle can tell if \IEEEpubid was called
+\newif\if@IEEEusingpubid
+\global\@IEEEusingpubidfalse
+% issue this command in the page to have the ID at the bottom
+% V1.6 use before \maketitle
+\def\IEEEpubid#1{\def\@IEEEpubid{#1}\global\@IEEEusingpubidtrue}
+
+
+% command which will pull up (shorten) the column it is executed in
+% to make room for the publisher ID. Place in the second column of
+% the title page when using \IEEEpubid
+% Is smart enough not to do anything when in single column text or
+% if the user hasn't called \IEEEpubid
+% currently needed in for the second column of a page with the
+% publisher ID. If not needed in future releases, please provide this
+% command and define it as \relax for backward compatibility
+% v1.6b do not allow command to operate if the peer review option has been 
+% selected because \IEEEpubidadjcol will not be on the cover page.
+% V1.7 do nothing if compsoc
+\def\IEEEpubidadjcol{\ifCLASSOPTIONcompsoc\else\ifCLASSOPTIONpeerreview\else
+\if@twocolumn\if@IEEEusingpubid\enlargethispage{-\@IEEEpubidpullup}\fi\fi\fi\fi}
+
+% Special thanks to Peter Wilson, Daniel Luecking, and the other
+% gurus at comp.text.tex, for helping me to understand how best to
+% implement the IEEEpubid command in LaTeX.
+
+
+
+%% Lockout some commands under various conditions
+
+% general purpose bit bucket
+\newsavebox{\@IEEEtranrubishbin}
+
+% flags to prevent multiple warning messages
+\newif\if@IEEEWARNthanks
+\newif\if@IEEEWARNIEEEPARstart
+\newif\if@IEEEWARNIEEEbiography
+\newif\if@IEEEWARNIEEEbiographynophoto
+\newif\if@IEEEWARNIEEEpubid
+\newif\if@IEEEWARNIEEEpubidadjcol
+\newif\if@IEEEWARNIEEEmembership
+\newif\if@IEEEWARNIEEEaftertitletext
+\@IEEEWARNthankstrue
+\@IEEEWARNIEEEPARstarttrue
+\@IEEEWARNIEEEbiographytrue
+\@IEEEWARNIEEEbiographynophototrue
+\@IEEEWARNIEEEpubidtrue
+\@IEEEWARNIEEEpubidadjcoltrue
+\@IEEEWARNIEEEmembershiptrue
+\@IEEEWARNIEEEaftertitletexttrue
+
+
+%% Lockout some commands when in various modes, but allow them to be restored if needed
+%%
+% save commands which might be locked out
+% so that the user can later restore them if needed
+\let\@IEEESAVECMDthanks\thanks
+\let\@IEEESAVECMDIEEEPARstart\IEEEPARstart
+\let\@IEEESAVECMDIEEEbiography\IEEEbiography
+\let\@IEEESAVECMDendIEEEbiography\endIEEEbiography
+\let\@IEEESAVECMDIEEEbiographynophoto\IEEEbiographynophoto
+\let\@IEEESAVECMDendIEEEbiographynophoto\endIEEEbiographynophoto
+\let\@IEEESAVECMDIEEEpubid\IEEEpubid
+\let\@IEEESAVECMDIEEEpubidadjcol\IEEEpubidadjcol
+\let\@IEEESAVECMDIEEEmembership\IEEEmembership
+\let\@IEEESAVECMDIEEEaftertitletext\IEEEaftertitletext
+
+
+% disable \IEEEPARstart when in draft mode
+% This may have originally been done because the pre-V1.6 drop letter
+% algorithm had problems with a non-unity baselinestretch
+% At any rate, it seems too formal to have a drop letter in a draft
+% paper.
+\ifCLASSOPTIONdraftcls
+\def\IEEEPARstart#1#2{#1#2\if@IEEEWARNIEEEPARstart\typeout{** ATTENTION: \noexpand\IEEEPARstart
+ is disabled in draft mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEPARstartfalse}
+\fi
+% and for technotes
+\ifCLASSOPTIONtechnote
+\def\IEEEPARstart#1#2{#1#2\if@IEEEWARNIEEEPARstart\typeout{** WARNING: \noexpand\IEEEPARstart
+ is locked out for technotes (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEPARstartfalse}
+\fi
+
+
+% lockout unneeded commands when in conference mode
+\ifCLASSOPTIONconference
+% when locked out, \thanks, \IEEEbiography, \IEEEbiographynophoto, \IEEEpubid,
+% \IEEEmembership and \IEEEaftertitletext will all swallow their given text. 
+% \IEEEPARstart will output a normal character instead
+% warn the user about these commands only once to prevent the console screen
+% from filling up with redundant messages
+\def\thanks#1{\if@IEEEWARNthanks\typeout{** WARNING: \noexpand\thanks
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNthanksfalse}
+\def\IEEEPARstart#1#2{#1#2\if@IEEEWARNIEEEPARstart\typeout{** WARNING: \noexpand\IEEEPARstart
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEPARstartfalse}
+
+
+% LaTeX treats environments and commands with optional arguments differently.
+% the actual ("internal") command is stored as \\commandname 
+% (accessed via \csname\string\commandname\endcsname )
+% the "external" command \commandname is a macro with code to determine
+% whether or not the optional argument is presented and to provide the 
+% default if it is absent. So, in order to save and restore such a command
+% we would have to save and restore \\commandname as well. But, if LaTeX
+% ever changes the way it names the internal names, the trick would break.
+% Instead let us just define a new environment so that the internal
+% name can be left undisturbed.
+\newenvironment{@IEEEbogusbiography}[2][]{\if@IEEEWARNIEEEbiography\typeout{** WARNING: \noexpand\IEEEbiography
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEbiographyfalse%
+\setbox\@IEEEtranrubishbin\vbox\bgroup}{\egroup\relax}
+% and make biography point to our bogus biography
+\let\IEEEbiography=\@IEEEbogusbiography
+\let\endIEEEbiography=\end@IEEEbogusbiography
+
+\renewenvironment{IEEEbiographynophoto}[1]{\if@IEEEWARNIEEEbiographynophoto\typeout{** WARNING: \noexpand\IEEEbiographynophoto
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEbiographynophotofalse%
+\setbox\@IEEEtranrubishbin\vbox\bgroup}{\egroup\relax}
+
+\def\IEEEpubid#1{\if@IEEEWARNIEEEpubid\typeout{** WARNING: \noexpand\IEEEpubid 
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEpubidfalse}
+\def\IEEEpubidadjcol{\if@IEEEWARNIEEEpubidadjcol\typeout{** WARNING: \noexpand\IEEEpubidadjcol
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEpubidadjcolfalse}
+\def\IEEEmembership#1{\if@IEEEWARNIEEEmembership\typeout{** WARNING: \noexpand\IEEEmembership
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEmembershipfalse}
+\def\IEEEaftertitletext#1{\if@IEEEWARNIEEEaftertitletext\typeout{** WARNING: \noexpand\IEEEaftertitletext
+ is locked out when in conference mode (line \the\inputlineno).}\fi\global\@IEEEWARNIEEEaftertitletextfalse}
+\fi
+
+
+% provide a way to restore the commands that are locked out
+\def\IEEEoverridecommandlockouts{%
+\typeout{** ATTENTION: Overriding command lockouts (line \the\inputlineno).}%
+\let\thanks\@IEEESAVECMDthanks%
+\let\IEEEPARstart\@IEEESAVECMDIEEEPARstart%
+\let\IEEEbiography\@IEEESAVECMDIEEEbiography%
+\let\endIEEEbiography\@IEEESAVECMDendIEEEbiography%
+\let\IEEEbiographynophoto\@IEEESAVECMDIEEEbiographynophoto%
+\let\endIEEEbiographynophoto\@IEEESAVECMDendIEEEbiographynophoto%
+\let\IEEEpubid\@IEEESAVECMDIEEEpubid%
+\let\IEEEpubidadjcol\@IEEESAVECMDIEEEpubidadjcol%
+\let\IEEEmembership\@IEEESAVECMDIEEEmembership%
+\let\IEEEaftertitletext\@IEEESAVECMDIEEEaftertitletext}
+
+
+
+% need a backslash character for typeout output
+{\catcode`\|=0 \catcode`\\=12
+|xdef|@IEEEbackslash{\}}
+
+
+% hook to allow easy disabling of all legacy warnings
+\def\@IEEElegacywarn#1#2{\typeout{** ATTENTION: \@IEEEbackslash #1 is deprecated (line \the\inputlineno).
+Use \@IEEEbackslash #2 instead.}}
+
+
+% provide for legacy commands
+\def\authorblockA{\@IEEElegacywarn{authorblockA}{IEEEauthorblockA}\IEEEauthorblockA}
+\def\authorblockN{\@IEEElegacywarn{authorblockN}{IEEEauthorblockN}\IEEEauthorblockN}
+\def\authorrefmark{\@IEEElegacywarn{authorrefmark}{IEEEauthorrefmark}\IEEEauthorrefmark}
+\def\PARstart{\@IEEElegacywarn{PARstart}{IEEEPARstart}\IEEEPARstart}
+\def\pubid{\@IEEElegacywarn{pubid}{IEEEpubid}\IEEEpubid}
+\def\pubidadjcol{\@IEEElegacywarn{pubidadjcol}{IEEEpubidadjcol}\IEEEpubidadjcol}
+\def\QED{\@IEEElegacywarn{QED}{IEEEQED}\IEEEQED}
+\def\QEDclosed{\@IEEElegacywarn{QEDclosed}{IEEEQEDclosed}\IEEEQEDclosed}
+\def\QEDopen{\@IEEElegacywarn{QEDopen}{IEEEQEDopen}\IEEEQEDopen}
+\def\specialpapernotice{\@IEEElegacywarn{specialpapernotice}{IEEEspecialpapernotice}\IEEEspecialpapernotice}
+
+
+
+% provide for legacy environments
+\def\biography{\@IEEElegacywarn{biography}{IEEEbiography}\IEEEbiography}
+\def\biographynophoto{\@IEEElegacywarn{biographynophoto}{IEEEbiographynophoto}\IEEEbiographynophoto}
+\def\keywords{\@IEEElegacywarn{keywords}{IEEEkeywords}\IEEEkeywords}
+\def\endbiography{\endIEEEbiography}
+\def\endbiographynophoto{\endIEEEbiographynophoto}
+\def\endkeywords{\endIEEEkeywords}
+
+
+% provide for legacy IED commands/lengths when possible
+\let\labelindent\IEEElabelindent
+\def\calcleftmargin{\@IEEElegacywarn{calcleftmargin}{IEEEcalcleftmargin}\IEEEcalcleftmargin}
+\def\setlabelwidth{\@IEEElegacywarn{setlabelwidth}{IEEEsetlabelwidth}\IEEEsetlabelwidth}
+\def\usemathlabelsep{\@IEEElegacywarn{usemathlabelsep}{IEEEusemathlabelsep}\IEEEusemathlabelsep}
+\def\iedlabeljustifyc{\@IEEElegacywarn{iedlabeljustifyc}{IEEEiedlabeljustifyc}\IEEEiedlabeljustifyc}
+\def\iedlabeljustifyl{\@IEEElegacywarn{iedlabeljustifyl}{IEEEiedlabeljustifyl}\IEEEiedlabeljustifyl}
+\def\iedlabeljustifyr{\@IEEElegacywarn{iedlabeljustifyr}{IEEEiedlabeljustifyr}\IEEEiedlabeljustifyr}
+
+
+
+% let \proof use the IEEEtran version even after amsthm is loaded
+% \proof is now deprecated in favor of \IEEEproof
+\AtBeginDocument{\def\proof{\@IEEElegacywarn{proof}{IEEEproof}\IEEEproof}\def\endproof{\endIEEEproof}}
+
+% V1.7 \overrideIEEEmargins is no longer supported.
+\def\overrideIEEEmargins{%
+\typeout{** WARNING: \string\overrideIEEEmargins \space no longer supported (line \the\inputlineno).}%
+\typeout{** Use the \string\CLASSINPUTinnersidemargin, \string\CLASSINPUToutersidemargin \space controls instead.}}
+
+
+\endinput
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%% End of IEEEtran.cls  %%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% That's all folks!
+
diff --git a/src/libswift_udp/doc/mfold-article/swarm.png b/src/libswift_udp/doc/mfold-article/swarm.png
new file mode 100644 (file)
index 0000000..339ac14
Binary files /dev/null and b/src/libswift_udp/doc/mfold-article/swarm.png differ
diff --git a/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.bib b/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.bib
new file mode 100644 (file)
index 0000000..17c9257
--- /dev/null
@@ -0,0 +1,30 @@
+@INPROCEEDINGS{hemminger:lca2005,
+       AUTHOR = "Stephen Hemminger",
+       TITLE = "Network Emulation with NetEm",
+       BOOKTITLE = "Proceedings of the 6th Australia's National Linux
+                  Conference (LCA2005), Canberra, Australia.",
+       MONTH = "April",
+       YEAR = {2005}
+}
+
+@MISC{devera:htb,
+       AUTHOR = "Martin Devera",
+       TITLE = "HTB home",
+       HOWPUBLISHED={\url{http://luxik.cdi.cz/~devik/qos/htb/}}
+}
+
+@INPROCEEDINGS{perala:tridentcom2010,
+       AUTHOR = "Pekka H. J. Per{\"a}l{\"a} and Jori P. Paananen and Milton
+                  Mukhopadhyay and Jukka-Pekka Laulajainen",
+       TITLE = "A Novel Testbed for P2P Networks",
+       BOOKTITLE = "Testbeds and Research
+                  Infrastructures. Development of Networks and
+                  Communities: 6th International ICST Conference,
+                  TridentCom 2010",
+       PAGES = "69-83",
+       ORGANIZATION = "The Institute for Computer Sciences, Social
+                  Informatics and Telecommunications Engineering
+                  (ICST)",
+       MONTH = "May",
+       YEAR = {2010}
+}
diff --git a/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.pdf b/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.pdf
new file mode 100644 (file)
index 0000000..cb7f2db
Binary files /dev/null and b/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.pdf differ
diff --git a/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.tex b/src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.tex
new file mode 100644 (file)
index 0000000..6fd1530
--- /dev/null
@@ -0,0 +1,255 @@
+\documentclass[conference]{IEEEtran}
+\usepackage{graphicx}
+\usepackage{url}
+\usepackage{stfloats}
+\usepackage[percent]{overpic}
+\bstctlcite{IEEEexample:BSTcontrol}
+
+\begin{document}
+
+\title{Manifold: $O(N^2)$ testing of network protocols}
+
+\author{\IEEEauthorblockN{XXX}
+\IEEEauthorblockA{XXX\\
+XXX,\\ XXX \\
+Email: XXX}
+\and
+\IEEEauthorblockN{XXX}
+\IEEEauthorblockA{XXX\\
+XXX\\
+XXX}
+\and
+\IEEEauthorblockN{XXX}
+\IEEEauthorblockA{XXX\\
+XXX,\\ XXX \\
+Email: XXX}
+}
+%\author{\IEEEauthorblockN{Victor~Grishchenko}
+%\IEEEauthorblockA{Delft University of Technology\\
+%Mekelweg 4, Delft,\\ The Netherlands \\
+%Email: victor.grishchenko@gmail.com}
+%\and
+%\IEEEauthorblockN{Jori~Paananen}
+%\IEEEauthorblockA{VTT Technical Research Centre of Finland\\
+%Vuorimiehentie 3, Espoo, Finland\\
+%Email: jori.paananen@vtt.fi}
+%\and
+%\IEEEauthorblockN{J. Pouwelse}
+%\IEEEauthorblockA{Delft University of Technology\\
+%Mekelweg 4, Delft,\\ The Netherlands \\
+%Email: j.a.pouwelse@ewi.tudelft.nl}
+%}
+
+\maketitle
+
+\begin{abstract}
+While developing a UDP-based peer-to-peer transport protocol, we faced the problem of testing the implementation, its state machine, and congestion control algorithms. The problem is known to be fundamentally hard. Discoveries of decades-old bugs in TCP/IP stacks give a good illustration to this. Not being satisfied with classic methods, we have created the Manifold framework for automated massively parallel testing. Our main challenge was the combinatorial amount of diverse network conditions, protocol states and code paths affecting the implementation's behavior. By running traffic flows between every pair of nodes, Manifold covers $O(N^2)$ combinations of simulated and/or real network conditions thus performing massive case coverage in limited time. Reports, graphs, and a full system-wide event log allows to trace code paths and investigate problems. Being integrated into the code/build/test loop, Manifold instantly reveals both progress and regressions and enables rapid iterations on the code.
+\end{abstract}
+% jori 21.4: Should the last sentence above be 'Manifold instantly
+% reveals both progress and regressions and enables rapid iterations
+% on the code.'
+
+\section{Introduction}
+The development and testing of network protocols is made difficult by the non-determinism of distributed systems. Congestion control is one of the most complicated topics as workings of the algorithms heavily depend on race conditions, packet losses, and other peculiarities of network behavior. Being put in somewhat different conditions, a ``proven'' code might turn ``problematic'', as it was the case with the famous LFN problem~\cite{lfn1,lfn2} of TCP. Bugs in TCP implementations are found till this day~\cite{tcp-bug}, despite the excessive level of use and testing in past 30 years.
+New TCP congestion control algorithms are normally tested in the settings of dumb bell and/or parking lot~\cite{tcp-eval} simulated network topologies, as well as in the wild. The dumb bell setting allows to test stream behavior while competing with other flows for a single bottleneck; the parking lot topology simulates a sequence of bottlenecks.
+
+We have developed a multiparty (swarming) transport protocol~\cite{swift} using the LEDBAT~\cite{ledbat} least-than-best-effort (``scavenger'') congestion control algorithm. We had to test the implementation's behavior in a swarm, an aspect not addressed by the classic methods.
+As well, we found out, that those methods do not allow to fully test a protocol and its implementation against various network behavior peculiarities.
+
+As an illustrative anecdote, one of the authors was debugging the implementation on an ordinary DSL line, when its uplink losses suddenly went to 10\% because of some technical problem on the ISP side. Regular web browsing was unaffected by the issue, as TCP is highly resilient to acknowledgement losses on the reverse (i.e. receiver to sender) path. Thus, the search for a non-existing regression lasted for a day, till the author decided to upload some photos thus uncovering the issue. Informally, a network might be ``special'' or simply ``broken'' in many different ways, and we needed a systematic way of checking our code against those peculiarities.
+
+As any change in the code might cause unintended effects in particular network conditions, we needed a fast way of checking those effects to track our progress as well as regressions.
+While unit tests check for \emph{correctness} of a deterministic result given certain inputs, we needed massive case coverage of network conditions to ensure the results are \emph{acceptable} in every particular case.
+Ideally, a test run had to be integrated into the regular code-build-test loop, similarly to unit testing.
+
+Thus, we came up with the idea of $O(N^{2})$ testing where $N$ real or emulated nodes represent different network conditions (high RTT, jitter, losses, NAT, asymmetry, etc). During one test run, we send traffic flows between every pair of nodes, ideally covering $O(N^{2})$ combinations of network conditions, like ``RTT and jitter'', or ``NAT and losses''.
+The testing setup had to run tests and present the resulting statistics comprehensibly and quickly, to allow for repeated testing runs.
+
+\section{Manifold test suite}
+
+% jori 21.4: The abbrev everywhere in {\tt mfold}?
+
+The building of the Manifold testing suite started with the realization that manually testing the code under various network conditions is an extremely cumbersome and error-prone process, as setting up network configurations involves multi-step technical operations, likely spanning several hosts.
+
+Thus, the objective was to implement our $O(N^{2})$ testing approach using simple, improvised means, allowing for maximum parallelism, supporting diverse  real and emulated setups. The system had to be simple enough and flexible in adapting to conditions, as the testing setups necessarily included diverse existing servers as well as (uniform) clusters.     
+
+The resulting suite is a collection of shell scripts intended for use
+with Linux/Unix test machines. Scripts are launched from a single controlling
+machine using Secure Shell ({\tt ssh}).
+Log parsing is done with {\tt perl} scripts, graphs are created with {\tt gnuplot}. Manifold scripts are included in the open source implementation of our protocol~\cite{libswift}.
+
+%: bash, ssh, perl for log parsing
+%easily customize by the same means
+%parallel as possible (time) parallelize execution as well
+%pluggable; extension script and variables
+%The Manifold (mfold) test suite enables creating and running test swarms with various topologies and a unique network QoS for each peer. After distributed parsing, the test results are combined and visualized.
+
+\subsection{General workings}
+
+Manifold execution is centered around a ``fan-out'' shell script named {\tt doall} that opens parallel {\tt ssh} sessions to every server of the testing setup and runs all the necessary commands.
+Every run involves a sequence of operations, typically {\tt build}, {\tt netem}, {\tt run}, {\tt clean}, all ran by {\tt doall}. For example, {\tt ./doall build} will check out a certain version of the code, check for dependencies, build it and do fast unit tests, on every server of the testing setup, in parallel.
+Individual server quirks are resolved through per-server plug-in extension scripts or environment variable profiles. Typically, in most cases it suffices to adjust environment variables in a profile script (named {\tt env.hostname.sh}). Given a really special platform, an extension script (e.g. {\tt build.hostname.sh}) might override the default process (i.e. {\tt build.default.sh}).
+
+In all scripts, servers are identified with their {\tt ssh} handles (as opposed to hostnames or IP addresses). That extra level of indirection allows to run several testing nodes at the same physical server or to move a node from one  server to another.
+% 'instances' and 'peer' sound right. Is 'profile name' synonym to % 'ssh hostname?'
+%Names of the servers participating in the swarm are listed in a ``setup'' file. Multiple instances??? can be run at one physical server node by using a different profile name for each peer??? in the same machine and configuring them to use different UDP ports.
+
+%Each running instance leaves very detailed event logs; after every run, all logs are collected, parsed and summarized, the resulting data presented to the user.
+
+\subsection{Traffic manipulation}
+
+Testing the code on real nodes in the wild has its advantages and drawbacks. The main advantage is that the code is tested in a \emph{real} network. The main drawback is that live network conditions are transient and can't be fully reproduced, so different runs may not be comparable. As well, using real setups is expensive. Thus, we developed several test cluster setups using nodes with emulated network conditions.
+
+%In principle, mfold scripts can be used to create a peer-to-peer swarm
+%over any kind of network infrastructure, with peers residing over
+%e.g. different continents. This enables experiments in real-life
+%conditions. However, they lack in controllability and fast
+%modification of network characteristics or in the reproducibility of
+%test results. These are achieved by using also shorter distance high
+%quality connections with traffic manipulation added into the test
+%server network interfaces.
+
+We added scripts to control traffic conditions using two standard Linux kernel queuing disciplines (qdisc) for network devices, used in our previous work \cite{perala} as well.
+HTB (Hierarchy Token Bucket) \cite{devera:htb} provides packet rate
+control capabilities. It also enables emulating different network conditions for several peers sharing the same physical server.
+Using Netem \cite{netem}, we added different packet delay, jitter
+and loss rules to every HTB class.
+%Our approach was similar to \cite{perala:tridentcom2010}. It can be
+%called controlled experimenting, which combines features from pure
+%network emulation in a virtualised environment to experiments with
+%real network equipment over real networks.
+Egress and ingress packet flows can have different sets of qdisc
+parameters. Ingress qdiscs are attached to an IFB (Intermediate
+Functional Block) pseudo-device. Rules are applied based on the UDP port of a packet, as every node occupies a single port.
+
+Thus, HTB/Netem scripts allow to emulate wide range of specific network conditions and to freely mix emulated and real network setups in a single test swarm.
+
+\subsection{Test swarm setups}
+
+Given $N$ peers running on $k$ servers, we may use different variants of a network topology to put an accent on different aspects of protocol behavior.
+We considered three types of topologies: swarm (mesh), chain (sequential data relay) and pairwise transfers. They test the code for swarming behavior, robustness, and single-stream performance, respectively.
+
+% In addition to the peer list, the swarm setup is controlled with the
+% environment variables handling the run parameters of each peer.  They
+% determine e.g. the file name and hash of data to be up- or downloaded,
+% the peers own listening address and port and the tracker address - no
+% tracker address means the peer is only a seeder.
+
+\subsubsection{Large swarms}
+
+This swarm topology mostly tests the code for general robustness, creating near-real-world swarming download scenarios. The main challenge with this topology was to run bigger swarms (and to process the resulting data).
+Limits of the swarm size are determined by the number of parallel ssh
+connections the control machine may start and the maximum number
+of peers each test server may run without exhausting its
+resources. (The former limit could be side-stepped by starting parts
+of a swarm from different control machines). So far, swarms of about thousand peers have been successfully run with one controlling machine (Lenovo T400 laptop with 2GB RAM) and 11-13 servers (Sun Fire X2100 servers with 8GB RAM).
+
+\subsubsection{Chain tests}
+
+Chain tests are mercilessly effective in finding state machine bugs.
+In a chained setup, each node is only connected to the previous (source) and to the next (sink) node. Thus, the data has to traverse the entire chain sequentially.
+That topology is the least forgiving with regard to state machine/ congestion control robustness, as a stall or a slowdown in one flow inevitably affects all the nodes further down the chain. That differs drastically from a swarm topology, that may run fairly well with 50\% transfers failed, because of its high redundancy.
+Technically,  our chained setup restricts node connections by starting local {\tt iptables} firewalls at every node.
+
+\subsubsection{Pairwise tests}
+This setup aims to cleanly test protocol behavior in different network conditions, by eliminating third factors. Namely, with no swarming or data relay, precisely one transfer is done form every node to every other node.
+This topology puts flows on equal footing as opposed to the swarmed and chained setups, where one transfer typically depends on others.
+For larger $N$, it might pose a challenge to run $N^{2}$ streams without interference, using $N$ servers. But, in this particular setup, we need just one node to represent one ``peculiarity''. So, we would not need larger $N$.
+%in a way that every node deals with one stream at a time, still the entire run terminates fast enough. A straightforward solution will consume $O(N)$ time for every run. 
+%(This testing regime is not implemented yet.)
+
+\begin{figure*}[hb]
+\centering
+\includegraphics[width=0.95\textwidth]{big-graph.pdf}
+\caption{A detailed graph exposes congestion control history of a flow.}
+\label{fig:graph}
+\end{figure*}
+
+\subsection{Data harvesting}
+
+Automatic harvesting and analysis of the data turned up to be a major challenge due to the sheer volume of it. While sending or receiving one datagram, a peer generates 10-20 events that are necessary to understand the inner workings of the state machine. A small 10MB transfer requires tens of thousands of datagrams. Given 20-30 peers in an average setup, that results in at least $10^{7}$ log records per a single run, or around 1GB of logs. Not precisely the Google scale, but that data had to be digested and delivered to the user as soon as possible, in a form that allows rapid analysis.
+
+The problem was solved the way it was created. Namely, log processing was implemented to run at the original servers, the controlling machine only left to do one-pass log merge and graph drawing. Thus, data harvesting and analysis was made to scale together with the cluster.
+
+
+% After the test is done and the peers are stopped, a data harvesting
+% script \texttt{dohrv} can be run on the control machine. This script
+% starts one log parser script for each peer on the test servers. The
+% parser scripts gather event-specific information and map the
+% information into the general swarm setup (e.g. IP addresses and ports
+% into ssh \texttt{Host} names). The general output of the parsers is
+% lines of timestamped log events with sending and receiving peer
+% names. This output is sent compressed via ssh to the controlling
+% machine. The output streams are directed through fifos to be merged
+% and sorted by their timestamps to one file. Additionally, the parser
+% scripts write timestamped parameter values (e.g. transferred data,
+% losses, RTT, OWD, CWND) into connection-specific files (identified
+% with the peer names), which are copied onto the control machine.
+
+% In order to prevent lack of resources on the control machine, when
+% parsing results of large swarms, the maximum number of parallel
+% parsings can be controlled with an environment variable. Parsings are
+% then done in a sequence of number of peers divided by maximum number
+% of parsings and sorted into temporary files. When all peers are
+% parsed, the temporary files are themselves merged and sorted.
+
+Although the bulk of parsing and statistics is done at the servers, it turned out, that with larger swarms (hundreds of nodes), even maintaining so many parallel {\tt ssh} connections and merging the logs exhausted the control machine. In order to prevent this, we added an option to restrict the maximum number of parallel parsings. Thus, log processing may be done in a sequence of $\sim\frac{N}{k}$ batches, each batch no more than $k$ logs.
+Since the number of sender-receiver pairs, and thus the number of traffic flows, might be on the order of $N^{2}$, the maximum number of running {\tt gnuplot} instances can also be limited.
+
+% When data copying and sorting is ready, a gnuplot instance is started
+% for each sender-receiver pair. A plot is created showing the
+% connections data out, RTT and its standard deviation, OWD, minimum
+% OWD, CWND, CWND target and send and receive losses over time.
+
+% Since the number of sender-receiver pairs can be almost the square of
+% swarm size at maximum, the maximum number of running gnuplots can
+% again be restricted with an environment variable.
+
+
+% At last, an html page is generated, where information of each
+% connection is shown as an element in a matrix of senders and receivers
+% (Figure \ref{fig:harvest}). The information contains the statistics of
+% sent and received protocol messages and their types. A thumbnail
+% picture of the plotted data is included. Clicking the thumbnail shows
+% a larger plot picture.
+
+% jori 21.4: Should this be \subsection{Reports}? Isn't it part of
+% section\{Manifold test suite}?
+\subsection{Reports}
+
+\begin{figure}[t]
+%\includegraphics[width=0.45\textwidth]{swarm-tomography.pdf}
+\setlength\fboxsep{0pt}
+\setlength\fboxrule{0.5pt}
+\makebox[0pt][l]{\includegraphics[width=0.45\textwidth]{swarm-tomography.pdf}}%
+\makebox[150pt][l]{}\fbox{\includegraphics[width=0.2\textwidth]{stats-cell.pdf}}
+\caption{The main $N$ by $N$ ``harvest'' spreadsheet (back) shows the big picture. Each cell (right) provides statistics on a flow.}
+\label{fig:swarm}
+\end{figure}
+
+The resulting reports must allow the user to rapidly examine the test run traces for performance and abnormalities. The top-level report must be simple enough to let the user grok the ``big picture'' of swarm/flow behavior. Once the user focuses on a particular location or event, it must be easy to switch fast to more detailed data, down to the full event log.
+
+After harvesting and processing the data, Manifold produces an HTML spreadsheet $N$ by $N$, showing summary stats for every flow, as well as small graphs showing dynamics of flows (Fig.~\ref{fig:swarm}).
+At this point, a user is able to estimate performance and stability of the streams. Closer inspection of every statistics bar reveals stats on message patterns. In case the summary raises some suspicions, the user may navigate to a large detailed version of the graph that gives a good overview of congestion control behavior and network conditions during the lifetime of the flow~(see~Fig.~\ref{fig:graph}). The graph plots three groups of parameters: time-based (average round trip time, RTT deviation, one-way delay, minimum delay, delay target~\cite{ledbat}), packet-based (congestion window, outstanding packets) and events (packet losses, detected by timeout or  reordering). This data is sufficient to understand in great detail, how the transfer performed. Once the user is interested in finer details, then the event of interest, its causes and consequences, might be found in the full \emph{all-swarm} event log. The log is primarily analyzed with {\tt grep} and similar custom utilities. The process is helped by the uniform format of log records: (time, node, flow, event, parameters).
+     
+%\begin{figure}[t]
+%\centering
+%\includegraphics[width=0.2\textwidth]{stats-cell.pdf}
+%\caption{A cell in the spreadsheet gives a summary for a flow.}
+%\label{fig:cell}
+%\end{figure}
+
+As a result, a Manifold user is able to start with a fast qualitative estimation of the swarm and flows, then delve deeper into details as necessary, down to quantitative examination of the log and event-by-event analysis.
+
+\section{Conclusion}
+
+The Manifold testing approach performs massively parallel $O(N^{2})$ case coverage, showering your code with millions and millions of unpredictable state/event combinations.
+The results often lead to a  realization, that your code's performance is never ``perfect'', but probably it is ``good enough'' for the current conditions.
+Despite the fact that Manifold invokes non-trivial computational resources, it still can be used in the routine code-build-test loop of software development.
+We consider Manifold a useful addition to the standard dumb-bell/parking-lot toolset of network protocol testbeds.
+
+
+\bibliographystyle{IEEEtran}
+\bibliography{sources}
+
+\end{document}
diff --git a/src/libswift_udp/doc/p2tp-lancaster.pdf b/src/libswift_udp/doc/p2tp-lancaster.pdf
new file mode 100755 (executable)
index 0000000..4bed32b
Binary files /dev/null and b/src/libswift_udp/doc/p2tp-lancaster.pdf differ
diff --git a/src/libswift_udp/doc/p2tp-uml-er.pdf b/src/libswift_udp/doc/p2tp-uml-er.pdf
new file mode 100644 (file)
index 0000000..4d858ec
Binary files /dev/null and b/src/libswift_udp/doc/p2tp-uml-er.pdf differ
diff --git a/src/libswift_udp/doc/state-diagram.pdf b/src/libswift_udp/doc/state-diagram.pdf
new file mode 100644 (file)
index 0000000..1057574
Binary files /dev/null and b/src/libswift_udp/doc/state-diagram.pdf differ
diff --git a/src/libswift_udp/doc/style.css b/src/libswift_udp/doc/style.css
new file mode 100644 (file)
index 0000000..0cb5e37
--- /dev/null
@@ -0,0 +1,68 @@
+body {
+       background: white no-repeat fixed;
+       color: black;
+}
+
+h1, h2, h3 {
+       font-family: Verdana;
+}
+
+p {
+       text-align: justify;
+}
+
+body > div {
+       width: 60em;
+       margin: auto;
+       margin-top: 64px;
+       margin-bottom: 64px;
+       /*background: #d0e0ff;*/
+       background: rgba(208,224,255,0.9);
+       padding-top: 16px;
+       padding-bottom: 16px;
+}
+
+img#logo {
+       /*display: block;
+    margin-left: auto;
+    margin-right: auto;
+       position: relative;
+       top: -40px;*/
+       position:absolute;
+       top: 4px;
+}
+
+body > div > h1 {
+       text-align:center;
+}
+
+body > div > div {
+       width: 90%;
+       margin: auto;
+}
+
+div#motto {
+       text-align: right;
+       font-style: italic;
+       font-size: larger;
+    margin-bottom: 28pt;
+}
+
+div#abstract {
+    letter-spacing: 0.06em;
+    /*font-size: larger;*/
+    font-style: italic;
+    font-family: Georgia;
+}
+
+div.fold>h2, div.fold>h3, div.fold>h4 {
+       cursor: pointer;
+}
+
+[bullet='open']:before {
+    content: "⊟ ";
+}
+
+[bullet='closed']:before {
+    content: "⊞ ";
+}
diff --git a/src/libswift_udp/doc/swift.css b/src/libswift_udp/doc/swift.css
new file mode 100644 (file)
index 0000000..52f3af8
--- /dev/null
@@ -0,0 +1,121 @@
+* {
+font-family: Georgia, serif;
+}
+
+a {
+text-decoration: none;
+}
+
+img#logo {
+    position:absolute;
+    top: 40px;
+}
+
+a:hover {
+text-decoration: underline;
+}
+
+body {
+background: #A3CDEA;
+margin: 0;
+padding: 0px;
+}
+
+h1, h2, h3, h4 {
+font-family: Trebuchet MS, Arial, sans-serif;
+padding: 0;
+}
+
+h1 {
+color: #000;
+font-size: 200%;
+font-weight: bold;
+margin: 0;
+}
+
+h2 {
+border-top: 1px dotted #aaa;
+font-size: 150%;
+margin: 30px 0 10px 0;
+padding: 20px 0 0 0;
+}
+
+div > h2:first-child {
+border-top: none;
+margin: 0 0 10px 0;
+padding: 0;
+}
+
+h3 {
+font-weight: normal;
+margin: 30px 0 10px 0;
+}
+
+h4 {
+font-size: 90%;
+font-weight: bold;
+margin: 20px 0 10px 0;
+}
+
+
+li {
+font-size: 12px;
+line-height: 1.5em;
+}
+
+p {
+font-size: 12px;
+line-height: 1.5em;
+} 
+
+ul {
+list-style: square;
+margin: 0;
+padding: 0 0 0 15px;
+}
+
+
+
+div#container {
+margin: 0 auto 0 auto;
+width: 700px;
+}
+
+div#header  {
+    font: 32pt Verdana bold;
+    color: white;
+    background: #44a;
+    padding: 5px 30px 15px 0;
+    text-align: center;
+}
+
+div#header img {
+vertical-align: middle;
+}
+
+div#intro {
+background: #fff;
+border-bottom: 1px dotted #aaa;
+padding: 25px 20px 10px 20px;
+}
+
+div#intro p {
+font-size: 16px;
+}
+
+div#content {
+background: #f6f6f6;
+border-bottom: 1px solid black;
+padding: 20px 20px 20px 20px;
+}
+
+div#contact {
+background: #fff;
+padding: 20px 20px 20px 20px;
+}
+
+div#footer {
+background: #444;
+color: #aaa;
+padding: 20px 20px 20px 20px;
+}
diff --git a/src/libswift_udp/doc/wireshark-dissector/AUTHORS b/src/libswift_udp/doc/wireshark-dissector/AUTHORS
new file mode 100644 (file)
index 0000000..e89964a
--- /dev/null
@@ -0,0 +1,2 @@
+Author :
+Andrew Keating <andrewzkeating@gmail.com>
diff --git a/src/libswift_udp/doc/wireshark-dissector/CMakeLists.txt b/src/libswift_udp/doc/wireshark-dissector/CMakeLists.txt
new file mode 100644 (file)
index 0000000..96e2e7e
--- /dev/null
@@ -0,0 +1,66 @@
+# CMakeLists.txt
+#
+# $Id: CMakeLists.txt 31995 2010-02-24 22:32:10Z jmayer $
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+set(DISSECTOR_SRC
+       packet-swift.c
+)
+
+set(PLUGIN_FILES
+       plugin.c
+       ${DISSECTOR_SRC}
+)
+
+set(CLEAN_FILES
+       ${PLUGIN_FILES}
+)
+
+if (WERROR)
+       set_source_files_properties(
+               ${CLEAN_FILES}
+               PROPERTIES
+               COMPILE_FLAGS -Werror
+       )
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+register_dissector_files(plugin.c
+       plugin
+       ${DISSECTOR_SRC}
+)
+
+add_library(swift ${LINK_MODE_MODULE}
+       ${PLUGIN_FILES}
+)
+set_target_properties(swift PROPERTIES PREFIX "")
+set_target_properties(swift PROPERTIES SOVERSION ${CPACK_PACKAGE_VERSION})
+set_target_properties(swift PROPERTIES LINK_FLAGS ${WS_LINK_FLAGS})
+
+target_link_libraries(swift epan)
+
+install(TARGETS swift
+       LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/@CPACK_PACKAGE_NAME@/plugins/${CPACK_PACKAGE_VERSION} NAMELINK_SKIP
+       RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/@CPACK_PACKAGE_NAME@/plugins/${CPACK_PACKAGE_VERSION}
+       ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/@CPACK_PACKAGE_NAME@/plugins/${CPACK_PACKAGE_VERSION}
+)
+
diff --git a/src/libswift_udp/doc/wireshark-dissector/COPYING b/src/libswift_udp/doc/wireshark-dissector/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/libswift_udp/doc/wireshark-dissector/ChangeLog b/src/libswift_udp/doc/wireshark-dissector/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/libswift_udp/doc/wireshark-dissector/Makefile b/src/libswift_udp/doc/wireshark-dissector/Makefile
new file mode 100644 (file)
index 0000000..98871f1
--- /dev/null
@@ -0,0 +1,828 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# plugins/swift/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+# Makefile.am
+# Automake file for swift plugin
+# By Andrew Keating <andrewzkeating@gmail.com>
+# Copyright 2011 Andrew Keating
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+# Makefile.common for Interlink plugin
+#     Contains the stuff from Makefile.am and Makefile.nmake that is
+#     a) common to both files and
+#     b) portable between both files
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+pkgdatadir = $(datadir)/wireshark
+pkgincludedir = $(includedir)/wireshark
+pkglibdir = $(libdir)/wireshark
+pkglibexecdir = $(libexecdir)/wireshark
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = x86_64-unknown-linux-gnu
+host_triplet = x86_64-unknown-linux-gnu
+target_triplet = x86_64-unknown-linux-gnu
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.common \
+       $(srcdir)/Makefile.in AUTHORS COPYING ChangeLog
+subdir = plugins/swift
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal-fallback/glib-2.0.m4 \
+       $(top_srcdir)/aclocal-fallback/gtk-2.0.m4 \
+       $(top_srcdir)/aclocal-fallback/libgcrypt.m4 \
+       $(top_srcdir)/aclocal-fallback/libsmi.m4 \
+       $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(plugindir)"
+LTLIBRARIES = $(plugin_LTLIBRARIES)
+swift_la_DEPENDENCIES =
+am__objects_1 = packet-swift.lo
+am__objects_2 =
+am_swift_la_OBJECTS = plugin.lo $(am__objects_1) $(am__objects_2)
+swift_la_OBJECTS = $(am_swift_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+swift_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(swift_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I. -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo "  CC    " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo "  CCLD  " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo "  GEN   " $@;
+SOURCES = $(swift_la_SOURCES)
+DIST_SOURCES = $(swift_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run aclocal-1.11
+ADNS_LIBS = 
+AMTAR = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run tar
+AM_DEFAULT_VERBOSITY = 1
+AR = ar
+AUTOCONF = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run autoconf
+AUTOHEADER = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run autoheader
+AUTOMAKE = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run automake-1.11
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CC_FOR_BUILD = gcc
+CFLAGS = -DINET6 -D_U_="__attribute__((unused))" -g -O2 -Wall -W -Wextra -Wdeclaration-after-statement -Wendif-labels -Wpointer-arith -Wno-pointer-sign -Warray-bounds -Wcast-align -Wformat-security -I/usr/local/include -pthread -D_REENTRANT -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/directfb -I/usr/include/libpng12   -I/usr/include/pcap
+CORESERVICES_FRAMEWORKS = 
+CPP = gcc -E
+CPPFLAGS =  -I/usr/local/include -I/usr/include/pcap '-DPLUGIN_DIR="$(plugindir)"'
+CXX = g++
+CXXCPP = g++ -E
+CXXDEPMODE = depmode=gcc3
+CXXFLAGS = -g -O2 -pthread -D_REENTRANT -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/directfb -I/usr/include/libpng12  
+CYGPATH_W = echo
+C_ARES_LIBS = 
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DOXYGEN = 
+DSYMUTIL = 
+DUMPBIN = 
+DUMPCAP_GROUP = 
+ECHO_C = 
+ECHO_N = -n
+ECHO_T = 
+EGREP = /bin/grep -E
+ELINKS = 
+ENABLE_STATIC = 
+EXEEXT = 
+FGREP = /bin/grep -F
+FLEX_PATH = 
+FOP = 
+GEOIP_LIBS = 
+GETOPT_LO = 
+GLIB_CFLAGS = -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include  
+GLIB_GENMARSHAL = glib-genmarshal
+GLIB_LIBS = -Wl,--export-dynamic -pthread -lgmodule-2.0 -lrt -lglib-2.0  
+GLIB_MKENUMS = glib-mkenums
+GOBJECT_QUERY = gobject-query
+GREP = /bin/grep
+GTK_CFLAGS = -pthread -D_REENTRANT -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/directfb -I/usr/include/libpng12  
+GTK_LIBS = -pthread -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lcairo -lpango-1.0 -lfreetype -lfontconfig -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lrt -lglib-2.0  
+HAVE_BLESS = no
+HAVE_DOXYGEN = no
+HAVE_DPKG_BUILDPACKAGE = yes
+HAVE_ELINKS = no
+HAVE_FOP = no
+HAVE_HDIUTIL = no
+HAVE_HHC = no
+HAVE_LYNX = no
+HAVE_OSX_PACKAGING = no
+HAVE_PKGMK = no
+HAVE_PKGPROTO = no
+HAVE_PKGTRANS = no
+HAVE_RPM = 
+HAVE_SVR4_PACKAGING = no
+HAVE_XCODEBUILD = no
+HAVE_XMLLINT = yes
+HAVE_XSLTPROC = yes
+HHC = 
+HTML_VIEWER = /usr/bin/xdg-open
+INET_ATON_LO = 
+INET_NTOP_LO = 
+INET_PTON_LO = 
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+KRB5_CONFIG = 
+KRB5_LIBS = 
+LAUNCHSERVICES_FRAMEWORKS = 
+LD = /usr/bin/ld -m elf_x86_64
+LDFLAGS =  -Wl,--as-needed -L/usr/local/lib -L/usr/local/lib -L/usr/local/lib -L/usr/local/lib -L/usr/local/lib
+LDFLAGS_SHAREDLIB = 
+LEX = /usr/bin/flex
+LEXLIB = -lfl
+LEX_OUTPUT_ROOT = lex.yy
+LIBCAP_LIBS = 
+LIBGCRYPT_CFLAGS = 
+LIBGCRYPT_CONFIG = no
+LIBGCRYPT_LIBS = 
+LIBGNUTLS_CFLAGS = 
+LIBGNUTLS_LIBS = 
+LIBOBJS = 
+
+# Libs must be cleared, or else libtool won't create a shared module.
+# If your module needs to be linked against any particular libraries,
+# add them here.
+LIBS = 
+LIBSMI_CFLAGS = 
+LIBSMI_LDFLAGS = 
+LIBSMI_VERSION = 
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIBTOOL_DEPS = ./ltmain.sh
+LIPO = 
+LN_S = ln -s
+LTLIBOBJS = 
+LUA_INCLUDES = 
+LUA_LIBS = 
+LYNX = 
+MAKEINFO = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/missing --run makeinfo
+MKDIR_P = /bin/mkdir -p
+NM = /usr/bin/nm -B
+NMEDIT = 
+NSL_LIBS = 
+OBJDUMP = objdump
+OBJEXT = o
+OTOOL = 
+OTOOL64 = 
+PACKAGE = wireshark
+PACKAGE_BUGREPORT = 
+PACKAGE_NAME = wireshark
+PACKAGE_STRING = wireshark 1.4.7
+PACKAGE_TARNAME = wireshark
+PACKAGE_URL = 
+PACKAGE_VERSION = 1.4.7
+PATH_SEPARATOR = :
+PCAP_CONFIG = 
+PCAP_LIBS = -lpcap
+PCRE_LIBS = 
+PERL = /usr/bin/perl
+PKG_CONFIG = /usr/bin/pkg-config
+PLUGIN_LIBS = 
+POD2HTML = /usr/bin/pod2html
+POD2MAN = /usr/bin/pod2man
+PORTAUDIO_INCLUDES = 
+PORTAUDIO_LIBS = 
+PYTHON = /usr/bin/python
+PY_CFLAGS = 
+PY_LIBS = 
+RANLIB = ranlib
+SED = /bin/sed
+SETCAP = /sbin/setcap
+SET_MAKE = 
+SHELL = /bin/bash
+SOCKET_LIBS = 
+SSL_LIBS = 
+STRERROR_LO = 
+STRIP = strip
+STRNCASECMP_LO = 
+STRPTIME_C = 
+STRPTIME_LO = 
+VERSION = 1.4.7
+XMLLINT = /usr/bin/xmllint
+XSLTPROC = /usr/bin/xsltproc
+YACC = bison -y
+YACCDUMMY = /usr/bin/bison
+YFLAGS = 
+abs_builddir = /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/plugins/swift
+abs_srcdir = /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/plugins/swift
+abs_top_builddir = /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7
+abs_top_srcdir = /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7
+ac_ct_CC = gcc
+ac_ct_CXX = g++
+ac_ct_DUMPBIN = 
+ac_cv_wireshark_have_rpm = no
+ac_ws_python_config = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+am__tar = tar --format=ustar -chf - "$$tardir"
+am__untar = tar -xf -
+bindir = ${exec_prefix}/bin
+build = x86_64-unknown-linux-gnu
+build_alias = 
+build_cpu = x86_64
+build_os = linux-gnu
+build_vendor = unknown
+builddir = .
+capinfos_bin = capinfos$(EXEEXT)
+capinfos_man = capinfos.1
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+dftest_bin = dftest$(EXEEXT)
+dftest_man = dftest.1
+docdir = /root/build/root/share/doc/wireshark
+dumpcap_bin = dumpcap$(EXEEXT)
+dumpcap_man = dumpcap.1
+dvidir = ${docdir}
+editcap_bin = editcap$(EXEEXT)
+editcap_man = editcap.1
+exec_prefix = ${prefix}
+host = x86_64-unknown-linux-gnu
+host_alias = 
+host_cpu = x86_64
+host_os = linux-gnu
+host_vendor = unknown
+htmldir = ${docdir}
+idl2wrs_bin = idl2wrs
+idl2wrs_man = idl2wrs.1
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /home/andrewk/Documents/P2P/dissector/wireshark-1.4.7/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+lt_ECHO = echo
+mandir = ${datarootdir}/man
+mergecap_bin = mergecap$(EXEEXT)
+mergecap_man = mergecap.1
+mkdir_p = /bin/mkdir -p
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+plugindir = ${libdir}/wireshark/plugins/${VERSION}
+prefix = /root/build/root
+program_transform_name = s,x,x,
+psdir = ${docdir}
+pythondir = 
+randpkt_bin = randpkt$(EXEEXT)
+randpkt_man = randpkt.1
+rawshark_bin = rawshark$(EXEEXT)
+rawshark_man = rawshark.1
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target = x86_64-unknown-linux-gnu
+target_alias = 
+target_cpu = x86_64
+target_os = linux-gnu
+target_vendor = unknown
+text2pcap_bin = text2pcap$(EXEEXT)
+text2pcap_man = text2pcap.1
+top_build_prefix = ../../
+top_builddir = ../..
+top_srcdir = ../..
+tshark_bin = tshark$(EXEEXT)
+tshark_man = tshark.1
+wireshark_SUBDIRS = codecs gtk
+wireshark_bin = wireshark$(EXEEXT)
+wireshark_man = wireshark.1
+wiresharkfilter_man = wireshark-filter.4
+INCLUDES = -I$(top_srcdir) -I$(includedir)
+
+# the name of the plugin
+PLUGIN_NAME = swift
+
+# the dissector sources (without any helpers)
+DISSECTOR_SRC = \
+       packet-swift.c
+
+
+# Dissector helpers. They're included in the source files in this
+# directory, but they're not dissectors themselves, i.e. they're not
+# used to generate "plugin.c".
+DISSECTOR_SUPPORT_SRC = 
+#AM_CFLAGS = -Werror
+plugin_LTLIBRARIES = swift.la
+swift_la_SOURCES = \
+       plugin.c \
+       moduleinfo.h \
+       $(DISSECTOR_SRC) \
+       $(DISSECTOR_SUPPORT_SRC) \
+       $(DISSECTOR_INCLUDES)
+
+swift_la_LDFLAGS = -module -avoid-version
+swift_la_LIBADD = 
+
+#
+# Currently plugin.c can be included in the distribution because
+# we always build all protocol dissectors. We used to have to check
+# whether or not to build the snmp dissector. If we again need to
+# variably build something, making plugin.c non-portable, uncomment
+# the dist-hook line below.
+#
+# Oh, yuk.  We don't want to include "plugin.c" in the distribution, as
+# its contents depend on the configuration, and therefore we want it
+# to be built when the first "make" is done; however, Automake insists
+# on putting *all* source into the distribution.
+#
+# We work around this by having a "dist-hook" rule that deletes
+# "plugin.c", so that "dist" won't pick it up.
+#
+#dist-hook:
+#      @rm -f $(distdir)/plugin.c
+CLEANFILES = \
+       swift \
+       *~
+
+MAINTAINERCLEANFILES = \
+       Makefile.in     \
+       plugin.c
+
+EXTRA_DIST = \
+       Makefile.common         \
+       Makefile.nmake          \
+       moduleinfo.nmake        \
+       plugin.rc.in            \
+       CMakeLists.txt
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(srcdir)/Makefile.common $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins/swift/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --gnu plugins/swift/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+       }
+
+uninstall-pluginLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+       done
+
+clean-pluginLTLIBRARIES:
+       -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+swift.la: $(swift_la_OBJECTS) $(swift_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(swift_la_LINK) -rpath $(plugindir) $(swift_la_OBJECTS) $(swift_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+include ./$(DEPDIR)/packet-swift.Plo
+include ./$(DEPDIR)/plugin.Plo
+
+.c.o:
+       $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+       $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+#      $(AM_V_CC) \
+#      source='$<' object='$@' libtool=no \
+#      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+#      $(COMPILE) -c $<
+
+.c.obj:
+       $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+       $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+#      $(AM_V_CC) \
+#      source='$<' object='$@' libtool=no \
+#      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+#      $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+       $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+       $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+#      $(AM_V_CC) \
+#      source='$<' object='$@' libtool=yes \
+#      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+#      $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+       for dir in "$(DESTDIR)$(plugindir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pluginLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+       clean-libtool clean-pluginLTLIBRARIES ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-pluginLTLIBRARIES \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-pluginLTLIBRARIES
+
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+plugin.c: $(DISSECTOR_SRC) $(top_srcdir)/tools/make-dissector-reg \
+    $(top_srcdir)/tools/make-dissector-reg.py
+       @if test -n "$(PYTHON)"; then \
+               echo Making plugin.c with python ; \
+               $(PYTHON) $(top_srcdir)/tools/make-dissector-reg.py $(srcdir) \
+                   plugin $(DISSECTOR_SRC) ; \
+       else \
+               echo Making plugin.c with shell script ; \
+               $(top_srcdir)/tools/make-dissector-reg $(srcdir) \
+                   $(plugin_src) plugin $(DISSECTOR_SRC) ; \
+       fi
+
+checkapi:
+       $(PERL) $(top_srcdir)/tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC) $(DISSECTOR_INCLUDES)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libswift_udp/doc/wireshark-dissector/Makefile.am b/src/libswift_udp/doc/wireshark-dissector/Makefile.am
new file mode 100644 (file)
index 0000000..3c9c9da
--- /dev/null
@@ -0,0 +1,131 @@
+# Makefile.am
+# Automake file for swift plugin
+# By Andrew Keating <andrewzkeating@gmail.com>
+# Copyright 2011 Andrew Keating
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+INCLUDES = -I$(top_srcdir) -I$(includedir)
+
+include Makefile.common
+
+if HAVE_WARNINGS_AS_ERRORS
+AM_CFLAGS = -Werror
+endif
+
+plugindir = @plugindir@
+
+plugin_LTLIBRARIES = swift.la
+swift_la_SOURCES = \
+       plugin.c \
+       moduleinfo.h \
+       $(DISSECTOR_SRC) \
+       $(DISSECTOR_SUPPORT_SRC) \
+       $(DISSECTOR_INCLUDES)
+swift_la_LDFLAGS = -module -avoid-version
+swift_la_LIBADD = @PLUGIN_LIBS@
+
+# Libs must be cleared, or else libtool won't create a shared module.
+# If your module needs to be linked against any particular libraries,
+# add them here.
+LIBS =
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+plugin.c: $(DISSECTOR_SRC) $(top_srcdir)/tools/make-dissector-reg \
+    $(top_srcdir)/tools/make-dissector-reg.py
+       @if test -n "$(PYTHON)"; then \
+               echo Making plugin.c with python ; \
+               $(PYTHON) $(top_srcdir)/tools/make-dissector-reg.py $(srcdir) \
+                   plugin $(DISSECTOR_SRC) ; \
+       else \
+               echo Making plugin.c with shell script ; \
+               $(top_srcdir)/tools/make-dissector-reg $(srcdir) \
+                   $(plugin_src) plugin $(DISSECTOR_SRC) ; \
+       fi
+
+#
+# Currently plugin.c can be included in the distribution because
+# we always build all protocol dissectors. We used to have to check
+# whether or not to build the snmp dissector. If we again need to
+# variably build something, making plugin.c non-portable, uncomment
+# the dist-hook line below.
+#
+# Oh, yuk.  We don't want to include "plugin.c" in the distribution, as
+# its contents depend on the configuration, and therefore we want it
+# to be built when the first "make" is done; however, Automake insists
+# on putting *all* source into the distribution.
+#
+# We work around this by having a "dist-hook" rule that deletes
+# "plugin.c", so that "dist" won't pick it up.
+#
+#dist-hook:
+#      @rm -f $(distdir)/plugin.c
+
+CLEANFILES = \
+       swift \
+       *~
+
+MAINTAINERCLEANFILES = \
+       Makefile.in     \
+       plugin.c
+
+EXTRA_DIST = \
+       Makefile.common         \
+       Makefile.nmake          \
+       moduleinfo.nmake        \
+       plugin.rc.in            \
+       CMakeLists.txt
+
+checkapi:
+       $(PERL) $(top_srcdir)/tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC) $(DISSECTOR_INCLUDES)
diff --git a/src/libswift_udp/doc/wireshark-dissector/Makefile.common b/src/libswift_udp/doc/wireshark-dissector/Makefile.common
new file mode 100644 (file)
index 0000000..705997d
--- /dev/null
@@ -0,0 +1,36 @@
+# Makefile.common for Interlink plugin
+#     Contains the stuff from Makefile.am and Makefile.nmake that is
+#     a) common to both files and
+#     b) portable between both files
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+# the name of the plugin
+PLUGIN_NAME = swift
+
+# the dissector sources (without any helpers)
+DISSECTOR_SRC = \
+       packet-swift.c
+
+# Dissector helpers. They're included in the source files in this
+# directory, but they're not dissectors themselves, i.e. they're not
+# used to generate "plugin.c".
+DISSECTOR_SUPPORT_SRC =
diff --git a/src/libswift_udp/doc/wireshark-dissector/Makefile.in b/src/libswift_udp/doc/wireshark-dissector/Makefile.in
new file mode 100644 (file)
index 0000000..3d584bb
--- /dev/null
@@ -0,0 +1,828 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am
+# Automake file for swift plugin
+# By Andrew Keating <andrewzkeating@gmail.com>
+# Copyright 2011 Andrew Keating
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+# Makefile.common for Interlink plugin
+#     Contains the stuff from Makefile.am and Makefile.nmake that is
+#     a) common to both files and
+#     b) portable between both files
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.common \
+       $(srcdir)/Makefile.in AUTHORS COPYING ChangeLog
+subdir = plugins/swift
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal-fallback/glib-2.0.m4 \
+       $(top_srcdir)/aclocal-fallback/gtk-2.0.m4 \
+       $(top_srcdir)/aclocal-fallback/libgcrypt.m4 \
+       $(top_srcdir)/aclocal-fallback/libsmi.m4 \
+       $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(plugindir)"
+LTLIBRARIES = $(plugin_LTLIBRARIES)
+swift_la_DEPENDENCIES =
+am__objects_1 = packet-swift.lo
+am__objects_2 =
+am_swift_la_OBJECTS = plugin.lo $(am__objects_1) $(am__objects_2)
+swift_la_OBJECTS = $(am_swift_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+swift_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(swift_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo "  CC    " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo "  CCLD  " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo "  GEN   " $@;
+SOURCES = $(swift_la_SOURCES)
+DIST_SOURCES = $(swift_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ADNS_LIBS = @ADNS_LIBS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CORESERVICES_FRAMEWORKS = @CORESERVICES_FRAMEWORKS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_ARES_LIBS = @C_ARES_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DUMPCAP_GROUP = @DUMPCAP_GROUP@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ELINKS = @ELINKS@
+ENABLE_STATIC = @ENABLE_STATIC@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FLEX_PATH = @FLEX_PATH@
+FOP = @FOP@
+GEOIP_LIBS = @GEOIP_LIBS@
+GETOPT_LO = @GETOPT_LO@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+HAVE_BLESS = @HAVE_BLESS@
+HAVE_DOXYGEN = @HAVE_DOXYGEN@
+HAVE_DPKG_BUILDPACKAGE = @HAVE_DPKG_BUILDPACKAGE@
+HAVE_ELINKS = @HAVE_ELINKS@
+HAVE_FOP = @HAVE_FOP@
+HAVE_HDIUTIL = @HAVE_HDIUTIL@
+HAVE_HHC = @HAVE_HHC@
+HAVE_LYNX = @HAVE_LYNX@
+HAVE_OSX_PACKAGING = @HAVE_OSX_PACKAGING@
+HAVE_PKGMK = @HAVE_PKGMK@
+HAVE_PKGPROTO = @HAVE_PKGPROTO@
+HAVE_PKGTRANS = @HAVE_PKGTRANS@
+HAVE_RPM = @HAVE_RPM@
+HAVE_SVR4_PACKAGING = @HAVE_SVR4_PACKAGING@
+HAVE_XCODEBUILD = @HAVE_XCODEBUILD@
+HAVE_XMLLINT = @HAVE_XMLLINT@
+HAVE_XSLTPROC = @HAVE_XSLTPROC@
+HHC = @HHC@
+HTML_VIEWER = @HTML_VIEWER@
+INET_ATON_LO = @INET_ATON_LO@
+INET_NTOP_LO = @INET_NTOP_LO@
+INET_PTON_LO = @INET_PTON_LO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LAUNCHSERVICES_FRAMEWORKS = @LAUNCHSERVICES_FRAMEWORKS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_SHAREDLIB = @LDFLAGS_SHAREDLIB@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
+LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
+LIBOBJS = @LIBOBJS@
+
+# Libs must be cleared, or else libtool won't create a shared module.
+# If your module needs to be linked against any particular libraries,
+# add them here.
+LIBS = 
+LIBSMI_CFLAGS = @LIBSMI_CFLAGS@
+LIBSMI_LDFLAGS = @LIBSMI_LDFLAGS@
+LIBSMI_VERSION = @LIBSMI_VERSION@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LUA_INCLUDES = @LUA_INCLUDES@
+LUA_LIBS = @LUA_LIBS@
+LYNX = @LYNX@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSL_LIBS = @NSL_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PCAP_CONFIG = @PCAP_CONFIG@
+PCAP_LIBS = @PCAP_LIBS@
+PCRE_LIBS = @PCRE_LIBS@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PLUGIN_LIBS = @PLUGIN_LIBS@
+POD2HTML = @POD2HTML@
+POD2MAN = @POD2MAN@
+PORTAUDIO_INCLUDES = @PORTAUDIO_INCLUDES@
+PORTAUDIO_LIBS = @PORTAUDIO_LIBS@
+PYTHON = @PYTHON@
+PY_CFLAGS = @PY_CFLAGS@
+PY_LIBS = @PY_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SETCAP = @SETCAP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSL_LIBS = @SSL_LIBS@
+STRERROR_LO = @STRERROR_LO@
+STRIP = @STRIP@
+STRNCASECMP_LO = @STRNCASECMP_LO@
+STRPTIME_C = @STRPTIME_C@
+STRPTIME_LO = @STRPTIME_LO@
+VERSION = @VERSION@
+XMLLINT = @XMLLINT@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YACCDUMMY = @YACCDUMMY@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_wireshark_have_rpm = @ac_cv_wireshark_have_rpm@
+ac_ws_python_config = @ac_ws_python_config@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+capinfos_bin = @capinfos_bin@
+capinfos_man = @capinfos_man@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dftest_bin = @dftest_bin@
+dftest_man = @dftest_man@
+docdir = @docdir@
+dumpcap_bin = @dumpcap_bin@
+dumpcap_man = @dumpcap_man@
+dvidir = @dvidir@
+editcap_bin = @editcap_bin@
+editcap_man = @editcap_man@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+idl2wrs_bin = @idl2wrs_bin@
+idl2wrs_man = @idl2wrs_man@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mergecap_bin = @mergecap_bin@
+mergecap_man = @mergecap_man@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pythondir = @pythondir@
+randpkt_bin = @randpkt_bin@
+randpkt_man = @randpkt_man@
+rawshark_bin = @rawshark_bin@
+rawshark_man = @rawshark_man@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+text2pcap_bin = @text2pcap_bin@
+text2pcap_man = @text2pcap_man@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+tshark_bin = @tshark_bin@
+tshark_man = @tshark_man@
+wireshark_SUBDIRS = @wireshark_SUBDIRS@
+wireshark_bin = @wireshark_bin@
+wireshark_man = @wireshark_man@
+wiresharkfilter_man = @wiresharkfilter_man@
+INCLUDES = -I$(top_srcdir) -I$(includedir)
+
+# the name of the plugin
+PLUGIN_NAME = swift
+
+# the dissector sources (without any helpers)
+DISSECTOR_SRC = \
+       packet-swift.c
+
+
+# Dissector helpers. They're included in the source files in this
+# directory, but they're not dissectors themselves, i.e. they're not
+# used to generate "plugin.c".
+DISSECTOR_SUPPORT_SRC = 
+@HAVE_WARNINGS_AS_ERRORS_TRUE@AM_CFLAGS = -Werror
+plugin_LTLIBRARIES = swift.la
+swift_la_SOURCES = \
+       plugin.c \
+       moduleinfo.h \
+       $(DISSECTOR_SRC) \
+       $(DISSECTOR_SUPPORT_SRC) \
+       $(DISSECTOR_INCLUDES)
+
+swift_la_LDFLAGS = -module -avoid-version
+swift_la_LIBADD = @PLUGIN_LIBS@
+
+#
+# Currently plugin.c can be included in the distribution because
+# we always build all protocol dissectors. We used to have to check
+# whether or not to build the snmp dissector. If we again need to
+# variably build something, making plugin.c non-portable, uncomment
+# the dist-hook line below.
+#
+# Oh, yuk.  We don't want to include "plugin.c" in the distribution, as
+# its contents depend on the configuration, and therefore we want it
+# to be built when the first "make" is done; however, Automake insists
+# on putting *all* source into the distribution.
+#
+# We work around this by having a "dist-hook" rule that deletes
+# "plugin.c", so that "dist" won't pick it up.
+#
+#dist-hook:
+#      @rm -f $(distdir)/plugin.c
+CLEANFILES = \
+       swift \
+       *~
+
+MAINTAINERCLEANFILES = \
+       Makefile.in     \
+       plugin.c
+
+EXTRA_DIST = \
+       Makefile.common         \
+       Makefile.nmake          \
+       moduleinfo.nmake        \
+       plugin.rc.in            \
+       CMakeLists.txt
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(srcdir)/Makefile.common $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins/swift/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --gnu plugins/swift/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+       }
+
+uninstall-pluginLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+       done
+
+clean-pluginLTLIBRARIES:
+       -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+swift.la: $(swift_la_OBJECTS) $(swift_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(swift_la_LINK) -rpath $(plugindir) $(swift_la_OBJECTS) $(swift_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet-swift.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+       for dir in "$(DESTDIR)$(plugindir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pluginLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+       clean-libtool clean-pluginLTLIBRARIES ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-pluginLTLIBRARIES \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-pluginLTLIBRARIES
+
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+plugin.c: $(DISSECTOR_SRC) $(top_srcdir)/tools/make-dissector-reg \
+    $(top_srcdir)/tools/make-dissector-reg.py
+       @if test -n "$(PYTHON)"; then \
+               echo Making plugin.c with python ; \
+               $(PYTHON) $(top_srcdir)/tools/make-dissector-reg.py $(srcdir) \
+                   plugin $(DISSECTOR_SRC) ; \
+       else \
+               echo Making plugin.c with shell script ; \
+               $(top_srcdir)/tools/make-dissector-reg $(srcdir) \
+                   $(plugin_src) plugin $(DISSECTOR_SRC) ; \
+       fi
+
+checkapi:
+       $(PERL) $(top_srcdir)/tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC) $(DISSECTOR_INCLUDES)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libswift_udp/doc/wireshark-dissector/Makefile.nmake b/src/libswift_udp/doc/wireshark-dissector/Makefile.nmake
new file mode 100644 (file)
index 0000000..992c9d4
--- /dev/null
@@ -0,0 +1,103 @@
+# Makefile.nmake
+# nmake file for Wireshark plugin
+#
+# $Id: Makefile.nmake 29883 2009-09-13 19:48:22Z morriss $
+#
+
+include ..\..\config.nmake
+include moduleinfo.nmake
+
+include Makefile.common
+
+CFLAGS=/WX /DHAVE_CONFIG_H /I../.. $(GLIB_CFLAGS) \
+       /I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
+
+.c.obj::
+       $(CC) $(CFLAGS) -Fd.\ -c $<
+
+LDFLAGS = $(PLUGIN_LDFLAGS)
+
+!IFDEF ENABLE_LIBWIRESHARK
+LINK_PLUGIN_WITH=..\..\epan\libwireshark.lib
+CFLAGS=/D_NEED_VAR_IMPORT_ $(CFLAGS)
+
+DISSECTOR_OBJECTS = $(DISSECTOR_SRC:.c=.obj)
+
+DISSECTOR_SUPPORT_OBJECTS = $(DISSECTOR_SUPPORT_SRC:.c=.obj)
+
+OBJECTS = $(DISSECTOR_OBJECTS) $(DISSECTOR_SUPPORT_OBJECTS) plugin.obj
+
+RESOURCE=$(PLUGIN_NAME).res
+
+all: $(PLUGIN_NAME).dll
+
+$(PLUGIN_NAME).rc : moduleinfo.nmake
+       sed -e s/@PLUGIN_NAME@/$(PLUGIN_NAME)/ \
+       -e s/@RC_MODULE_VERSION@/$(RC_MODULE_VERSION)/ \
+       -e s/@RC_VERSION@/$(RC_VERSION)/ \
+       -e s/@MODULE_VERSION@/$(MODULE_VERSION)/ \
+       -e s/@PACKAGE@/$(PACKAGE)/ \
+       -e s/@VERSION@/$(VERSION)/ \
+       -e s/@MSVC_VARIANT@/$(MSVC_VARIANT)/ \
+       < plugin.rc.in > $@
+
+$(PLUGIN_NAME).dll $(PLUGIN_NAME).exp $(PLUGIN_NAME).lib : $(OBJECTS) $(LINK_PLUGIN_WITH) $(RESOURCE)
+       link -dll /out:$(PLUGIN_NAME).dll $(LDFLAGS) $(OBJECTS) $(LINK_PLUGIN_WITH) \
+       $(GLIB_LIBS) $(RESOURCE)
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+!IFDEF PYTHON
+plugin.c: $(DISSECTOR_SRC) moduleinfo.h ../../tools/make-dissector-reg.py
+       @echo Making plugin.c (using python)
+       @$(PYTHON) "../../tools/make-dissector-reg.py" . plugin $(DISSECTOR_SRC)
+!ELSE
+plugin.c: $(DISSECTOR_SRC) moduleinfo.h ../../tools/make-dissector-reg
+       @echo Making plugin.c (using sh)
+       @$(SH) ../../tools/make-dissector-reg . plugin $(DISSECTOR_SRC)
+!ENDIF
+
+!ENDIF
+
+clean:
+       rm -f $(OBJECTS) $(RESOURCE) plugin.c *.pdb \
+           $(PLUGIN_NAME).dll $(PLUGIN_NAME).dll.manifest $(PLUGIN_NAME).lib \
+           $(PLUGIN_NAME).exp $(PLUGIN_NAME).rc
+
+distclean: clean
+
+maintainer-clean: distclean
+
+checkapi:
+       $(PERL) ../../tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC) $(DISSECTOR_INCLUDES)
diff --git a/src/libswift_udp/doc/wireshark-dissector/README b/src/libswift_udp/doc/wireshark-dissector/README
new file mode 100644 (file)
index 0000000..78f5700
--- /dev/null
@@ -0,0 +1,10 @@
+This is a protocol dissector for swift: the multiparty transport protocol
+(http://libswift.org)
+
+For instructions on how to include this plugin in a Wireshark build, see
+Wireshark's /doc/README.developer
+
+If you are new to Wireshark protocol dissectors, take a look at 
+http://www.wireshark.org/docs/wsdg_html_chunked/ChDissectAdd.html
+
+Author: Andrew Keating <andrewzkeating@gmail.com>
diff --git a/src/libswift_udp/doc/wireshark-dissector/moduleinfo.h b/src/libswift_udp/doc/wireshark-dissector/moduleinfo.h
new file mode 100644 (file)
index 0000000..b5f70c7
--- /dev/null
@@ -0,0 +1,17 @@
+/* Included *after* config.h, in order to re-define these macros */
+
+#ifdef PACKAGE
+#undef PACKAGE
+#endif
+
+/* Name of package */
+#define PACKAGE "swift"
+
+
+#ifdef VERSION
+#undef VERSION
+#endif
+
+/* Version number of package */
+#define VERSION "0.0.1"
+
diff --git a/src/libswift_udp/doc/wireshark-dissector/moduleinfo.nmake b/src/libswift_udp/doc/wireshark-dissector/moduleinfo.nmake
new file mode 100644 (file)
index 0000000..077a9e4
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+# The name
+PACKAGE=swift
+
+# The version
+MODULE_VERSION_MAJOR=0
+MODULE_VERSION_MINOR=0
+MODULE_VERSION_MICRO=1
+MODULE_VERSION_EXTRA=0
+
+#
+# The RC_VERSION should be comma-separated, not dot-separated, 
+# as per Graham Bloice's message in
+#
+#      http://www.ethereal.com/lists/ethereal-dev/200303/msg00283.html
+#
+# "The RC_VERSION variable in config.nmake should be comma separated. 
+# This allows the resources to be built correctly and the version
+# number to be correctly displayed in the explorer properties dialog
+# for the executables, and XP's tooltip, rather than 0.0.0.0."
+#
+
+MODULE_VERSION=$(MODULE_VERSION_MAJOR).$(MODULE_VERSION_MINOR).$(MODULE_VERSION_MICRO).$(MODULE_VERSION_EXTRA)
+RC_MODULE_VERSION=$(MODULE_VERSION_MAJOR),$(MODULE_VERSION_MINOR),$(MODULE_VERSION_MICRO),$(MODULE_VERSION_EXTRA)
+
diff --git a/src/libswift_udp/doc/wireshark-dissector/packet-swift.c b/src/libswift_udp/doc/wireshark-dissector/packet-swift.c
new file mode 100644 (file)
index 0000000..7190061
--- /dev/null
@@ -0,0 +1,383 @@
+/* packet-swift.c
+ * Routines for swift protocol packet disassembly
+ * By Andrew Keating <andrewzkeating@gmail.com>
+ * Copyright 2011 Andrew Keating
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <epan/packet.h>
+
+static int proto_swift = -1;
+
+/* Global fields */
+static int hf_swift_receiving_channel = -1;
+static int hf_swift_message_type = -1;
+
+/* 00 Handshake fields */
+static int hf_swift_handshake_channel = -1;
+
+/* 01 Data fields */
+static int hf_swift_data_bin_id = -1;
+static int hf_swift_data_payload = -1;
+
+/* 02 Ack fields */
+static int hf_swift_ack_bin_id = -1;
+static int hf_swift_ack_timestamp = -1;
+
+/* 03 Have fields */
+static int hf_swift_have_bin_id = -1;
+
+/* 04 Hash fields */
+static int hf_swift_hash_bin_id = -1;
+static int hf_swift_hash_value = -1;
+
+/* 05 PEX+ fields */
+static int hf_swift_pexplus_ip = -1;
+static int hf_swift_pexplus_port = -1;
+
+/* 06 PEX- fields */
+static int hf_swift_pexminus_ip = -1;
+static int hf_swift_pexminus_port = -1;
+
+/* 07 Signed hash fields */
+static int hf_swift_signed_hash_bin_id = -1;
+static int hf_swift_signed_hash_value = -1;
+static int hf_swift_signed_hash_signature = -1;
+
+/* 08 Hint fields */
+static int hf_swift_hint_bin_id = -1;
+
+static gint ett_swift = -1;
+
+static void dissect_swift(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static gboolean dissect_swift_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+
+static const value_string message_type_names[] = {
+    { 0, "Handshake" },
+    { 1, "Data" },
+    { 2, "Ack" },
+    { 3, "Have" },
+    { 4, "Hash" },
+    { 5, "PEX+" },
+    { 6, "PEX-" },
+    { 7, "Signed Hash" },
+    { 8, "Hint" },
+    { 9, "SWIFT_MSGTYPE_RCVD" },
+    { 10, "SWIFT_MESSAGE_COUNT" },
+    { 0, NULL}
+};
+
+
+void
+proto_register_swift(void)
+{
+    static hf_register_info hf[] = {
+       /* Global */
+    { &hf_swift_receiving_channel,
+               { "Receiving Channel", "swift.receiving.channel",
+               FT_UINT32, BASE_HEX,
+               NULL, 0x0,
+               NULL, HFILL }
+     },
+       { &hf_swift_message_type,
+           { "Message Type", "swift.message.type",
+        FT_UINT8, BASE_DEC,
+        VALS(message_type_names), 0x0,
+        NULL, HFILL }
+       },
+
+       /* 00 Handshake */
+       { &hf_swift_handshake_channel,
+           { "Handshake Channel", "swift.handshake.channel",
+           FT_UINT32, BASE_HEX,
+        NULL, 0x0,
+        NULL, HFILL }
+       },
+
+       /* 01 Data */
+       { &hf_swift_data_bin_id,
+           { "Data Bin ID", "swift.data.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_data_payload,
+           { "Data Payload", "swift.data.payload",
+           FT_BYTES, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 02 Ack */
+       { &hf_swift_ack_bin_id,
+           { "Ack Bin ID", "swift.ack.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_ack_timestamp,
+           { "Timestamp", "swift.ack.timestamp",
+           FT_UINT64, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 03 Have */
+       { &hf_swift_have_bin_id,
+           { "Have Bin ID", "swift.have.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 04 Hash */ 
+       { &hf_swift_hash_bin_id,
+           { "Hash Bin ID", "swift.hash.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_hash_value,
+           { "Hash Value", "swift.hash.value",
+           FT_BYTES, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 05 PEX+ */
+       { &hf_swift_pexplus_ip,
+           { "PEX+ IP Address", "swift.pex_plus.ip",
+           FT_IPv4, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_pexplus_port,
+           { "PEX+ Port", "swift.pex_plus.port",
+           FT_UINT16, BASE_DEC,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 06 PEX- */
+       { &hf_swift_pexminus_ip,
+           { "PEX- IP Address", "swift.pex_minus.ip",
+           FT_IPv4, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_pexminus_port,
+           { "PEX- Port", "swift.pex_minus.port",
+           FT_UINT16, BASE_DEC,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 07 Signed Hash */
+       { &hf_swift_signed_hash_bin_id,
+           { "Signed Hash Bin ID", "swift.signed_hash.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_signed_hash_value,
+           { "Signed Hash Value", "swift.signed_hash.value",
+           FT_BYTES, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+       { &hf_swift_signed_hash_signature,
+           { "Signed Hash Signature", "swift.signed_hash.signature",
+           FT_BYTES, BASE_NONE,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+
+       /* 08 Hint */
+       { &hf_swift_hint_bin_id,
+           { "Hint Bin ID", "swift.hint.bin_id",
+           FT_UINT32, BASE_HEX,
+           NULL, 0x0,
+           NULL, HFILL }
+       },
+   };
+
+    /* Setup protocol subtree array */
+    static gint *ett[] = {
+        &ett_swift
+    };
+
+    proto_swift = proto_register_protocol (
+        "swift: the multiparty transport protocol", /* name       */
+        "swift",      /* short name */
+        "swift"       /* abbrev     */
+        );
+
+    proto_register_field_array(proto_swift, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+       register_dissector("swift", dissect_swift, proto_swift);
+}
+
+void
+proto_reg_handoff_swift(void)
+{
+       dissector_handle_t swift_handle;
+       swift_handle = find_dissector("swift");
+
+       /* Allow "Decode As" with any UDP packet. */
+       dissector_add_handle("udp.port", swift_handle);
+
+       /* Add our heuristic packet finder. */
+       heur_dissector_add("udp", dissect_swift_heur, proto_swift);
+}
+
+/* This heuristic is somewhat ambiguous, but for research purposes, it should be fine */
+static gboolean
+dissect_swift_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       guint message_length;
+       message_length = tvb_length(tvb);
+       /* If the fifth byte isn't one of the supported packet types, it's not swift (except keep-alives) */
+       if(message_length != 4) {       
+               guint8 message_type;
+               message_type = tvb_get_guint8(tvb, 4);
+               if(message_type > 10) {
+                       return FALSE;
+               }
+       }
+
+       dissect_swift(tvb, pinfo, tree);
+       return TRUE;
+}
+
+static void
+dissect_swift(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       gint offset = 0;
+    col_set_str(pinfo->cinfo, COL_PROTOCOL, "swift");
+    /* Clear out stuff in the info column */
+    col_clear(pinfo->cinfo,COL_INFO);
+
+    if (tree) { /* we are being asked for details */
+        proto_item *ti;
+               ti = proto_tree_add_item(tree, proto_swift, tvb, 0, -1, FALSE);
+
+               proto_tree *swift_tree;
+               swift_tree = proto_item_add_subtree(ti, ett_swift);
+
+               /* All messages start with the receiving channel, so we can pull it out here */
+               proto_tree_add_item(swift_tree, hf_swift_receiving_channel, tvb, offset, 4, FALSE); offset += 4;
+
+               /* Loop until there is nothing left to read in the packet */
+               while(tvb_bytes_exist(tvb, offset, 1)) {
+                       guint8 message_type;
+                       guint dat_len;
+                   message_type = tvb_get_guint8(tvb, offset);
+                       proto_tree_add_item(swift_tree, hf_swift_message_type, tvb, offset, 1, FALSE); 
+                       offset += 1;
+
+                       /* Add message type to the info column */
+                       if(offset > 5) {
+                               col_append_fstr(pinfo->cinfo, COL_INFO, ", ");
+                       }
+                       col_append_fstr(pinfo->cinfo, COL_INFO, "%s",
+                               val_to_str(message_type, message_type_names, "Unknown (0x%02x)"));
+
+                       /* Add it to the dissection window as well */
+                       proto_item_append_text(ti, ", %s",
+                               val_to_str(message_type, message_type_names, "Unknown (0x%02x)"));
+
+                       switch(message_type) {
+                               case 0: /* Handshake */
+                                       proto_tree_add_item(swift_tree, hf_swift_handshake_channel, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       break;
+                               case 1: /* Data */
+                                       proto_tree_add_item(swift_tree, hf_swift_data_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       /* We assume that the data field comprises the rest of this packet */
+                                       dat_len = tvb_length(tvb) - offset;
+                                       proto_tree_add_item(swift_tree, hf_swift_data_payload, tvb, offset, dat_len, FALSE); 
+                                       offset += dat_len;
+                                       break;
+                               case 2: /* Ack */
+                                       proto_tree_add_item(swift_tree, hf_swift_ack_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       proto_tree_add_item(swift_tree, hf_swift_ack_timestamp, tvb, offset, 8, FALSE); 
+                                       offset += 8;
+                                       break;
+                               case 3: /* Have */
+                                       proto_tree_add_item(swift_tree, hf_swift_have_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       break;
+                               case 4: /* Hash */
+                                       proto_tree_add_item(swift_tree, hf_swift_hash_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       proto_tree_add_item(swift_tree, hf_swift_hash_value, tvb, offset, 20, FALSE); 
+                                       offset += 20;
+                                       break;
+                               case 5: /* PEX+ */
+                                       proto_tree_add_item(swift_tree, hf_swift_pexplus_ip, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       proto_tree_add_item(swift_tree, hf_swift_pexplus_port, tvb, offset, 2, FALSE); 
+                                       offset += 2;
+                                       break;
+                               case 6: /* PEX- */
+                                       proto_tree_add_item(swift_tree, hf_swift_pexminus_ip, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       proto_tree_add_item(swift_tree, hf_swift_pexminus_port, tvb, offset, 2, FALSE); 
+                                       offset += 2;
+                                       break;
+                               case 7: /* Signed Hash */
+                                       proto_tree_add_item(swift_tree, hf_swift_signed_hash_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       proto_tree_add_item(swift_tree, hf_swift_signed_hash_value, tvb, offset, 20, FALSE); 
+                                       offset += 20;
+                                       /* It is not entirely clear what size the public key will be, so we allow any size
+                                          For this to work, we must assume there aren't any more messages in the packet */
+                                       dat_len = tvb_length(tvb) - offset;
+                                       proto_tree_add_item(swift_tree, hf_swift_signed_hash_signature, tvb, offset, dat_len, FALSE); 
+                                       offset += dat_len;
+                                       break;
+                               case 8: /* Hint */
+                                       proto_tree_add_item(swift_tree, hf_swift_hint_bin_id, tvb, offset, 4, FALSE); 
+                                       offset += 4;
+                                       break;
+                               case 9: /* SWIFT_MSGTYPE_RCVD */
+                                       break;
+                               case 10: /* SWIFT_MESSAGE_COUNT */
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               /* If the offset is still 4 here, the message is a keep-alive */
+               if(offset == 4) {
+                       col_append_fstr(pinfo->cinfo, COL_INFO, "Keep-Alive");
+                       proto_item_append_text(ti, ", Keep-Alive");
+               }
+       }
+}
+
diff --git a/src/libswift_udp/doc/wireshark-dissector/plugin.c b/src/libswift_udp/doc/wireshark-dissector/plugin.c
new file mode 100644 (file)
index 0000000..fea12f6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Do not modify this file.
+ *
+ * It is created automatically by Makefile or Makefile.nmake.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gmodule.h>
+
+#include "moduleinfo.h"
+
+#ifndef ENABLE_STATIC
+G_MODULE_EXPORT const gchar version[] = VERSION;
+
+/* Start the functions we need for the plugin stuff */
+
+G_MODULE_EXPORT void
+plugin_register (void)
+{
+  {extern void proto_register_swift (void); proto_register_swift ();}
+}
+
+G_MODULE_EXPORT void
+plugin_reg_handoff(void)
+{
+  {extern void proto_reg_handoff_swift (void); proto_reg_handoff_swift ();}
+}
+#endif
diff --git a/src/libswift_udp/doc/wireshark-dissector/plugin.rc.in b/src/libswift_udp/doc/wireshark-dissector/plugin.rc.in
new file mode 100644 (file)
index 0000000..568dc07
--- /dev/null
@@ -0,0 +1,34 @@
+#include "winver.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @RC_MODULE_VERSION@
+ PRODUCTVERSION @RC_VERSION@
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "The Wireshark developer community, http://www.wireshark.org/\0"
+            VALUE "FileDescription", "@PACKAGE@ dissector\0"
+            VALUE "FileVersion", "@MODULE_VERSION@\0"
+            VALUE "InternalName", "@PACKAGE@ @MODULE_VERSION@\0"
+            VALUE "LegalCopyright", "Copyright Â© 1998 Gerald Combs <gerald@wireshark.org>, Gilbert Ramirez <gram@alumni.rice.edu> and others\0"
+            VALUE "OriginalFilename", "@PLUGIN_NAME@.dll\0"
+            VALUE "ProductName", "Wireshark\0"
+            VALUE "ProductVersion", "@VERSION@\0"
+            VALUE "Comments", "Build with @MSVC_VARIANT@\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/src/libswift_udp/ext/seq_picker.cpp b/src/libswift_udp/ext/seq_picker.cpp
new file mode 100644 (file)
index 0000000..2be5206
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  seq_picker.cpp
+ *  swift
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#include <cassert>
+
+using namespace swift;
+
+
+/** Picks pieces nearly sequentialy; some local randomization (twisting)
+    is introduced to prevent synchronization among multiple channels. */
+class SeqPiecePicker : public PiecePicker {
+
+    binmap_t        ack_hint_out_;
+    tbqueue         hint_out_;
+    FileTransfer*   transfer_;
+    uint64_t        twist_;
+    bin_t           range_;
+
+public:
+
+    SeqPiecePicker (FileTransfer* file_to_pick_from) : ack_hint_out_(),
+           transfer_(file_to_pick_from), twist_(0), range_(bin_t::ALL) {
+        binmap_t::copy(ack_hint_out_, file().ack_out());
+    }
+    virtual ~SeqPiecePicker() {}
+
+    HashTree& file() {
+        return transfer_->file();
+    }
+
+    virtual void Randomize (uint64_t twist) {
+        twist_ = twist;
+    }
+
+    virtual void LimitRange (bin_t range) {
+        range_ = range;
+    }
+
+    virtual bin_t Pick (binmap_t& offer, uint64_t max_width, tint expires) {
+        while (hint_out_.size() && hint_out_.front().time<NOW-TINT_SEC*3/2) { // FIXME sec
+            binmap_t::copy(ack_hint_out_, file().ack_out(), hint_out_.front().bin);
+            hint_out_.pop_front();
+        }
+        if (!file().size()) {
+            return bin_t(0,0); // whoever sends it first
+        // Arno, 2011-06-28: Partial fix by Victor. exact_size_known() missing
+        //} else if (!file().exact_size_known()) {
+        //    return bin64_t(0,(file().size()>>10)-1); // dirty
+        }
+    retry:      // bite me
+        twist_ &= (file().peak(0).toUInt()) & ((1<<6)-1);
+
+        bin_t hint = binmap_t::find_complement(ack_hint_out_, offer, twist_);
+        if (hint.is_none()) {
+            return hint; // TODO: end-game mode
+        }
+
+        if (!file().ack_out().is_empty(hint)) { // unhinted/late data
+            binmap_t::copy(ack_hint_out_, file().ack_out(), hint);
+            goto retry;
+        }
+        while (hint.base_length()>max_width)
+            hint = hint.left();
+        assert(ack_hint_out_.is_empty(hint));
+        ack_hint_out_.set(hint);
+        hint_out_.push_back(tintbin(NOW,hint));
+        return hint;
+    }
+
+    void updatePlaybackPos(int size = 1)
+    {
+       return;
+    }
+
+};
diff --git a/src/libswift_udp/ext/simple_selector.cpp b/src/libswift_udp/ext/simple_selector.cpp
new file mode 100644 (file)
index 0000000..6431c47
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  simple_selector.cpp
+ *  swift
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include <queue>
+#include "swift.h"
+
+using namespace swift;
+
+class SimpleSelector : public PeerSelector {
+    typedef std::pair<Address,Sha1Hash> memo_t;
+    typedef std::deque<memo_t>  peer_queue_t;
+    peer_queue_t    peers;
+public:
+    SimpleSelector () {
+    }
+    void AddPeer (const Address& addr, const Sha1Hash& root) {
+        peers.push_front(memo_t(addr,root)); //,root.fingerprint() !!!
+    }
+    Address GetPeer (const Sha1Hash& for_root) {
+        //uint32_t fp = for_root.fingerprint();
+        for(peer_queue_t::iterator i=peers.begin(); i!=peers.end(); i++)
+            if (i->second==for_root) {
+                i->second = Sha1Hash::ZERO; // horror TODO rewrite
+                sockaddr_in ret = i->first;
+                while (peers.begin()->second==Sha1Hash::ZERO)
+                    peers.pop_front();
+                return ret;
+            }
+        return Address();
+    }
+};
+
diff --git a/src/libswift_udp/ext/vod_picker.cpp b/src/libswift_udp/ext/vod_picker.cpp
new file mode 100644 (file)
index 0000000..690b4dc
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ *  vod_picker.cpp
+ *  swift
+ *
+ *  Created by Riccardo Petrocco.
+ *  Copyright 2009-2012 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#include <cassert>
+
+using namespace swift;
+
+#define HIGHPRIORITYWINDOW 45000;      // initial high priority window in bin unit
+#define MIDPRIORITYWINDOW 4;           // proportion of the mid priority window compared to the high pri. one
+
+/** Picks pieces in VoD fashion. The stream is divided in three priority
+ *  sets based on the current playback position. In the high priority set
+ *  bins are selected in order, while on the medium and low priority sets
+ *  in a rarest fist fashion */
+class VodPiecePicker : public PiecePicker {
+
+    binmap_t        ack_hint_out_;
+    tbqueue         hint_out_;
+    FileTransfer*   transfer_;
+    Availability*      avail_;
+    uint64_t        twist_;
+    bin_t           range_;
+    int                                playback_pos_;          // playback position in KB
+    int                                high_pri_window_;
+
+public:
+
+    VodPiecePicker (FileTransfer* file_to_pick_from) : ack_hint_out_(),
+           transfer_(file_to_pick_from), twist_(0), range_(bin_t::ALL)
+    {
+       avail_ = &(transfer_->availability());
+        binmap_t::copy(ack_hint_out_, file().ack_out());
+        playback_pos_ = -1;
+        high_pri_window_ = HIGHPRIORITYWINDOW;
+    }
+
+    virtual ~VodPiecePicker() {}
+
+    HashTree& file() {
+        return transfer_->file();
+    }
+
+    virtual void Randomize (uint64_t twist) {
+       // Arno, 2012-03-21: After consulting with Riccardo, disable
+       // twisting for VOD PP. Randomization of peers over the bitmap
+       // is already guaranteed by the ack_hint_out_ (prevents double requesting)
+       // and rarest first.
+    }
+
+    virtual void LimitRange (bin_t range) {
+        range_ = range;
+    }
+
+
+    bin_t getTopBin(bin_t bin, uint64_t start, uint64_t size)
+    {
+       while (bin.parent().base_length() <= size && bin.parent().base_left() >= bin_t(start))
+               {
+                       bin.to_parent();
+               }
+       return bin;
+    }
+
+
+    bin_t pickUrgent (binmap_t& offer, uint64_t max_width, uint64_t size) {
+
+       bin_t curr = bin_t((playback_pos_+1)<<1); // the base bin will be indexed by the double of the value (bin(4) == bin(0,2))
+       bin_t hint = bin_t::NONE;
+       uint64_t examined = 0;
+               binmap_t binmap;
+
+       // report the first bin we find
+       while (hint.is_none() && examined < size)
+       {
+               curr = getTopBin(curr, (playback_pos_+1)<<1, size-examined);
+               if (!ack_hint_out_.is_filled(curr))
+               {
+                       binmap.fill(offer);
+                               binmap_t::copy(binmap, ack_hint_out_, curr);
+                               hint = binmap_t::find_complement(binmap, offer, twist_);
+                               binmap.clear();
+               }
+               examined += curr.base_length();
+               curr = bin_t(0, curr.base_right().layer_offset()+1 );
+       }
+
+       if (!hint.is_none())
+               while (hint.base_length()>max_width && !hint.is_base()) // Arno,2012-01-17: stop!
+               hint.to_left();
+
+       return hint;
+    }
+
+
+    bin_t pickRarest (binmap_t& offer, uint64_t max_width, uint64_t start, uint64_t size) {
+
+       //fprintf(stderr,"%s #1 Picker -> choosing from mid/low priority \n",tintstr());
+       bin_t curr = bin_t(start<<1);
+               bin_t hint = bin_t::NONE;
+               uint64_t examined = 0;
+               //uint64_t size = end-start;
+               bin_t rarest_hint = bin_t::NONE;
+               // TODO remove..
+               binmap_t binmap;
+
+               // TODO.. this is the dummy version... put some logic in deciding what to DL
+               while (examined < size)
+               {
+                       curr = getTopBin(curr, start<<1, size-examined);
+
+                       if (!ack_hint_out_.is_filled(curr))
+                       {
+                               // remove
+                               //binmap_t::copy(binmap, offer);
+                               //binmap.reset(curr);
+
+                               binmap.fill(offer);
+                               binmap_t::copy(binmap, ack_hint_out_, curr);
+                               //hint = binmap_t::find_complement(ack_hint_out_, offer, curr, twist_);
+                               hint = binmap_t::find_complement(binmap, offer, twist_);
+                               binmap.clear();
+
+                               if (!hint.is_none())
+                               {
+                                       if (avail_->size())
+                                       {
+                                               rarest_hint = avail_->get(rarest_hint) < avail_->get(hint) ? rarest_hint : hint;
+                                       }
+                                       else
+                                       {
+                                               examined = size;
+                                               rarest_hint = hint;
+                                       }
+                               }
+                       }
+
+                       examined += curr.base_length();
+                       curr = bin_t(0, curr.base_right().layer_offset()+1 );
+
+               }
+
+               if (!rarest_hint.is_none())
+               {
+                       if (avail_->size())
+                               rarest_hint = avail_->getRarest(rarest_hint, max_width);
+                       else
+                               while (rarest_hint.base_length()>max_width && !rarest_hint.is_base()) // Arno,2012-01-17: stop!
+                                       rarest_hint.to_left();
+               }
+
+               return rarest_hint;
+    }
+
+
+    virtual bin_t Pick (binmap_t& offer, uint64_t max_width, tint expires)
+    {
+       bin_t hint;
+       bool retry;
+        char tmp[32];
+        char set = 'X';        // TODO remove set var, only used for debug
+
+       // TODO check... the seconds should depend on previous speed of the peer
+        while (hint_out_.size() && hint_out_.front().time<NOW-TINT_SEC*3/2) { // FIXME sec
+            binmap_t::copy(ack_hint_out_, file().ack_out(), hint_out_.front().bin);
+            hint_out_.pop_front();
+        }
+
+        // get the first piece to estimate the size, whoever sends it first
+        if (!file().size()) {
+
+            return bin_t(0,0);
+        }
+
+        do {
+               uint64_t max_size = file().size_in_chunks() - playback_pos_ - 1;
+               max_size = high_pri_window_ < max_size ? high_pri_window_ : max_size;
+
+                       // check the high priority window for data we r missing
+                       hint = pickUrgent(offer, max_width, max_size);
+
+                       // check the mid priority window
+                       uint64_t start = (1 + playback_pos_) + HIGHPRIORITYWINDOW;      // start in KB
+                       if (hint.is_none() && start < file().size_in_chunks())
+                       {
+                               int mid = MIDPRIORITYWINDOW;
+                               int size = mid * HIGHPRIORITYWINDOW;                                            // size of window in KB
+                               // check boundaries
+                               max_size = file().size_in_chunks() - start;
+                               max_size = size < max_size ? size : max_size;
+
+                               hint = pickRarest(offer, max_width, start, max_size);
+
+                               //check low priority
+                               start += max_size;
+                               if (hint.is_none() && start < file().size_in_chunks())
+                               {
+                                       size = file().size_in_chunks() - start;
+                                       hint = pickRarest(offer, max_width, start, size);
+                                       set = 'L';
+                               }
+                               else
+                                       set = 'M';
+                       }
+                       else
+                               set = 'H';
+
+                       // unhinted/late data
+                       if (!file().ack_out().is_empty(hint)) {
+                               binmap_t::copy(ack_hint_out_, file().ack_out(), hint);
+                               retry = true;
+                       }
+                       else
+                               retry = false;
+
+        } while (retry);
+
+
+        if (hint.is_none()) {
+               // TODO, control if we want: check for missing hints (before playback pos.)
+               hint = binmap_t::find_complement(ack_hint_out_, offer, twist_);
+               // TODO: end-game mode
+               if (hint.is_none())
+                       return hint;
+               else
+                               while (hint.base_length()>max_width && !hint.is_base()) // Arno,2012-01-17: stop!
+                                       hint.to_left();
+
+
+        }
+
+        assert(ack_hint_out_.is_empty(hint));
+        ack_hint_out_.set(hint);
+        hint_out_.push_back(tintbin(NOW,hint));
+
+
+        // TODO clean ... printing percentage of completeness for the priority sets
+        //status();
+
+        //fprintf(stderr,"%s #1 Picker -> picked %s\t from %c set\t max width %lu \n",tintstr(), hint.str(tmp), set, max_width );
+        //if (avail_->size())
+        return hint;
+    }
+
+
+    void updatePlaybackPos(int size = 1)
+    {
+       assert(size>-1);
+       if (size< file().size_in_chunks() - playback_pos_ - 2)
+               playback_pos_ += size;
+    }
+
+
+    void status()
+       {
+               int t = 0;
+               int x = HIGHPRIORITYWINDOW;
+               int y = MIDPRIORITYWINDOW;
+               int i = playback_pos_ + 1;
+               int end_high = (x+playback_pos_)<<1;
+               int end_mid = ((x*y)+x+playback_pos_)<<1;
+               int total = 0;
+
+
+               while (i<=end_high)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total = t;
+               t = t*100/((x<<1)-1);
+               fprintf(stderr, "low %u, ", t);
+               t = 0;
+               while (i<=end_mid)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total += t;
+               t = t*100/((x*y)<<1);
+               fprintf(stderr, "mid %u, ", t);
+               t = 0;
+               while (i<=file().size_in_chunks()<<1)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total += t;
+               t = t*100/((file().size_in_chunks()-(x*y+playback_pos_))<<1);
+               fprintf(stderr, "low %u  -> in total: %i\t pp: %i\n", t, total, playback_pos_);
+       }
+
+};
diff --git a/src/libswift_udp/getopt.c b/src/libswift_udp/getopt.c
new file mode 100644 (file)
index 0000000..4cfe746
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *     The Regents of the University of California.  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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c   8.3 (Berkeley) 4/27/95";
+#endif /* LIBC_SCCS and not lint
+#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: src/lib/libc/stdlib/getopt.c,v 1.6 2002/03/29 22:43:42 markm Exp $");
+
+#include "namespace.h"*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+/*#include "un-namespace.h"*/
+
+/*#include "libc_private.h"*/
+
+int    opterr = 1,             /* if error message should be printed */
+       optind = 1,             /* index into parent argv vector */
+       optopt,                 /* character checked for validity */
+       optreset;               /* reset getopt */
+char   *optarg;                /* argument associated with option */
+
+#define        BADCH   (int)'?'
+#define        BADARG  (int)':'
+#define        EMSG    ""
+
+/*
+ * getopt --
+ *     Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+       int nargc;
+       char * const *nargv;
+       const char *ostr;
+{
+       static char *place = EMSG;              /* option letter processing */
+       char *oli;                              /* option letter list index */
+
+       if (optreset || !*place) {              /* update scanning pointer */
+               optreset = 0;
+               if (optind >= nargc || *(place = nargv[optind]) != '-') {
+                       place = EMSG;
+                       return (-1);
+               }
+               if (place[1] && *++place == '-') {      /* found "--" */
+                       ++optind;
+                       place = EMSG;
+                       return (-1);
+               }
+       }                                       /* option letter okay? */
+       if ((optopt = (int)*place++) == (int)':' ||
+           !(oli = strchr(ostr, optopt))) {
+               /*
+                * if the user didn't specify '-' as an option,
+                * assume it means -1.
+                */
+               if (optopt == (int)'-')
+                       return (-1);
+               if (!*place)
+                       ++optind;
+               if (opterr && *ostr != ':' && optopt != BADCH)
+                       (void)fprintf(stderr, "%s: illegal option -- %c\n",
+                           "progname", optopt);
+               return (BADCH);
+       }
+       if (*++oli != ':') {                    /* don't need argument */
+               optarg = NULL;
+               if (!*place)
+                       ++optind;
+       }
+       else {                                  /* need an argument */
+               if (*place)                     /* no white space */
+                       optarg = place;
+               else if (nargc <= ++optind) {   /* no arg */
+                       place = EMSG;
+                       if (*ostr == ':')
+                               return (BADARG);
+                       if (opterr)
+                               (void)fprintf(stderr,
+                                   "%s: option requires an argument -- %c\n",
+                                   "progname", optopt);
+                       return (BADCH);
+               }
+               else                            /* white space */
+                       optarg = nargv[optind];
+               place = EMSG;
+               ++optind;
+       }
+       return (optopt);                        /* dump back option letter */
+}
diff --git a/src/libswift_udp/getopt_long.c b/src/libswift_udp/getopt_long.c
new file mode 100644 (file)
index 0000000..1dbf7d1
--- /dev/null
@@ -0,0 +1,548 @@
+/*     $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $      */
+/*     $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.2 2002/10/16 22:18:42 alfred Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "getopt_win.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+
+/* Windows needs warnx().  We change the definition though:
+ *  1. (another) global is defined, opterrmsg, which holds the error message
+ *  2. errors are always printed out on stderr w/o the program name
+ * Note that opterrmsg always gets set no matter what opterr is set to.  The
+ * error message will not be printed if opterr is 0 as usual.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+GETOPT_API extern char opterrmsg[128];
+char opterrmsg[128]; /* last error message is stored here */
+
+static void warnx(int print_error, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       if (fmt != NULL)
+               _vsnprintf(opterrmsg, 128, fmt, ap);
+       else
+               opterrmsg[0]='\0';
+       va_end(ap);
+       if (print_error) {
+               fprintf(stderr, opterrmsg);
+               fprintf(stderr, "\n");
+       }
+}
+#else
+#include <err.h>
+#endif /*_WIN32*/
+
+/* not part of the original file */
+#ifndef _DIAGASSERT
+#define _DIAGASSERT(X)
+#endif
+
+#if HAVE_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND
+#define REPLACE_GETOPT
+#endif
+
+#ifdef REPLACE_GETOPT
+#ifdef __weak_alias
+__weak_alias(getopt,_getopt)
+#endif
+int    opterr = 1;             /* if error message should be printed */
+int    optind = 1;             /* index into parent argv vector */
+int    optopt = '?';           /* character checked for validity */
+int    optreset;               /* reset getopt */
+char    *optarg;               /* argument associated with option */
+#elif HAVE_CONFIG_H && !HAVE_DECL_OPTRESET
+static int optreset;
+#endif
+
+#ifdef __weak_alias
+__weak_alias(getopt_long,_getopt_long)
+#endif
+
+#if !HAVE_GETOPT_LONG
+#define IGNORE_FIRST   (*options == '-' || *options == '+')
+#define PRINT_ERROR    ((opterr) && ((*options != ':') \
+                                     || (IGNORE_FIRST && options[1] != ':')))
+#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
+#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#define        BADCH   (int)'?'
+#define        BADARG          ((IGNORE_FIRST && options[1] == ':') \
+                        || (*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define        EMSG    ""
+
+static int getopt_internal(int, char * const *, const char *);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(a, b)
+       int a;
+       int b;
+{
+       int c;
+
+       c = a % b;
+       while (c != 0) {
+               a = b;
+               b = c;
+               c = a % b;
+       }
+          
+       return b;
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(panonopt_start, panonopt_end, opt_end, nargv)
+       int panonopt_start;
+       int panonopt_end;
+       int opt_end;
+       char * const *nargv;
+{
+       int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+       char *swap;
+
+       _DIAGASSERT(nargv != NULL);
+
+       /*
+        * compute lengths of blocks and number and size of cycles
+        */
+       nnonopts = panonopt_end - panonopt_start;
+       nopts = opt_end - panonopt_end;
+       ncycle = gcd(nnonopts, nopts);
+       cyclelen = (opt_end - panonopt_start) / ncycle;
+
+       for (i = 0; i < ncycle; i++) {
+               cstart = panonopt_end+i;
+               pos = cstart;
+               for (j = 0; j < cyclelen; j++) {
+                       if (pos >= panonopt_end)
+                               pos -= nnonopts;
+                       else
+                               pos += nopts;
+                       swap = nargv[pos];
+                       /* LINTED const cast */
+                       ((char **) nargv)[pos] = nargv[cstart];
+                       /* LINTED const cast */
+                       ((char **)nargv)[cstart] = swap;
+               }
+       }
+}
+
+/*
+ * getopt_internal --
+ *     Parse argc/argv argument vector.  Called by user level routines.
+ *  Returns -2 if -- is found (can be long option or end of options marker).
+ */
+static int
+getopt_internal(nargc, nargv, options)
+       int nargc;
+       char * const *nargv;
+       const char *options;
+{
+       char *oli;                              /* option letter list index */
+       int optchar;
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+
+       optarg = NULL;
+
+       /*
+        * XXX Some programs (like rsyncd) expect to be able to
+        * XXX re-initialize optind to 0 and have getopt_long(3)
+        * XXX properly function again.  Work around this braindamage.
+        */
+       if (optind == 0)
+               optind = 1;
+
+       if (optreset)
+               nonopt_start = nonopt_end = -1;
+start:
+       if (optreset || !*place) {              /* update scanning pointer */
+               optreset = 0;
+               if (optind >= nargc) {          /* end of argument vector */
+                       place = EMSG;
+                       if (nonopt_end != -1) {
+                               /* do permutation, if we have to */
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, nargv);
+                               optind -= nonopt_end - nonopt_start;
+                       }
+                       else if (nonopt_start != -1) {
+                               /*
+                                * If we skipped non-options, set optind
+                                * to the first of them.
+                                */
+                               optind = nonopt_start;
+                       }
+                       nonopt_start = nonopt_end = -1;
+                       return -1;
+               }
+               if ((*(place = nargv[optind]) != '-')
+                   || (place[1] == '\0')) {    /* found non-option */
+                       place = EMSG;
+                       if (IN_ORDER) {
+                               /*
+                                * GNU extension: 
+                                * return non-option as argument to option 1
+                                */
+                               optarg = nargv[optind++];
+                               return INORDER;
+                       }
+                       if (!PERMUTE) {
+                               /*
+                                * if no permutation wanted, stop parsing
+                                * at first non-option
+                                */
+                               return -1;
+                       }
+                       /* do permutation */
+                       if (nonopt_start == -1)
+                               nonopt_start = optind;
+                       else if (nonopt_end != -1) {
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, nargv);
+                               nonopt_start = optind -
+                                   (nonopt_end - nonopt_start);
+                               nonopt_end = -1;
+                       }
+                       optind++;
+                       /* process next argument */
+                       goto start;
+               }
+               if (nonopt_start != -1 && nonopt_end == -1)
+                       nonopt_end = optind;
+               if (place[1] && *++place == '-') {      /* found "--" */
+                       place++;
+                       return -2;
+               }
+       }
+       if ((optchar = (int)*place++) == (int)':' ||
+           (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+               /* option letter unknown or ':' */
+               if (!*place)
+                       ++optind;
+#ifndef _WIN32
+               if (PRINT_ERROR)
+                       warnx(illoptchar, optchar);
+#else
+                       warnx(PRINT_ERROR, illoptchar, optchar);
+#endif
+               optopt = optchar;
+               return BADCH;
+       }
+       if (optchar == 'W' && oli[1] == ';') {          /* -W long-option */
+               /* XXX: what if no long options provided (called by getopt)? */
+               if (*place) 
+                       return -2;
+
+               if (++optind >= nargc) {        /* no arg */
+                       place = EMSG;
+#ifndef _WIN32
+                       if (PRINT_ERROR)
+                               warnx(recargchar, optchar);
+#else
+                               warnx(PRINT_ERROR, recargchar, optchar);
+#endif
+                       optopt = optchar;
+                       return BADARG;
+               } else                          /* white space */
+                       place = nargv[optind];
+               /*
+                * Handle -W arg the same as --arg (which causes getopt to
+                * stop parsing).
+                */
+               return -2;
+       }
+       if (*++oli != ':') {                    /* doesn't take argument */
+               if (!*place)
+                       ++optind;
+       } else {                                /* takes (optional) argument */
+               optarg = NULL;
+               if (*place)                     /* no white space */
+                       optarg = place;
+               /* XXX: disable test for :: if PC? (GNU doesn't) */
+               else if (oli[1] != ':') {       /* arg not optional */
+                       if (++optind >= nargc) {        /* no arg */
+                               place = EMSG;
+#ifndef _WIN32
+                               if (PRINT_ERROR)
+                                       warnx(recargchar, optchar);
+#else
+                                       warnx(PRINT_ERROR, recargchar, optchar);
+#endif
+                               optopt = optchar;
+                               return BADARG;
+                       } else
+                               optarg = nargv[optind];
+               }
+               place = EMSG;
+               ++optind;
+       }
+       /* dump back option letter */
+       return optchar;
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ *     Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the real getopt]
+ */
+int
+getopt(nargc, nargv, options)
+       int nargc;
+       char * const *nargv;
+       const char *options;
+{
+       int retval;
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+
+       if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+               ++optind;
+               /*
+                * We found an option (--), so if we skipped non-options,
+                * we have to permute.
+                */
+               if (nonopt_end != -1) {
+                       permute_args(nonopt_start, nonopt_end, optind,
+                                      nargv);
+                       optind -= nonopt_end - nonopt_start;
+               }
+               nonopt_start = nonopt_end = -1;
+               retval = -1;
+       }
+       return retval;
+}
+#endif
+
+/*
+ * getopt_long --
+ *     Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, idx)
+       int nargc;
+       char * const *nargv;
+       const char *options;
+       const struct option *long_options;
+       int *idx;
+{
+       int retval;
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+       _DIAGASSERT(long_options != NULL);
+       /* idx may be NULL */
+
+       if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+               char *current_argv, *has_equal;
+               size_t current_argv_len;
+               int i, match;
+
+               current_argv = place;
+               match = -1;
+
+               optind++;
+               place = EMSG;
+
+               if (*current_argv == '\0') {            /* found "--" */
+                       /*
+                        * We found an option (--), so if we skipped
+                        * non-options, we have to permute.
+                        */
+                       if (nonopt_end != -1) {
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, nargv);
+                               optind -= nonopt_end - nonopt_start;
+                       }
+                       nonopt_start = nonopt_end = -1;
+                       return -1;
+               }
+               if ((has_equal = strchr(current_argv, '=')) != NULL) {
+                       /* argument found (--option=arg) */
+                       current_argv_len = has_equal - current_argv;
+                       has_equal++;
+               } else
+                       current_argv_len = strlen(current_argv);
+           
+               for (i = 0; long_options[i].name; i++) {
+                       /* find matching long option */
+                       if (strncmp(current_argv, long_options[i].name,
+                           current_argv_len))
+                               continue;
+
+                       if (strlen(long_options[i].name) ==
+                           (unsigned)current_argv_len) {
+                               /* exact match */
+                               match = i;
+                               break;
+                       }
+                       if (match == -1)                /* partial match */
+                               match = i;
+                       else {
+                               /* ambiguous abbreviation */
+#ifndef _WIN32
+                               if (PRINT_ERROR)
+                                       warnx(ambig, (int)current_argv_len,
+                                            current_argv);
+#else
+                                       warnx(PRINT_ERROR, ambig, (int)current_argv_len,
+                                            current_argv);
+#endif
+                               optopt = 0;
+                               return BADCH;
+                       }
+               }
+               if (match != -1) {                      /* option found */
+                       if (long_options[match].has_arg == no_argument
+                           && has_equal) {
+#ifndef _WIN32
+                               if (PRINT_ERROR)
+                                       warnx(noarg, (int)current_argv_len,
+                                            current_argv);
+#else
+                                       warnx(PRINT_ERROR, noarg, (int)current_argv_len,
+                                            current_argv);
+#endif
+                               /*
+                                * XXX: GNU sets optopt to val regardless of
+                                * flag
+                                */
+                               if (long_options[match].flag == NULL)
+                                       optopt = long_options[match].val;
+                               else
+                                       optopt = 0;
+                               return BADARG;
+                       }
+                       if (long_options[match].has_arg == required_argument ||
+                           long_options[match].has_arg == optional_argument) {
+                               if (has_equal)
+                                       optarg = has_equal;
+                               else if (long_options[match].has_arg ==
+                                   required_argument) {
+                                       /*
+                                        * optional argument doesn't use
+                                        * next nargv
+                                        */
+                                       optarg = nargv[optind++];
+                               }
+                       }
+                       if ((long_options[match].has_arg == required_argument)
+                           && (optarg == NULL)) {
+                               /*
+                                * Missing argument; leading ':'
+                                * indicates no error should be generated
+                                */
+#ifndef _WIN32
+                               if (PRINT_ERROR)
+                                       warnx(recargstring, current_argv);
+#else
+                                       warnx(PRINT_ERROR, recargstring, current_argv);
+#endif
+                               /*
+                                * XXX: GNU sets optopt to val regardless
+                                * of flag
+                                */
+                               if (long_options[match].flag == NULL)
+                                       optopt = long_options[match].val;
+                               else
+                                       optopt = 0;
+                               --optind;
+                               return BADARG;
+                       }
+               } else {                        /* unknown option */
+#ifndef _WIN32
+                       if (PRINT_ERROR)
+                               warnx(illoptstring, current_argv);
+#else
+                               warnx(PRINT_ERROR, illoptstring, current_argv);
+#endif
+                       optopt = 0;
+                       return BADCH;
+               }
+               if (long_options[match].flag) {
+                       *long_options[match].flag = long_options[match].val;
+                       retval = 0;
+               } else 
+                       retval = long_options[match].val;
+               if (idx)
+                       *idx = match;
+       }
+       return retval;
+}
+#endif /* !GETOPT_LONG */
diff --git a/src/libswift_udp/getopt_win.h b/src/libswift_udp/getopt_win.h
new file mode 100644 (file)
index 0000000..d95d6cf
--- /dev/null
@@ -0,0 +1,110 @@
+/*      $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $    */
+/*      $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#ifdef _WIN32
+/* from <sys/cdefs.h> */
+# ifdef  __cplusplus
+#  define __BEGIN_DECLS  extern "C" {
+#  define __END_DECLS    }
+# else
+#  define __BEGIN_DECLS
+#  define __END_DECLS
+# endif
+# define __P(args)      args
+#endif
+
+/*#ifndef _WIN32
+#include <sys/cdefs.h>
+#include <unistd.h>
+#endif*/
+
+#ifdef _WIN32
+# if !defined(GETOPT_API)
+#  define GETOPT_API __declspec(dllimport)
+# endif
+#endif
+
+/*
+ * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions
+ */
+#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE)
+#define no_argument        0
+#define required_argument  1
+#define optional_argument  2
+
+struct option {
+        /* name of long option */
+        const char *name;
+        /*
+         * one of no_argument, required_argument, and optional_argument:
+         * whether option takes an argument
+         */
+        int has_arg;
+        /* if not NULL, set *flag to val when option found */
+        int *flag;
+        /* if flag not NULL, value to set *flag to; else return value */
+        int val;
+};
+
+__BEGIN_DECLS
+GETOPT_API int getopt_long __P((int, char * const *, const char *,
+    const struct option *, int *));
+__END_DECLS
+#endif
+
+#ifdef _WIN32
+/* These are global getopt variables */
+__BEGIN_DECLS
+
+GETOPT_API extern int   opterr,   /* if error message should be printed */
+                        optind,   /* index into parent argv vector */
+                        optopt,   /* character checked for validity */
+                        optreset; /* reset getopt */
+GETOPT_API extern char* optarg;   /* argument associated with option */
+
+/* Original getopt */
+GETOPT_API int getopt __P((int, char * const *, const char *));
+
+__END_DECLS
+#endif
+#endif /* !_GETOPT_H_ */
diff --git a/src/libswift_udp/hashtree.cpp b/src/libswift_udp/hashtree.cpp
new file mode 100644 (file)
index 0000000..17785bf
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ *  hashtree.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include "hashtree.h"
+#include "bin_utils.h"
+//#include <openssl/sha.h>
+#include "sha1.h"
+#include <cassert>
+#include <cstring>
+#include <cstdlib>
+#include <fcntl.h>
+#include "compat.h"
+#include "swift.h"
+
+#include <iostream>
+
+#ifdef _WIN32
+#define OPENFLAGS         O_RDWR|O_CREAT|_O_BINARY
+#else
+#define OPENFLAGS         O_RDWR|O_CREAT
+#endif
+
+
+using namespace swift;
+
+const Sha1Hash Sha1Hash::ZERO = Sha1Hash();
+
+void SHA1 (const void *data, size_t length, unsigned char *hash) {
+    blk_SHA_CTX ctx;
+    blk_SHA1_Init(&ctx);
+    blk_SHA1_Update(&ctx, data, length);
+    blk_SHA1_Final(hash, &ctx);
+}
+
+Sha1Hash::Sha1Hash(const Sha1Hash& left, const Sha1Hash& right) {
+    blk_SHA_CTX ctx;
+    blk_SHA1_Init(&ctx);
+    blk_SHA1_Update(&ctx, left.bits,SIZE);
+    blk_SHA1_Update(&ctx, right.bits,SIZE);
+    blk_SHA1_Final(bits, &ctx);
+}
+
+Sha1Hash::Sha1Hash(const char* data, size_t length) {
+    if (length==-1)
+        length = strlen(data);
+    SHA1((unsigned char*)data,length,bits);
+}
+
+Sha1Hash::Sha1Hash(const uint8_t* data, size_t length) {
+    SHA1(data,length,bits);
+}
+
+Sha1Hash::Sha1Hash(bool hex, const char* hash) {
+    if (hex) {
+        int val;
+        for(int i=0; i<SIZE; i++) {
+            if (sscanf(hash+i*2, "%2x", &val)!=1) {
+                memset(bits,0,20);
+                return;
+            }
+            bits[i] = val;
+        }
+        assert(this->hex()==std::string(hash));
+    } else
+        memcpy(bits,hash,SIZE);
+}
+
+std::string    Sha1Hash::hex() const {
+    char hex[HASHSZ*2+1];
+    for(int i=0; i<HASHSZ; i++)
+        sprintf(hex+i*2, "%02x", (int)(unsigned char)bits[i]);
+    return std::string(hex,HASHSZ*2);
+}
+
+
+
+/**     H a s h   t r e e       */
+
+
+HashTree::HashTree (const char* filename, const Sha1Hash& root_hash, uint32_t chunk_size, const char* hash_filename, bool force_check_diskvshash, bool check_netwvshash, const char* binmap_filename) :
+root_hash_(root_hash), hashes_(NULL), peak_count_(0), fd_(0), hash_fd_(0),
+filename_(filename), size_(0), sizec_(0), complete_(0), completec_(0),
+chunk_size_(chunk_size), check_netwvshash_(check_netwvshash)
+{
+    fd_ = open(filename,OPENFLAGS,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (fd_<0) {
+        fd_ = 0;
+        print_error("cannot open the file");
+        return;
+    }
+       std::string hfn;
+       if (!hash_filename){
+               hfn.assign(filename);
+               hfn.append(".mhash");
+               hash_filename = hfn.c_str();
+       }
+
+       std::string bfn;
+       if (!binmap_filename){
+               bfn.assign(filename);
+               bfn.append(".mbinmap");
+               binmap_filename = bfn.c_str();
+       }
+
+       // Arno: if user doesn't want to check hashes but no .mhash, check hashes anyway
+       bool actually_force_check_diskvshash = force_check_diskvshash;
+    bool mhash_exists=true;
+#ifdef WIN32
+    struct _stat buf;
+#else
+    struct stat buf;
+#endif
+    int res = stat( hash_filename, &buf );
+    if( res < 0 && errno == ENOENT)
+       mhash_exists = false;
+    if (!mhash_exists && !force_check_diskvshash)
+       actually_force_check_diskvshash = true;
+
+
+    // Arno: if the remainder of the hashtree state is on disk we can
+    // hashcheck very quickly
+    bool binmap_exists=true;
+    res = stat( binmap_filename, &buf );
+    if( res < 0 && errno == ENOENT)
+       binmap_exists = false;
+
+    //fprintf(stderr,"hashtree: hashchecking want %s do %s binmap-on-disk %s\n", (force_check_diskvshash ? "yes" : "no"), (actually_force_check_diskvshash? "yes" : "no"), (binmap_exists? "yes" : "no") );
+
+    hash_fd_ = open(hfn.c_str(),OPENFLAGS,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (hash_fd_<0) {
+        hash_fd_ = 0;
+        print_error("cannot open hash file");
+        return;
+    }
+
+    // Arno: if user wants to or no .mhash, and if root hash unknown (new file) and no checkpoint, (re)calc root hash
+    if (file_size(fd_) && ((actually_force_check_diskvshash) || (root_hash_==Sha1Hash::ZERO && !binmap_exists) || (!mhash_exists)) ) {
+       // fresh submit, hash it
+       dprintf("%s hashtree full compute\n",tintstr());
+        assert(file_size(fd_));
+        Submit();
+    } else if (mhash_exists && binmap_exists && file_size(hash_fd_)) {
+       // Arno: recreate hash tree without rereading content
+       dprintf("%s hashtree read from checkpoint\n",tintstr());
+       FILE *fp = fopen(binmap_filename,"rb");
+       if (!fp) {
+                print_error("hashtree: cannot open .mbinmap file");
+                return;
+       }
+       if (deserialize(fp) < 0) {
+               // Try to rebuild hashtree data
+               Submit();
+       }
+    } else {
+       // Arno: no data on disk, or mhash on disk, but no binmap. In latter
+       // case recreate binmap by reading content again. Historic optimization
+       // of Submit.
+       dprintf("%s hashtree empty or partial recompute\n",tintstr());
+        RecoverProgress();
+    }
+}
+
+
+HashTree::HashTree(bool dummy, const char* binmap_filename) :
+root_hash_(Sha1Hash::ZERO), hashes_(NULL), peak_count_(0), fd_(0), hash_fd_(0),
+filename_(""), size_(0), sizec_(0), complete_(0), completec_(0),
+chunk_size_(0), check_netwvshash_(false)
+{
+       FILE *fp = fopen(binmap_filename,"rb");
+       if (!fp) {
+                return;
+       }
+       if (partial_deserialize(fp) < 0) {
+       }
+       fclose(fp);
+}
+
+
+// Reads complete file and constructs hash tree
+void            HashTree::Submit () {
+    size_ = file_size(fd_);
+    sizec_ = (size_ + chunk_size_-1) / chunk_size_;
+
+    //fprintf(stderr,"hashtree: submit: cs %i\n", chunk_size_);
+
+    peak_count_ = gen_peaks(sizec_,peaks_);
+    int hashes_size = Sha1Hash::SIZE*sizec_*2;
+    dprintf("%s hashtree submit resizing hash file\n",tintstr() );
+    file_resize(hash_fd_,hashes_size);
+    hashes_ = (Sha1Hash*) memory_map(hash_fd_,hashes_size);
+    if (!hashes_) {
+        size_ = sizec_ = complete_ = completec_ = 0;
+        print_error("mmap failed");
+        return;
+    }
+    size_t last_piece_size = (sizec_ - 1) % (chunk_size_) + 1;
+    char *chunk = new char[chunk_size_];
+    for (uint64_t i=0; i<sizec_; i++) {
+
+        size_t rd = read(fd_,chunk,chunk_size_);
+        if (rd<(chunk_size_) && i!=sizec_-1) {
+            free(hashes_);
+            hashes_=NULL;
+            return;
+        }
+        bin_t pos(0,i);
+        hashes_[pos.toUInt()] = Sha1Hash(chunk,rd);
+        ack_out_.set(pos);
+        while (pos.is_right()){
+            pos = pos.parent();
+            hashes_[pos.toUInt()] = Sha1Hash(hashes_[pos.left().toUInt()],hashes_[pos.right().toUInt()]);
+        }
+        complete_+=rd;
+        completec_++;
+    }
+    delete chunk;
+    for (int p=0; p<peak_count_; p++) {
+        peak_hashes_[p] = hashes_[peaks_[p].toUInt()];
+    }
+
+    root_hash_ = DeriveRoot();
+
+}
+
+
+/** Basically, simulated receiving every single chunk, except
+ for some optimizations.
+ Precondition: root hash known */
+void            HashTree::RecoverProgress () {
+
+       //fprintf(stderr,"hashtree: recover: cs %i\n", chunk_size_);
+
+       if (!RecoverPeakHashes())
+               return;
+
+    // at this point, we may use mmapd hashes already
+    // so, lets verify hashes and the data we've got
+    char *zero_chunk = new char[chunk_size_];
+    memset(zero_chunk, 0, chunk_size_);
+    Sha1Hash zero_hash(zero_chunk,chunk_size_);
+
+    // Arno: loop over all pieces, read each from file
+    // ARNOSMPTODO: problem is that we may have the complete hashtree, but
+    // not have all pieces. So hash file gives too little information to
+    // determine whether file is complete on disk.
+    //
+    char *buf = new char[chunk_size_];
+    for(int p=0; p<size_in_chunks(); p++) {
+        bin_t pos(0,p);
+        if (hashes_[pos.toUInt()]==Sha1Hash::ZERO)
+            continue;
+        size_t rd = read(fd_,buf,chunk_size_);
+        if (rd!=(chunk_size_) && p!=size_in_chunks()-1)
+            break;
+        if (rd==(chunk_size_) && !memcmp(buf, zero_chunk, rd) &&
+                hashes_[pos.toUInt()]!=zero_hash) // FIXME // Arno == don't have piece yet?
+            continue;
+        if (!OfferHash(pos, Sha1Hash(buf,rd)) )
+            continue;
+        ack_out_.set(pos);
+        completec_++;
+        complete_+=rd;
+        if (rd!=(chunk_size_) && p==size_in_chunks()-1) // set the exact file size
+            size_ = ((sizec_-1)*chunk_size_) + rd;
+    }
+    delete buf;
+    delete zero_chunk;
+}
+
+/** Precondition: root hash known */
+bool HashTree::RecoverPeakHashes()
+{
+    uint64_t size = file_size(fd_);
+    uint64_t sizek = (size + chunk_size_-1) / chunk_size_;
+
+       // Arno: Calc location of peak hashes, read them from hash file and check if
+       // they match to root hash. If so, load hashes into memory.
+       bin_t peaks[64];
+    int peak_count = gen_peaks(sizek,peaks);
+    for(int i=0; i<peak_count; i++) {
+        Sha1Hash peak_hash;
+        file_seek(hash_fd_,peaks[i].toUInt()*sizeof(Sha1Hash));
+        if (read(hash_fd_,&peak_hash,sizeof(Sha1Hash))!=sizeof(Sha1Hash))
+            return false;
+        OfferPeakHash(peaks[i], peak_hash);
+    }
+    if (!this->size())
+        return false; // if no valid peak hashes found
+
+    return true;
+}
+
+int HashTree::serialize(FILE *fp)
+{
+       fprintf_retiffail(fp,"version %i\n", 1 );
+       fprintf_retiffail(fp,"root hash %s\n", root_hash_.hex().c_str() );
+       fprintf_retiffail(fp,"chunk size %lu\n", chunk_size_ );
+       fprintf_retiffail(fp,"complete %llu\n", complete_ );
+       fprintf_retiffail(fp,"completec %llu\n", completec_ );
+       return ack_out_.serialize(fp);
+}
+
+
+/** Arno: recreate hash tree from .mbinmap file without rereading content.
+ * Precondition: root hash known
+ */
+int HashTree::deserialize(FILE *fp) {
+       return internal_deserialize(fp,true);
+}
+
+int HashTree::partial_deserialize(FILE *fp) {
+       return internal_deserialize(fp,false);
+}
+
+
+int HashTree::internal_deserialize(FILE *fp,bool contentavail) {
+
+       char hexhashstr[256];
+       uint64_t c,cc;
+       size_t cs;
+       int version;
+
+       fscanf_retiffail(fp,"version %i\n", &version );
+       fscanf_retiffail(fp,"root hash %s\n", hexhashstr);
+       fscanf_retiffail(fp,"chunk size %lu\n", &cs);
+       fscanf_retiffail(fp,"complete %llu\n", &c );
+       fscanf_retiffail(fp,"completec %llu\n", &cc );
+
+       //fprintf(stderr,"hashtree: deserialize: %s %llu ~ %llu * %i\n", hexhashstr, c, cc, cs );
+
+       if (ack_out_.deserialize(fp) < 0)
+               return -1;
+       root_hash_ = Sha1Hash(true, hexhashstr);
+       chunk_size_ = cs;
+
+       // Arno, 2012-01-03: Hack to just get root hash
+       if (!contentavail)
+               return 2;
+
+       if (!RecoverPeakHashes()) {
+               root_hash_ = Sha1Hash::ZERO;
+               ack_out_.clear();
+               return -1;
+       }
+
+       // Are reset by RecoverPeakHashes() for some reason.
+       complete_ = c;
+       completec_ = cc;
+    size_ = file_size(fd_);
+    sizec_ = (size_ + chunk_size_-1) / chunk_size_;
+
+    return 0;
+}
+
+
+bool            HashTree::OfferPeakHash (bin_t pos, const Sha1Hash& hash) {
+       char bin_name_buf[32];
+       dprintf("%s hashtree offer peak %s\n",tintstr(),pos.str(bin_name_buf));
+
+    assert(!size_);
+    if (peak_count_) {
+        bin_t last_peak = peaks_[peak_count_-1];
+        if ( pos.layer()>=last_peak.layer() ||
+            pos.base_offset()!=last_peak.base_offset()+last_peak.base_length() )
+            peak_count_ = 0;
+    }
+    peaks_[peak_count_] = pos;
+    peak_hashes_[peak_count_] = hash;
+    peak_count_++;
+    // check whether peak hash candidates add up to the root hash
+    Sha1Hash mustbe_root = DeriveRoot();
+    if (mustbe_root!=root_hash_)
+        return false;
+    for(int i=0; i<peak_count_; i++)
+        sizec_ += peaks_[i].base_length();
+
+    // bingo, we now know the file size (rounded up to a chunk_size() unit)
+
+    size_ = sizec_ * chunk_size_;
+    completec_ = complete_ = 0;
+    sizec_ = (size_ + chunk_size_-1) / chunk_size_;
+
+    // ARNOTODO: win32: this is pretty slow for ~200 MB already. Perhaps do
+    // on-demand sizing for Win32?
+    uint64_t cur_size = file_size(fd_);
+    if ( cur_size<=(sizec_-1)*chunk_size_  || cur_size>sizec_*chunk_size_ ) {
+       dprintf("%s hashtree offerpeak resizing file\n",tintstr() );
+        if (file_resize(fd_, size_)) {
+            print_error("cannot set file size\n");
+            size_=0; // remain in the 0-state
+            return false;
+        }
+    }
+
+    // mmap the hash file into memory
+    uint64_t expected_size = sizeof(Sha1Hash)*sizec_*2;
+    // Arno, 2011-10-18: on Windows we could optimize this away,
+    //CreateFileMapping, see compat.cpp will resize the file for us with
+    // the right params.
+    //
+    if ( file_size(hash_fd_) != expected_size ) {
+       dprintf("%s hashtree offerpeak resizing hash file\n",tintstr() );
+        file_resize (hash_fd_, expected_size);
+    }
+
+    hashes_ = (Sha1Hash*) memory_map(hash_fd_,expected_size);
+    if (!hashes_) {
+        size_ = sizec_ = complete_ = completec_ = 0;
+        print_error("mmap failed");
+        return false;
+    }
+
+    for(int i=0; i<peak_count_; i++)
+        hashes_[peaks_[i].toUInt()] = peak_hashes_[i];
+
+    dprintf("%s hashtree memory mapped\n",tintstr() );
+
+    return true;
+}
+
+
+Sha1Hash        HashTree::DeriveRoot () {
+
+       dprintf("%s hashtree deriving root\n",tintstr() );
+
+    int c = peak_count_-1;
+    bin_t p = peaks_[c];
+    Sha1Hash hash = peak_hashes_[c];
+    c--;
+    // Arno, 2011-10-14: Root hash = top of smallest tree covering content IMHO.
+    //while (!p.is_all()) {
+    while (c >= 0) {
+        if (p.is_left()) {
+            p = p.parent();
+            hash = Sha1Hash(hash,Sha1Hash::ZERO);
+        } else {
+            if (c<0 || peaks_[c]!=p.sibling())
+                return Sha1Hash::ZERO;
+            hash = Sha1Hash(peak_hashes_[c],hash);
+            p = p.parent();
+            c--;
+        }
+    }
+    //fprintf(stderr,"root bin is %lli covers %lli\n", p.toUInt(), p.base_length() );
+    return hash;
+}
+
+
+/** For live streaming: appends the data, adjusts the tree.
+    @ return the number of fresh (tail) peak hashes */
+int         HashTree::AppendData (char* data, int length) {
+    return 0;
+}
+
+
+bin_t         HashTree::peak_for (bin_t pos) const {
+    int pi=0;
+    while (pi<peak_count_ && !peaks_[pi].contains(pos))
+        pi++;
+    return pi==peak_count_ ? bin_t(bin_t::NONE) : peaks_[pi];
+}
+
+bool            HashTree::OfferHash (bin_t pos, const Sha1Hash& hash) {
+    if (!size_)  // only peak hashes are accepted at this point
+        return OfferPeakHash(pos,hash);
+
+    //NETWVSHASH
+    if (!check_netwvshash_)
+       return true;
+
+    bin_t peak = peak_for(pos);
+    if (peak.is_none())
+        return false;
+    if (peak==pos)
+        return hash == hashes_[pos.toUInt()];
+    if (!ack_out_.is_empty(pos.parent()))
+        return hash==hashes_[pos.toUInt()]; // have this hash already, even accptd data
+    // LESSHASH
+    // Arno: if we already verified this hash against the root, don't replace
+    if (!is_hash_verified_.is_empty(bin_t(0,pos.toUInt())))
+       return hash == hashes_[pos.toUInt()];
+
+    hashes_[pos.toUInt()] = hash;
+    if (!pos.is_base())
+        return false; // who cares?
+    bin_t p = pos;
+    Sha1Hash uphash = hash;
+    // Arno: Note well: bin_t(0,p.toUInt()) is to abuse binmap as bitmap.
+    while ( p!=peak && ack_out_.is_empty(p) && is_hash_verified_.is_empty(bin_t(0,p.toUInt())) ) {
+        hashes_[p.toUInt()] = uphash;
+        p = p.parent();
+               // Arno: Prevent poisoning the tree with bad values:
+               // Left hand hashes should never be zero, and right
+               // hand hash is only zero for the last packet, i.e.,
+               // layer 0. Higher layers will never have 0 hashes
+               // as SHA1(zero+zero) != zero (but b80de5...)
+               //
+        if (hashes_[p.left().toUInt()] == Sha1Hash::ZERO || hashes_[p.right().toUInt()] == Sha1Hash::ZERO)
+               break;
+        uphash = Sha1Hash(hashes_[p.left().toUInt()],hashes_[p.right().toUInt()]);
+    }// walk to the nearest proven hash
+
+    bool success = (uphash==hashes_[p.toUInt()]);
+    // LESSHASH
+    if (success) {
+       // Arno: The hash checks out. Mark all hashes on the uncle path as
+       // being verified, so we don't have to go higher than them on a next
+       // check.
+       p = pos;
+       // Arno: Note well: bin_t(0,p.toUInt()) is to abuse binmap as bitmap.
+       is_hash_verified_.set(bin_t(0,p.toUInt()));
+        while (p.layer() != peak.layer()) {
+            p = p.parent().sibling();
+               is_hash_verified_.set(bin_t(0,p.toUInt()));
+        }
+        // Also mark hashes on direct path to root as verified. Doesn't decrease
+        // #checks, but does increase the number of verified hashes faster.
+       p = pos;
+        while (p != peak) {
+            p = p.parent();
+               is_hash_verified_.set(bin_t(0,p.toUInt()));
+        }
+    }
+
+    return success;
+}
+
+
+bool            HashTree::OfferData (bin_t pos, const char* data, size_t length) {
+    if (!size())
+        return false;
+    if (!pos.is_base())
+        return false;
+    if (length<chunk_size_ && pos!=bin_t(0,sizec_-1))
+        return false;
+    if (ack_out_.is_filled(pos))
+        return true; // to set data_in_
+    bin_t peak = peak_for(pos);
+    if (peak.is_none())
+        return false;
+
+    Sha1Hash data_hash(data,length);
+    if (!OfferHash(pos, data_hash)) {
+        char bin_name_buf[32];
+//        printf("invalid hash for %s: %s\n",pos.str(bin_name_buf),data_hash.hex().c_str()); // paranoid
+       //fprintf(stderr,"INVALID HASH FOR %lli layer %d\n", pos.toUInt(), pos.layer() );
+       dprintf("%s hashtree check failed (bug TODO) %s\n",tintstr(),pos.str(bin_name_buf));
+        return false;
+    }
+
+    //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
+    ack_out_.set(pos);
+    // Arno,2011-10-03: appease g++
+    if (pwrite(fd_,data,length,pos.base_offset()*chunk_size_) < 0)
+       print_error("pwrite failed");
+    complete_ += length;
+    completec_++;
+    if (pos.base_offset()==sizec_-1) {
+        size_ = ((sizec_-1)*chunk_size_) + length;
+        if (file_size(fd_)!=size_)
+            file_resize(fd_,size_);
+    }
+    return true;
+}
+
+
+uint64_t      HashTree::seq_complete () {
+    uint64_t seqc = ack_out_.find_empty().base_offset();
+    if (seqc==sizec_)
+        return size_;
+    else
+        return seqc*chunk_size_;
+}
+
+HashTree::~HashTree () {
+    if (hashes_)
+        memory_unmap(hash_fd_, hashes_, sizec_*2*sizeof(Sha1Hash));
+    if (fd_)
+        close(fd_);
+    if (hash_fd_)
+        close(hash_fd_);
+}
+
diff --git a/src/libswift_udp/hashtree.h b/src/libswift_udp/hashtree.h
new file mode 100644 (file)
index 0000000..a9827d0
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *  hashtree.h
+ *  hashing, Merkle hash trees and data integrity
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#ifndef SWIFT_SHA1_HASH_TREE_H
+#define SWIFT_SHA1_HASH_TREE_H
+#include <string.h>
+#include <string>
+#include "bin.h"
+#include "binmap.h"
+
+namespace swift {
+
+#define HASHSZ 20
+
+/** SHA-1 hash, 20 bytes of data */
+struct Sha1Hash {
+    uint8_t    bits[HASHSZ];
+
+    Sha1Hash() { memset(bits,0,HASHSZ); }
+    /** Make a hash of two hashes (for building Merkle hash trees). */
+    Sha1Hash(const Sha1Hash& left, const Sha1Hash& right);
+    /** Hash an old plain string. */
+    Sha1Hash(const char* str, size_t length=-1);
+    Sha1Hash(const uint8_t* data, size_t length);
+    /** Either parse hash from hex representation of read in raw format. */
+    Sha1Hash(bool hex, const char* hash);
+    
+    std::string    hex() const;
+    bool    operator == (const Sha1Hash& b) const
+        { return 0==memcmp(bits,b.bits,SIZE); }
+    bool    operator != (const Sha1Hash& b) const { return !(*this==b); }
+    const char* operator * () const { return (char*) bits; }
+    
+    const static Sha1Hash ZERO;
+    const static size_t SIZE = HASHSZ;
+};
+
+// Arno: The chunk size parameter can now be configured via the constructor,
+// for values up to 8192. Above that you'll have to edit the
+// SWIFT_MAX_SEND_DGRAM_SIZE in swift.h
+//
+#define SWIFT_DEFAULT_CHUNK_SIZE 1024
+
+
+/** This class controls data integrity of some file; hash tree is put to
+    an auxilliary file next to it. The hash tree file is mmap'd for
+    performance reasons. Actually, I'd like the data file itself to be
+    mmap'd, but 32-bit platforms do not allow that for bigger files. 
+    There are two variants of the general workflow: either a HashTree
+    is initialized with a root hash and the rest of hashes and data is
+    spoon-fed later, OR a HashTree is initialized with a data file, so
+    the hash tree is derived, including the root hash.
+ */
+class HashTree : Serializable {
+    /** Merkle hash tree: root */
+    Sha1Hash        root_hash_;
+    Sha1Hash        *hashes_;
+    /** Merkle hash tree: peak hashes */
+    Sha1Hash        peak_hashes_[64];
+    bin_t           peaks_[64];
+    int             peak_count_;
+    /** File descriptor to put hashes to */
+    int             fd_;
+    int             hash_fd_;
+    std::string                filename_; // for easy serialization
+    /** Base size, as derived from the hashes. */
+    uint64_t        size_;
+    uint64_t        sizec_;
+    /**    Part of the tree currently checked. */
+    uint64_t        complete_;
+    uint64_t        completec_;
+    /**    Binmap of own chunk availability */
+    binmap_t        ack_out_;
+
+       // CHUNKSIZE
+       /** Arno: configurable fixed chunk size in bytes */
+    uint32_t                   chunk_size_;
+
+       // LESSHASH
+       binmap_t                is_hash_verified_; // binmap being abused as bitmap, only layer 0 used
+       // FAXME: make is_hash_verified_ part of persistent state?
+
+    int                        internal_deserialize(FILE *fp,bool contentavail=true);
+
+    //NETWVSHASH
+    bool                       check_netwvshash_;
+
+protected:
+    
+    void            Submit();
+    void            RecoverProgress();
+    bool                       RecoverPeakHashes();
+    Sha1Hash        DeriveRoot();
+    bool            OfferPeakHash (bin_t pos, const Sha1Hash& hash);
+
+    
+public:
+    
+    HashTree (const char* file_name, const Sha1Hash& root=Sha1Hash::ZERO, uint32_t chunk_size=SWIFT_DEFAULT_CHUNK_SIZE,
+              const char* hash_filename=NULL, bool force_check_diskvshash=true, bool check_netwvshash=true, const char* binmap_filename=NULL);
+    
+    // Arno, 2012-01-03: Hack to quickly learn root hash from a checkpoint
+    HashTree (bool dummy, const char* binmap_filename);
+
+    /** Offer a hash; returns true if it verified; false otherwise.
+     Once it cannot be verified (no sibling or parent), the hash
+     is remembered, while returning false. */
+    bool            OfferHash (bin_t pos, const Sha1Hash& hash);
+    /** Offer data; the behavior is the same as with a hash:
+     accept or remember or drop. Returns true => ACK is sent. */
+    bool            OfferData (bin_t bin, const char* data, size_t length);
+    /** For live streaming. Not implemented yet. */
+    int             AppendData (char* data, int length) ;
+    
+    int             file_descriptor () const { return fd_; }
+    /** Returns the number of peaks (read on peak hashes). */
+    int             peak_count () const { return peak_count_; }
+    /** Returns the i-th peak's bin number. */
+    bin_t           peak (int i) const { return peaks_[i]; }
+    /** Returns peak hash #i. */
+    const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
+    /** Return the peak bin the given bin belongs to. */
+    bin_t           peak_for (bin_t pos) const;
+    /** Return a (Merkle) hash for the given bin. */
+    const Sha1Hash& hash (bin_t pos) const {return hashes_[pos.toUInt()];}
+    /** Give the root hash, which is effectively an identifier of this file. */
+    const Sha1Hash& root_hash () const { return root_hash_; }
+    /** Get file size, in bytes. */
+    uint64_t        size () const { return size_; }
+    /** Get file size in chunks (in kilobytes, rounded up). */
+    uint64_t        size_in_chunks () const { return sizec_; }
+    /** Number of bytes retrieved and checked. */
+    uint64_t        complete () const { return complete_; }
+    /** Number of chunks retrieved and checked. */
+    uint64_t        chunks_complete () const { return completec_; }
+    /** The number of bytes completed sequentially, i.e. from the beginning of
+        the file, uninterrupted. */
+    uint64_t        seq_complete () ;
+    /** Whether the file is complete. */
+    bool            is_complete () 
+        { return size_ && complete_==size_; }
+    /** The binmap of complete chunks. */
+    binmap_t&       ack_out () { return ack_out_; }
+    std::string                filename() { return filename_; } // Arno
+    uint32_t           chunk_size() { return chunk_size_; } // CHUNKSIZE
+    ~HashTree ();
+
+    // Arno: persistent storage for state other than hashes (which are in .mhash)
+    int serialize(FILE *fp);
+    int deserialize(FILE *fp);
+    int partial_deserialize(FILE *fp);
+
+    //NETWVSHASH
+    bool get_check_netwvshash() { return check_netwvshash_; }
+};
+
+}
+
+#endif
diff --git a/src/libswift_udp/httpgw.cpp b/src/libswift_udp/httpgw.cpp
new file mode 100644 (file)
index 0000000..542b1e5
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ *  httpgw.cpp
+ *  gateway for serving swift content via HTTP, libevent2 based.
+ *
+ *  Created by Victor Grishchenko, Arno Bakker
+ *  Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include "swift.h"
+#include <event2/http.h>
+#include <event2/bufferevent.h>
+
+using namespace swift;
+
+#define HTTPGW_PROGRESS_STEP_BYTES             (256*1024)
+// For best performance make bigger than HTTPGW_PROGRESS_STEP_BYTES
+#define HTTPGW_MAX_WRITE_BYTES                 (512*1024)
+
+// Report swift download progress every 2^layer * chunksize bytes (so 0 = report every chunk)
+#define HTTPGW_FIRST_PROGRESS_BYTE_INTERVAL_AS_LAYER   0
+
+// Arno: libevent2 has a liberal understanding of socket writability,
+// that may result in tens of megabytes being cached in memory. Limit that
+// amount at app level.
+#define HTTPGW_MAX_PREBUF_BYTES                        (2*1024*1024)
+
+#define HTTPGW_MAX_REQUEST 128
+
+struct http_gw_t {
+    int      id;
+    uint64_t offset;
+    uint64_t tosend;
+    int      transfer;
+    uint64_t lastcpoffset; // last offset at which we checkpointed
+    struct evhttp_request *sinkevreq;
+    struct event                 *sinkevwrite;
+    char*    xcontentdur;
+    bool   closing;
+
+} http_requests[HTTPGW_MAX_REQUEST];
+
+
+int http_gw_reqs_open = 0;
+int http_gw_reqs_count = 0;
+struct evhttp *http_gw_event;
+struct evhttp_bound_socket *http_gw_handle;
+uint32_t httpgw_chunk_size = SWIFT_DEFAULT_CHUNK_SIZE; // Copy of cmdline param
+double *httpgw_maxspeed = NULL;                                                 // Copy of cmdline param
+
+// Arno, 2010-11-30: for SwarmPlayer 3000 backend autoquit when no HTTP req is received
+bool sawhttpconn = false;
+
+
+http_gw_t *HttpGwFindRequestByEV(struct evhttp_request *evreq) {
+       for (int httpc=0; httpc<http_gw_reqs_open; httpc++) {
+               if (http_requests[httpc].sinkevreq==evreq)
+                       return &http_requests[httpc];
+    }
+       return NULL;
+}
+
+http_gw_t *HttpGwFindRequestByTransfer(int transfer) {
+       for (int httpc=0; httpc<http_gw_reqs_open; httpc++) {
+               if (http_requests[httpc].transfer==transfer) {
+                       return &http_requests[httpc];
+               }
+       }
+       return NULL;
+}
+
+void HttpGwCloseConnection (http_gw_t* req) {
+       dprintf("%s @%i cleanup http request evreq %p\n",tintstr(),req->id, req->sinkevreq);
+
+       struct evhttp_connection *evconn = evhttp_request_get_connection(req->sinkevreq);
+
+       dprintf("%s @%i cleanup: before send reply\n",tintstr(),req->id);
+
+       req->closing = true;
+       if (req->offset > 0)
+               evhttp_send_reply_end(req->sinkevreq); //WARNING: calls HttpGwLibeventCloseCallback
+       else
+               evhttp_request_free(req->sinkevreq);
+
+       dprintf("%s @%i cleanup: reset evreq\n",tintstr(),req->id);
+       req->sinkevreq = NULL;
+       dprintf("%s @%i cleanup: after reset evreq\n",tintstr(),req->id);
+
+       // Note: for some reason calling conn_free here prevents the last chunks
+       // to be sent to the requester?
+       //      evhttp_connection_free(evconn); // WARNING: calls HttpGwLibeventCloseCallback
+
+       // Current close policy: checkpoint and DO NOT close transfer, keep on
+       // seeding forever. More sophisticated clients should use CMD GW and issue
+       // REMOVE.
+       swift::Checkpoint(req->transfer);
+
+       //swift::Close(req->transfer);
+
+       *req = http_requests[--http_gw_reqs_open];
+}
+
+
+void HttpGwLibeventCloseCallback(struct evhttp_connection *evconn, void *evreqvoid) {
+       // Called by libevent on connection close, either when the other side closes
+       // or when we close (because we call evhttp_connection_free()). To prevent
+       // doing cleanup twice, we see if there is a http_gw_req that has the
+       // passed evreqvoid as sinkevreq. If so, clean up, if not, ignore.
+       // I.e. evhttp_request * is used as sort of request ID
+       //
+       fprintf(stderr,"HttpGwLibeventCloseCallback: called\n");
+       http_gw_t * req = HttpGwFindRequestByEV((struct evhttp_request *)evreqvoid);
+       if (req == NULL)
+               dprintf("%s http conn already closed\n",tintstr() );
+       else {
+               dprintf("%s T%i http close conn\n",tintstr(),req->transfer);
+               if (req->closing)
+                       dprintf("%s http conn already closing\n",tintstr() );
+               else
+                       HttpGwCloseConnection(req);
+       }
+}
+
+
+
+
+void HttpGwMayWriteCallback (int transfer) {
+       // Write some data to client
+
+       http_gw_t* req = HttpGwFindRequestByTransfer(transfer);
+       if (req == NULL) {
+       print_error("httpgw: MayWrite: can't find req for transfer");
+        return;
+    }
+
+    uint64_t complete = swift::SeqComplete(req->transfer);
+
+    //dprintf("%s @%i http write complete %lli offset %lli\n",tintstr(),req->id, complete, req->offset);
+    //fprintf(stderr,"offset %lli seqcomp %lli comp %lli\n",req->offset, complete, swift::Complete(req->transfer) );
+
+       struct evhttp_connection *evconn = evhttp_request_get_connection(req->sinkevreq);
+       struct bufferevent* buffy = evhttp_connection_get_bufferevent(evconn);
+       struct evbuffer *outbuf = bufferevent_get_output(buffy);
+
+       //fprintf(stderr,"httpgw: MayWrite avail %i bufev outbuf %i\n",complete-req->offset, evbuffer_get_length(outbuf) );
+
+    if (complete > req->offset && evbuffer_get_length(outbuf) < HTTPGW_MAX_PREBUF_BYTES)
+    {
+       // Received more than I pushed to player, send data
+        char buf[HTTPGW_MAX_WRITE_BYTES];
+// Arno, 2010-08-16, TODO
+#ifdef WIN32
+        uint64_t tosend = min(HTTPGW_MAX_WRITE_BYTES,complete-req->offset);
+#else
+        uint64_t tosend = std::min((uint64_t)HTTPGW_MAX_WRITE_BYTES,complete-req->offset);
+#endif
+        size_t rd = pread(req->transfer,buf,tosend,req->offset); // hope it is cached
+        if (rd<0) {
+               print_error("httpgw: MayWrite: error pread");
+            HttpGwCloseConnection(req);
+            return;
+        }
+
+        // Construct evbuffer and send incrementally
+        struct evbuffer *evb = evbuffer_new();
+        int ret = evbuffer_add(evb,buf,rd);
+        if (ret < 0) {
+               print_error("httpgw: MayWrite: error evbuffer_add");
+               evbuffer_free(evb);
+            HttpGwCloseConnection(req);
+            return;
+        }
+
+        if (req->offset == 0) {
+               // Not just for chunked encoding, see libevent2's http.c
+               evhttp_send_reply_start(req->sinkevreq, 200, "OK");
+        }
+
+        evhttp_send_reply_chunk(req->sinkevreq, evb);
+        evbuffer_free(evb);
+
+        int wn = rd;
+        dprintf("%s @%i http sent data %ib\n",tintstr(),req->id,(int)wn);
+
+        req->offset += wn;
+        req->tosend -= wn;
+
+        // PPPLUG
+       FileTransfer *ft = FileTransfer::file(transfer);
+       if (ft == NULL)
+               return;
+        ft->picker().updatePlaybackPos( wn/ft->file().chunk_size() );
+    }
+
+    // Arno, 2010-11-30: tosend is set to fuzzy len, so need extra/other test.
+    if (req->tosend==0 || req->offset ==  swift::Size(req->transfer)) {
+       // done; wait for new HTTP request
+        dprintf("%s @%i done\n",tintstr(),req->id);
+        //fprintf(stderr,"httpgw: MayWrite: done, wait for buffer empty before send_end_reply\n" );
+
+        if (evbuffer_get_length(outbuf) == 0) {
+               //fprintf(stderr,"httpgw: MayWrite: done, buffer empty, end req\n" );
+               HttpGwCloseConnection(req);
+        }
+    }
+    else {
+       // wait for data
+        dprintf("%s @%i waiting for data\n",tintstr(),req->id);
+    }
+}
+
+void HttpGwLibeventMayWriteCallback(evutil_socket_t fd, short events, void *evreqvoid );
+
+void HttpGwSubscribeToWrite(http_gw_t * req) {
+       struct evhttp_connection *evconn = evhttp_request_get_connection(req->sinkevreq);
+       struct event_base *evbase =     evhttp_connection_get_base(evconn);
+       struct bufferevent* evbufev = evhttp_connection_get_bufferevent(evconn);
+
+       if (req->sinkevwrite != NULL)
+               event_free(req->sinkevwrite); // FAXME: clean in CloseConn
+
+       req->sinkevwrite = event_new(evbase,bufferevent_getfd(evbufev),EV_WRITE,HttpGwLibeventMayWriteCallback,req->sinkevreq);
+       struct timeval t;
+       t.tv_sec = 10;
+       int ret = event_add(req->sinkevwrite,&t);
+       //fprintf(stderr,"httpgw: HttpGwSubscribeToWrite: added event\n");
+}
+
+
+void HttpGwLibeventMayWriteCallback(evutil_socket_t fd, short events, void *evreqvoid )
+{
+       //fprintf(stderr,"httpgw: MayWrite: %d events %d evreq is %p\n", fd, events, evreqvoid);
+
+       http_gw_t * req = HttpGwFindRequestByEV((struct evhttp_request *)evreqvoid);
+       if (req != NULL) {
+               //fprintf(stderr,"httpgw: MayWrite: %d events %d httpreq is %p\n", fd, events, req);
+               HttpGwMayWriteCallback(req->transfer);
+
+
+               // Arno, 2011-12-20: No autoreschedule, let HttpGwSwiftProgressCallback do that
+               //if (req->sinkevreq != NULL) // Conn closed
+               //      HttpGwSubscribeToWrite(req);
+
+               //fprintf(stderr,"GOTO WRITE %lli >= %lli\n", swift::Complete(req->transfer)+HTTPGW_MAX_WRITE_BYTES, swift::Size(req->transfer) );
+
+               if (swift::Complete(req->transfer)+HTTPGW_MAX_WRITE_BYTES >= swift::Size(req->transfer)) {
+
+               // We don't get progress callback for last chunk < chunk size, nor
+               // when all data is already on disk. In that case, just keep on
+                       // subscribing to HTTP socket writability until all data is sent.
+                       if (req->sinkevreq != NULL) // Conn closed
+                               HttpGwSubscribeToWrite(req);
+               }
+       }
+}
+
+void HttpGwSwiftProgressCallback (int transfer, bin_t bin) {
+       // Subsequent HTTPGW_PROGRESS_STEP_BYTES available
+
+       dprintf("%s T%i http more progress\n",tintstr(),transfer);
+       http_gw_t* req = HttpGwFindRequestByTransfer(transfer);
+       if (req == NULL)
+               return;
+
+       // Arno, 2011-12-20: We have new data to send, wait for HTTP socket writability
+       if (req->sinkevreq != NULL) { // Conn closed
+               HttpGwSubscribeToWrite(req);
+       }
+}
+
+
+void HttpGwFirstProgressCallback (int transfer, bin_t bin) {
+       // First chunk of data available
+       dprintf("%s T%i http first progress\n",tintstr(),transfer);
+
+       if (!bin.contains(bin_t(0,0))) // need the first chunk
+        return;
+
+       swift::RemoveProgressCallback(transfer,&HttpGwFirstProgressCallback);
+    int progresslayer = bytes2layer(HTTPGW_PROGRESS_STEP_BYTES,swift::ChunkSize(transfer));
+    swift::AddProgressCallback(transfer,&HttpGwSwiftProgressCallback,progresslayer);
+
+       http_gw_t* req = HttpGwFindRequestByTransfer(transfer);
+       if (req == NULL)
+               return;
+       if (req->tosend==0) { // FIXME states
+               uint64_t filesize = swift::Size(transfer);
+               char filesizestr[256];
+               sprintf(filesizestr,"%lli",filesize);
+
+               struct evkeyvalq *headers = evhttp_request_get_output_headers(req->sinkevreq);
+               //evhttp_add_header(headers, "Connection", "keep-alive" );
+               evhttp_add_header(headers, "Connection", "close" );
+               evhttp_add_header(headers, "Content-Type", "video/ogg" );
+               evhttp_add_header(headers, "X-Content-Duration",req->xcontentdur );
+               evhttp_add_header(headers, "Content-Length", filesizestr );
+               evhttp_add_header(headers, "Accept-Ranges", "none" );
+
+               req->tosend = filesize;
+               dprintf("%s @%i headers_sent size %lli\n",tintstr(),req->id,filesize);
+
+               /*
+                * Arno, 2011-10-17: Swift ProgressCallbacks are only called when
+                * the data is downloaded, not when it is already on disk. So we need
+                * to handle the situation where all or part of the data is already
+                * on disk. Subscribing to writability of the socket works,
+                * but requires libevent2 >= 2.1 (or our backported version)
+                */
+               HttpGwSubscribeToWrite(req);
+    }
+}
+
+
+void HttpGwNewRequestCallback (struct evhttp_request *evreq, void *arg) {
+
+    dprintf("%s @%i http new request\n",tintstr(),http_gw_reqs_count+1);
+
+    if (evhttp_request_get_command(evreq) != EVHTTP_REQ_GET) {
+            return;
+    }
+       sawhttpconn = true;
+
+    // Parse URI
+    const char *uri = evhttp_request_get_uri(evreq);
+    //struct evkeyvalq *headers =      evhttp_request_get_input_headers(evreq);
+    //const char *contentrangestr =evhttp_find_header(headers,"Content-Range");
+
+    char *tokenuri = (char *)malloc(strlen(uri)+1);
+    strcpy(tokenuri,uri);
+    char * hashch=strtok(tokenuri,"/"), hash[41];
+    while (hashch && (1!=sscanf(hashch,"%40[0123456789abcdefABCDEF]",hash) || strlen(hash)!=40))
+        hashch = strtok(NULL,"/");
+    free(tokenuri);
+    if (strlen(hash)!=40) {
+       evhttp_send_error(evreq,400,"Path must be root hash in hex, 40 bytes.");
+        return;
+    }
+    char *xcontentdur = NULL;
+    if (strlen(uri) > 42) {
+       xcontentdur = (char *)malloc(strlen(uri)-42+1);
+       strcpy(xcontentdur,&uri[42]);
+    }
+    else
+       xcontentdur = (char *)"0";
+    dprintf("%s @%i demands %s %s\n",tintstr(),http_gw_reqs_open+1,hash,xcontentdur);
+
+    // initiate transmission
+    Sha1Hash root_hash = Sha1Hash(true,hash);
+    int transfer = swift::Find(root_hash);
+    if (transfer==-1) {
+        transfer = swift::Open(hash,root_hash,Address(),false,true,httpgw_chunk_size); // ARNOTODO: allow for chunk size to be set via URL?
+        dprintf("%s @%i trying to HTTP GET swarm %s that has not been STARTed\n",tintstr(),http_gw_reqs_open+1,hash);
+
+        // Arno, 2011-12-20: Only on new transfers, otherwise assume that CMD GW
+        // controls speed
+        FileTransfer *ft = FileTransfer::file(transfer);
+        ft->SetMaxSpeed(DDIR_DOWNLOAD,httpgw_maxspeed[DDIR_DOWNLOAD]);
+        ft->SetMaxSpeed(DDIR_UPLOAD,httpgw_maxspeed[DDIR_UPLOAD]);
+    }
+
+    // Record request
+    http_gw_t* req = http_requests + http_gw_reqs_open++;
+    req->id = ++http_gw_reqs_count;
+    req->sinkevreq = evreq;
+    req->xcontentdur = xcontentdur;
+    req->offset = 0;
+    req->tosend = 0;
+    req->transfer = transfer;
+    req->lastcpoffset = 0;
+    req->sinkevwrite = NULL;
+    req->closing = false;
+
+    fprintf(stderr,"httpgw: Opened %s\n",hash);
+
+    // We need delayed replying, so take ownership.
+    // See http://code.google.com/p/libevent-longpolling/source/browse/trunk/main.c
+       // Careful: libevent docs are broken. It doesn't say that evhttp_send_reply_send
+       // actually calls evhttp_request_free, i.e. releases ownership for you.
+       //
+    evhttp_request_own(evreq);
+
+    // Register callback for connection close
+    struct evhttp_connection *evconn = evhttp_request_get_connection(req->sinkevreq);
+    evhttp_connection_set_closecb(evconn,HttpGwLibeventCloseCallback,req->sinkevreq);
+
+    if (swift::Size(transfer)) {
+        HttpGwFirstProgressCallback(transfer,bin_t(0,0));
+    } else {
+        swift::AddProgressCallback(transfer,&HttpGwFirstProgressCallback,HTTPGW_FIRST_PROGRESS_BYTE_INTERVAL_AS_LAYER);
+    }
+}
+
+
+bool InstallHTTPGateway (struct event_base *evbase,Address bindaddr, uint32_t chunk_size, double *maxspeed) {
+       // Arno, 2011-10-04: From libevent's http-server.c example
+
+       /* Create a new evhttp object to handle requests. */
+       http_gw_event = evhttp_new(evbase);
+       if (!http_gw_event) {
+               print_error("httpgw: evhttp_new failed");
+               return false;
+       }
+
+       /* Install callback for all requests */
+       evhttp_set_gencb(http_gw_event, HttpGwNewRequestCallback, NULL);
+
+       /* Now we tell the evhttp what port to listen on */
+       http_gw_handle = evhttp_bind_socket_with_handle(http_gw_event, bindaddr.ipv4str(), bindaddr.port());
+       if (!http_gw_handle) {
+               print_error("httpgw: evhttp_bind_socket_with_handle failed");
+               return false;
+       }
+
+       httpgw_chunk_size = chunk_size;
+       httpgw_maxspeed = maxspeed;
+       return true;
+}
+
+
+uint64_t lastoffset=0;
+uint64_t lastcomplete=0;
+tint test_time = 0;
+
+/** For SwarmPlayer 3000's HTTP failover. We should exit if swift isn't
+ * delivering such that the extension can start talking HTTP to the backup.
+ */
+bool HTTPIsSending()
+{
+       if (http_gw_reqs_open > 0)
+       {
+               FileTransfer *ft = FileTransfer::file(http_requests[http_gw_reqs_open-1].transfer);
+               if (ft != NULL) {
+                       fprintf(stderr,"httpgw: upload %lf\n",ft->GetCurrentSpeed(DDIR_UPLOAD)/1024.0);
+                       fprintf(stderr,"httpgw: dwload %lf\n",ft->GetCurrentSpeed(DDIR_DOWNLOAD)/1024.0);
+                       fprintf(stderr,"httpgw: seqcmp %llu\n", swift::SeqComplete(http_requests[http_gw_reqs_open-1].transfer));
+               }
+       }
+    return true;
+
+    // TODO: reactivate when used in SwiftTransport / SwarmPlayer 3000.
+
+       if (test_time == 0)
+       {
+               test_time = NOW;
+               return true;
+       }
+
+       if (NOW > test_time+5*1000*1000)
+       {
+               fprintf(stderr,"http alive: httpc count is %d\n", http_gw_reqs_open );
+
+               if (http_gw_reqs_open == 0 && !sawhttpconn)
+               {
+                       fprintf(stderr,"http alive: no HTTP activity ever, quiting\n");
+                       return false;
+               }
+               else
+                       sawhttpconn = true;
+
+           for (int httpc=0; httpc<http_gw_reqs_open; httpc++)
+           {
+
+               /*
+               if (http_requests[httpc].offset >= 100000)
+               {
+                       fprintf(stderr,"http alive: 100K sent, quit\n");
+                               return false;
+               }
+               else
+               {
+                       fprintf(stderr,"http alive: sent %lli\n", http_requests[httpc].offset );
+                       return true;
+               }
+               */
+
+                       // If
+                       // a. don't know anything about content (i.e., size still 0) or
+                       // b. not sending to HTTP client and not at end, and
+                       //    not downloading from P2P and not at end
+                       // then stop.
+                       if ( swift::Size(http_requests[httpc].transfer) == 0 || \
+                                (http_requests[httpc].offset == lastoffset &&
+                                http_requests[httpc].offset != swift::Size(http_requests[httpc].transfer) && \
+                            swift::Complete(http_requests[httpc].transfer) == lastcomplete && \
+                            swift::Complete(http_requests[httpc].transfer) != swift::Size(http_requests[httpc].transfer)))
+                       {
+                               fprintf(stderr,"http alive: no progress, quiting\n");
+                               //getchar();
+                               return false;
+                       }
+
+                       /*
+                       if (http_requests[httpc].offset == swift::Size(http_requests[httpc].transfer))
+                       {
+                               // TODO: seed for a while.
+                               fprintf(stderr,"http alive: data delivered to client, quiting\n");
+                               return false;
+                       }
+                       */
+
+                       lastoffset = http_requests[httpc].offset;
+                       lastcomplete = swift::Complete(http_requests[httpc].transfer);
+           }
+               test_time = NOW;
+
+           return true;
+       }
+       else
+               return true;
+}
diff --git a/src/libswift_udp/mfold/bash_profile b/src/libswift_udp/mfold/bash_profile
new file mode 100644 (file)
index 0000000..50f96e6
--- /dev/null
@@ -0,0 +1,4 @@
+export PATH=$HOME/bin:$PATH
+export CPPPATH=$CPPPATH:$HOME/include
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/lib
+export LIBPATH=$LD_LIBRARY_PATH
diff --git a/src/libswift_udp/mfold/build.default.sh b/src/libswift_udp/mfold/build.default.sh
new file mode 100644 (file)
index 0000000..3c22054
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+if [ -e ~/.building_swift ]; then
+    exit 0
+fi
+
+touch ~/.building_swift
+
+if ! which git || ! which g++ || ! which scons || ! which make ; then
+    sudo apt-get -y install make g++ scons git-core || exit 1
+fi
+
+if [ ! -e ~/include/event.h ]; then
+    echo installing libevent
+    mkdir tmp
+    cd tmp || exit 2
+    wget -c http://monkey.org/~provos/libevent-2.0.7-rc.tar.gz || exit 3
+    rm -rf libevent-2.0.7-rc
+    tar -xzf libevent-2.0.7-rc.tar.gz || exit 4
+    cd libevent-2.0.7-rc/ || exit 5
+    ./configure --prefix=$HOME || exit 6
+    make || exit 7
+    make install || exit 8
+    cd ~/
+    echo done libevent
+fi
+
+if [ ! -e ~/include/gtest/gtest.h ]; then
+    echo installing gtest
+    mkdir tmp
+    cd tmp || exit 9
+    wget -c http://googletest.googlecode.com/files/gtest-1.4.0.tar.bz2 || exit 10 
+    rm -rf gtest-1.4.0
+    tar -xjf gtest-1.4.0.tar.bz2 || exit 11
+    cd gtest-1.4.0 || exit 12
+    ./configure --prefix=$HOME || exit 13
+    make || exit 14
+    make install || exit 15
+    cd ~/
+    echo done gtest
+fi
+
+#if ! which pcregrep ; then
+#    echo installing pcregrep
+#    mkdir tmp
+#    cd tmp
+#    wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.01.tar.gz || exit 5
+#    tar -xzf pcre-8.01.tar.gz 
+#    cd pcre-8.01
+#    ./configure --prefix=$HOME || exit 6
+#    make -j4 || exit 7
+#    make install || exit 8
+#    echo done pcregrep
+#fi
+
+if [ ! -e swift ]; then
+    echo clone the repo
+    git clone $ORIGIN || exit 16
+fi
+cd swift
+echo switching the branch
+git checkout $BRANCH || exit 17
+echo pulling updates
+git pull origin $BRANCH:$BRANCH || exit 18
+
+echo building
+INCL=~/include LIB=~/lib
+CPPPATH=$INCL LIBPATH=$LIB scons -j4 || exit 19
+echo testing
+LD_LIBRARY_PATH=$LIB tests/connecttest || exit 20
+
+# TODO: one method
+mv bingrep.cpp ext/
+if [ ! -e bin ]; then mkdir bin; fi
+g++ -I. -I$INCL *.cpp ext/seq_picker.cpp -pg -o bin/swift-pg -L$LIB -levent &
+g++ -I. -I$INCL *.cpp ext/seq_picker.cpp -g -o bin/swift-dbg -L$LIB -levent &
+g++ -I. -I$INCL *.cpp ext/seq_picker.cpp -O2 -o bin/swift-o2 -L$LIB -levent &
+wait
+
+rm ~/.building_swift
+
+echo done
diff --git a/src/libswift_udp/mfold/clean.default.sh b/src/libswift_udp/mfold/clean.default.sh
new file mode 100644 (file)
index 0000000..fdd507c
--- /dev/null
@@ -0,0 +1,10 @@
+#if [ $EMIF ]; then
+#    sudo tc qdisc del dev $EMIF ingress
+#    sudo tc qdisc del dev ifb0 root
+#fi
+#sudo iptables -F &
+cd swift
+rm -rf *chunk core *harvest ~/.building_swift ~/.dohrv_copying
+killall swift-o2
+killall swift-dbg
+echo DONE
diff --git a/src/libswift_udp/mfold/cleanup.default.sh b/src/libswift_udp/mfold/cleanup.default.sh
new file mode 100644 (file)
index 0000000..7fabe3e
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+killall -q leecher || true
diff --git a/src/libswift_udp/mfold/cleanup.node300.das2.ewi.tudelft.nl.sh b/src/libswift_udp/mfold/cleanup.node300.das2.ewi.tudelft.nl.sh
new file mode 100644 (file)
index 0000000..398f4bb
--- /dev/null
@@ -0,0 +1 @@
+killall seeder || true
diff --git a/src/libswift_udp/mfold/compile.default.sh b/src/libswift_udp/mfold/compile.default.sh
new file mode 100644 (file)
index 0000000..45f999a
--- /dev/null
@@ -0,0 +1,20 @@
+if [ -e ~/.building_swift ]; then
+    exit 0
+fi
+
+touch ~/.building_swift
+
+cd swift || exit 1
+if [ ! -d bin ]; then mkdir bin; fi
+git pull origin $BRANCH:$BRANCH || exit 2
+rm bin/swift-pg bin/swift-o3 bin/swift-dbg
+
+g++ -I. *.cpp ext/seq_picker.cpp -pg -o bin/swift-pg &
+g++ -I. *.cpp ext/seq_picker.cpp -g -o bin/swift-dbg &
+g++ -I. *.cpp ext/seq_picker.cpp -O3 -o bin/swift-o3 &
+wait
+if [ ! -e bin/swift-pg ]; then exit 4; fi
+if [ ! -e bin/swift-dbg ]; then exit 5; fi
+if [ ! -e bin/swift-o3 ]; then exit 6; fi
+
+rm ~/.building_swift
diff --git a/src/libswift_udp/mfold/das2.txt b/src/libswift_udp/mfold/das2.txt
new file mode 100644 (file)
index 0000000..b4e3c88
--- /dev/null
@@ -0,0 +1,27 @@
+130.161.211.200        node300
+130.161.211.201        node301
+130.161.211.202        node302
+130.161.211.203        node303
+130.161.211.204        node304
+130.161.211.205        node305
+130.161.211.206        node306
+130.161.211.208        node308
+130.161.211.209        node309
+130.161.211.210        node310
+130.161.211.212        node312
+130.161.211.213        node313
+130.161.211.214        node314
+130.161.211.215        node315
+130.161.211.217        node317
+130.161.211.219        node319
+130.161.211.220        node320
+130.161.211.222        node322
+130.161.211.223        node323
+130.161.211.224        node324
+130.161.211.225        node325
+130.161.211.226        node326
+130.161.211.227        node327
+130.161.211.228        node328
+130.161.211.229        node329
+130.161.211.230        node330
+130.161.211.231        node331
diff --git a/src/libswift_udp/mfold/do-harvest.sh b/src/libswift_udp/mfold/do-harvest.sh
new file mode 100644 (file)
index 0000000..7060705
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+# This script executes a chain of commands
+# on all the member servers, in parallel.
+# Commands are defined in .sh files (see
+# docmd.sh); all failed executions are
+# put to the FAILURES file
+rm -f FAILURES
+
+if [ -z "$SERVERS" ]; then
+    SERVERS="das2.txt"
+fi
+HOSTS=`cat $SERVERS | awk '{print $1}'`
+
+for srv in $HOSTS; do
+    ( for cmd in $@; do
+        if ! ./docmd.sh $srv $cmd; then
+            echo $srv >> FAILURES
+            echo $src FAILED
+            break
+        fi
+    done ) &
+done
+
+wait
+echo DONE
diff --git a/src/libswift_udp/mfold/doall b/src/libswift_udp/mfold/doall
new file mode 100755 (executable)
index 0000000..dd3a7d1
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+# This script executes a chain of commands
+# on all the member servers, in parallel.
+# Commands are defined in .sh files (see
+# docmd.sh); all failed executions are
+# put to the FAILURES file
+rm -f FAILURES
+if [ ! -d logs ]; then
+    mkdir logs
+fi
+
+if [ -z "$SERVERS" ]; then
+    SERVERS="servers.txt"
+fi
+
+
+# Line format in $SERVERS: <hostname>:<port number>
+for srvstr in `grep -v '^#' $SERVERS`; do
+    (
+       srv=${srvstr%:*}
+       port=${srvstr#*:}
+       if [[ $port  &&  $srv == $port ]]; then
+           port=
+       fi
+        if ! ./docmd $srv $1 $port; then
+            echo $srv >> FAILURES
+            echo $srv FAILED
+            break
+        fi
+    ) &
+done
+
+wait
+echo DONE
diff --git a/src/libswift_udp/mfold/docmd b/src/libswift_udp/mfold/docmd
new file mode 100755 (executable)
index 0000000..8f72216
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+HOST=$1
+CMD=$2
+PORT=$3
+ENV=env.default.sh
+
+if [ -e env.$HOST.sh ]; then
+    ENV="$ENV env.$HOST.sh"
+fi
+
+if [ -e $CMD.$HOST.sh ] ; then 
+    SHSC=$CMD.$HOST.sh ;
+else 
+    SHSC=$CMD.default.sh ;
+fi
+
+ENVSTR="HOST=$HOST"
+
+if [ $PORT ]; then
+    ENVSTR=$ENVSTR"; export SWFTPORT=$PORT"
+fi
+
+if [ ! -d logs ]; then mkdir logs; fi
+if [ ! -e $SHSC ]; then
+    echo $HOST  $CMD    EMPTY
+    exit 0
+fi
+
+if ( (cat $ENV; echo $ENVSTR; cat $SHSC) | ssh -T $HOST ) > \
+    logs/$HOST.$CMD.out 2> logs/$HOST.$CMD.err; then
+    echo $HOST  $CMD    OK
+    exit 0
+else
+    echo $HOST  $CMD    FAIL
+    cat $SHSC
+    cat logs/$HOST.$CMD.out logs/$HOST.$CMD.err
+    exit 1
+fi
diff --git a/src/libswift_udp/mfold/dohrv b/src/libswift_udp/mfold/dohrv
new file mode 100755 (executable)
index 0000000..c0b37c2
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/bash
+#   The script downloads logs in parallel,
+#   feeds them into fifos; sort takes logs
+#   from fifos, merges and gzips them;
+#   the result is put into harvest/
+#
+if [ ! $SERVERS ]; then
+    export SERVERS="servers.txt"
+fi
+
+. env.default.sh
+
+if [ ! $TMPDIR ]; then
+    TMPDIR=/tmp
+fi
+
+if [ ! $MAXHARVEST ]; then
+    MAXHARVEST=100
+fi
+
+mv harvest .hrv-old
+rm -rf .hrv-old &
+mkdir harvest
+
+i=0
+j=1
+for sstr in `grep -v '#' $SERVERS`; do
+    s=${sstr%:*}
+    mkfifo harvest/$s-$j.fifo
+    # yas, yes, yes
+    (
+       if ssh $s \
+           "if [ -e ~/.dohrv_copying ]; then exit 1; \
+            else touch ~/.dohrv_copying; fi" ; then
+           scp ~/.ssh/config $s:.ssh/config > /dev/null 
+           scp $SERVERS $s:swift/mfold/servers.txt > /dev/null
+           ssh $s "rm -f ~/.dohrv_copying"
+       fi
+    ) &
+    let i++
+    if [ $i == $MAXHARVEST ]; then
+       wait
+       i=0
+       mkfifo harvest/swpart$j.fifo
+       let j++
+    fi
+done
+if [[ $i>0 ]]; then
+    wait
+    mkfifo harvest/swpart$j.fifo
+fi
+
+echo 'Done making fifos and copying configs.'
+
+i=0
+j=1
+for sstr in `grep -v '#' $SERVERS`; do
+    s=${sstr%:*}
+    (
+       if ssh $s \
+            "cd swift/  && \
+        rm -rf $s-harvest && mkdir $s-harvest && \
+        ( zcat $s-lout.gz | ./mfold/logparse $s | gzip )" \
+            | gunzip > harvest/$s-$j.fifo ; then
+
+            ssh $s "cd swift/; tar cz $s-harvest" | tar xz
+           mv $s-harvest/* harvest/
+           rmdir $s-harvest
+            echo $s harvest OK
+           
+       else
+            echo $s harvest FAIL
+       fi
+    ) &
+    let i++
+    if [ $i == $MAXHARVEST ]; then
+       # Ensure your version of sort is recent enough
+       # batch-size is critical for performance
+       LC_ALL=C sort -m -s -T $TMPDIR --batch-size=64 --compress-program=gzip \
+           harvest/*-$j.fifo | gzip > harvest/swpart$j.log.gz &
+       wait
+       i=0
+       let j++
+    fi
+done  
+if [[ $i>0 ]]; then
+    LC_ALL=C sort -m -s -T $TMPDIR --batch-size=64 --compress-program=gzip \
+       harvest/*-$j.fifo | gzip > harvest/swpart$j.log.gz &
+    wait
+    let j++
+fi
+
+echo 'Done sorting of swarm parts.'
+
+if [[ $j>2 ]]; then
+    for (( i=1; i<j; i++ )); do
+       (zcat harvest/swpart$i.log.gz > harvest/swpart$i.fifo) &
+    done
+    LC_ALL=C sort -m -s -T $TMPDIR --batch-size=64 --compress-program=gzip \
+       harvest/swpart*.fifo | gzip > harvest/swarm.log.gz &
+    wait
+else
+    mv harvest/swpart1.log.gz harvest/swarm.log.gz
+fi
+
+echo 'Done sorting of whole swarm.'
+
+rm harvest/*.fifo
+rm harvest/swpart*.log.gz
+./loggraphs
+./logreport > harvest/index.html
+#./logdistr
+
+cp report.css harvest
+# scp -rq harvest mfold.libswift.org:/storage/mfold-granary/`date +%d%b_%H:%M`_`whoami` &
+
+echo DONE
diff --git a/src/libswift_udp/mfold/dotop b/src/libswift_udp/mfold/dotop
new file mode 100755 (executable)
index 0000000..33a6c36
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+while true; do
+    rm logs/*status.out
+    ( ./doall status > /dev/null ) &
+    wait
+    clear
+    cat logs/*status.out | sort
+done
diff --git a/src/libswift_udp/mfold/env.1mbit.sh b/src/libswift_udp/mfold/env.1mbit.sh
new file mode 100644 (file)
index 0000000..11c4ece
--- /dev/null
@@ -0,0 +1,4 @@
+EMIF=eth0
+EMBW=1mbit
+EMDELAY=50ms
+
diff --git a/src/libswift_udp/mfold/env.default.sh b/src/libswift_udp/mfold/env.default.sh
new file mode 100644 (file)
index 0000000..797c2f7
--- /dev/null
@@ -0,0 +1,19 @@
+# This script sets up shared environment variables
+# at the servers and during harvesting
+export SEEDER=130.161.211.198
+# SEEDERPORT must match with the port number on seeder line in
+# $SERVERS
+export SEEDERPORT=10004
+export HASH=66b9644bb01eaad09269354df00172c8a924773b
+export BRANCH=master
+export ORIGIN=git://github.com/gritzko/swift.git
+# Temporary directory for sort (run by dohrv)
+export TMPDIR=/home/jori/tmp
+# Maximum number of peers to be parsed in parallel by dohrv
+export MAXHARVEST=200
+# Maximum number of gnuplots to be run in parallel by loggraphs
+export MAXGNUPLOTS=50
+# General HTB and Netem parameters. Overdriven by env.<hostname>.sh
+EMIF=eth0
+EMBW=10Mbit
+EMDELAY=10ms
diff --git a/src/libswift_udp/mfold/env.lossy.sh b/src/libswift_udp/mfold/env.lossy.sh
new file mode 100644 (file)
index 0000000..1e49d1f
--- /dev/null
@@ -0,0 +1,4 @@
+EMIF=eth0
+EMBW=1mbit
+EMDELAY=100ms
+EMLOSS=5.0%
diff --git a/src/libswift_udp/mfold/env.messy.sh b/src/libswift_udp/mfold/env.messy.sh
new file mode 100644 (file)
index 0000000..c75f27e
--- /dev/null
@@ -0,0 +1,5 @@
+EMIF=eth0
+EMBW=1mbit
+EMDELAY=100ms
+EMJTTR=20ms
+
diff --git a/src/libswift_udp/mfold/hosts.txt b/src/libswift_udp/mfold/hosts.txt
new file mode 100644 (file)
index 0000000..b4e3c88
--- /dev/null
@@ -0,0 +1,27 @@
+130.161.211.200        node300
+130.161.211.201        node301
+130.161.211.202        node302
+130.161.211.203        node303
+130.161.211.204        node304
+130.161.211.205        node305
+130.161.211.206        node306
+130.161.211.208        node308
+130.161.211.209        node309
+130.161.211.210        node310
+130.161.211.212        node312
+130.161.211.213        node313
+130.161.211.214        node314
+130.161.211.215        node315
+130.161.211.217        node317
+130.161.211.219        node319
+130.161.211.220        node320
+130.161.211.222        node322
+130.161.211.223        node323
+130.161.211.224        node324
+130.161.211.225        node325
+130.161.211.226        node326
+130.161.211.227        node327
+130.161.211.228        node328
+130.161.211.229        node329
+130.161.211.230        node330
+130.161.211.231        node331
diff --git a/src/libswift_udp/mfold/install.default.sh b/src/libswift_udp/mfold/install.default.sh
new file mode 100644 (file)
index 0000000..483708e
--- /dev/null
@@ -0,0 +1 @@
+echo TODO
diff --git a/src/libswift_udp/mfold/loggraphs b/src/libswift_udp/mfold/loggraphs
new file mode 100755 (executable)
index 0000000..613b8af
--- /dev/null
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+if [ -z "$SERVERS" ]; then
+    SERVERS="servers.txt"
+fi
+
+. env.default.sh
+
+if [ ! $MAXGNUPLOTS ]; then
+    MAXGNUPLOTS=200
+fi
+
+VERSION=`date`,`git log --summary | head -1`
+
+cd harvest
+
+# HEAD=`head -1 *.log | grep -v '^$' | cut -f1 | sort | head -1`
+# TAIL=`tail -n1 -q *.log | grep -v '^$' | cut -f1 | sort | tail -n1 -q`
+
+i=0
+for fromstr in `grep -v '^#' ../$SERVERS`; do
+    from=${fromstr%:*}
+    for tostr in `grep -v '^#' ../$SERVERS`; do
+       to=${tostr%:*}
+        CWNDLOG="$from-$to-cwnd.log"
+        if [ ! -e $CWNDLOG ]; then
+            continue
+        fi
+        GP="$from-$to.gnuplot"
+
+        echo "set term png large size 2048,768" > $GP
+        PNG="$from-$to.big.png"
+        if [ -e $PNG ]; then rm $PNG; fi
+        echo "set out '$PNG'" >> $GP
+
+        echo "set y2tics" >> $GP
+        echo "set y2label 'packets'" >> $GP
+        echo "set ylabel 'microseconds'" >> $GP
+        echo "set xlabel 'run time millis'" >> $GP
+        echo "set title '$VERSION'" >> $GP
+        #echo "set xrange [$HEAD:$TAIL]" >> $GP
+        CWNDLOG="$from-$to-cwnd.log"
+        echo -ne "plot '$CWNDLOG' using 1:2 with lines lt rgb '#00aa00' title 'cwnd'"\
+                " axis x1y2, "\
+                " '$CWNDLOG' using 1:3 with lines lt rgb '#99ff99'  title 'data out'"\
+                " axis x1y2 "\
+                >> $GP
+        RTTLOG="$from-$to-rtt.log"
+        if [ -e $RTTLOG ]; then
+            echo -ne ", '$RTTLOG' using 1:2 with lines lt rgb '#2833ff'  title 'rtt' "\
+                "axis x1y1, "\
+                "'$RTTLOG' using 1:3 with lines lt rgb '#8844ff' title 'dev' "\
+                "axis x1y1"\
+                >> $GP
+        fi
+        OWDLOG="$from-$to-owd.log"
+        if [ -e $OWDLOG ]; then
+            echo -ne ", '$OWDLOG' using 1:2 with lines lt rgb '#ff00ee' title 'owd' "\
+                "axis x1y1, "\
+                "'$OWDLOG' using 1:3 with lines lw 2 lt rgb '#0044cc'  title 'min owd'"\
+                "axis x1y1, "\
+                "'$OWDLOG' using 1:(\$3+25000) with lines lw 2 lt rgb '#0000ff' title 'target'"\
+                "axis x1y1 "\
+                >> $GP
+        fi
+        RDATALOG="$from-$to-rdata.log"
+        if [ -e $RDATALOG ]; then
+            echo -ne ", '$RDATALOG' using 1:(1) with points "\
+                "lt rgb '#0f0000' title 'r-losses'"\
+                >> $GP
+        fi
+        TDATALOG="$from-$to-tdata.log"
+        if [ -e $TDATALOG ]; then
+            echo -ne ", '$TDATALOG' using 1:(1) with points "\
+                "lt rgb '#ff0000' title 't-losses'"\
+                >> $GP
+        fi
+        echo  >> $GP
+
+        echo "set term png size 512,192" >> $GP
+        PNG="$from-$to.thumb.png"
+        if [ -e $PNG ]; then rm $PNG; fi
+        echo "set out '$PNG'" >> $GP
+        echo "unset title" >> $GP
+        echo "unset xlabel" >> $GP
+        echo "unset ylabel" >> $GP
+        echo "replot" >> $GP
+
+        ( cat $GP | gnuplot ) &
+       let i++
+       if [ $i == $MAXGNUPLOTS ]; then
+           wait
+           i=0
+       fi
+    done
+done
+
+wait
+cd ..
diff --git a/src/libswift_udp/mfold/logparse b/src/libswift_udp/mfold/logparse
new file mode 100755 (executable)
index 0000000..03c4d86
--- /dev/null
@@ -0,0 +1,185 @@
+#!/usr/bin/perl -w
+
+$SERVER=shift;
+%PORTS = ();
+%HOSTS = ();
+%CHANN = ( "#0" => "none" );
+%EVENTS = ( "#0" => {"+hs"=>0} );
+%SENT = ();
+%RCVD = ();
+%DSENT = ();
+%DRCVD = ();
+%CWNDLOG = ();
+%RTTLOG = ();
+%OWDLOG = ();
+%TDATALOG = ();
+%RDATALOG = ();
+%INDATALOG = ();
+$SENTB = 0;
+$RCVDB = 0;
+
+open(SRVP,$ENV{"HOME"}."/swift/mfold/servers.txt") or die;
+while (<SRVP>) {
+    /(\S+):(\d+)/ or next;
+    $PORTS{$1} = $2;
+}
+close SRVP;
+
+open(SRV,$ENV{"HOME"}."/.ssh/config") or die;
+while (<SRV>) {
+    if (/Host (\S+)/) {
+       $srvname=$1;
+       $port = $PORTS{$srvname};
+    }
+    $HOSTS{$1}{$port}=$srvname if /HostName (\S+)/ && $port;
+}
+close SRV;
+
+while (<>) {
+    /(\d+_\d+_\d+_\d+_\d+) (#\d+) (\S+) (.*)/ or next;
+    my $time = $1;
+    my $channel = $2;
+    my $event = $3;
+    my $rest = $4;
+    my $host = $CHANN{"$channel"};
+    $host = "unknown" if not $host;
+    $time =~ /^(\d+)_(\d+)_(\d+)_(\d+)/;
+    my $ms=$1*60; $ms=($ms+$2)*60; $ms=($ms+$3)*1000; $ms+=$4;
+    if ($event eq "sent") {
+        $rest =~ /(\d+)b ([\d\.]+):(\d+):/;
+        $ip = $2;
+       $port = $3;
+       $host = $HOSTS{$ip}{$port};
+        #$SENT{$h} = 0 if not exists $SENT{$h};
+        $SENT{$host} += $1;
+        $SENTB += $1;
+        $DSENT{$host}++;
+        $CHANN{"$channel"} = $host;
+    } elsif ($event eq "recvd") {
+        $rest =~ /(\d+)/;
+        #$RCVD{$h} = 0 if not exists $RCVD{$h};
+        $DRCVD{$host}++;
+        $RCVD{$host} += $1;
+        $RCVDB += $1;
+    } elsif ($event eq "sendctrl") {
+        if ($rest =~ /cwnd (\d+\.\d+).*data_out (\d+)/) {
+            if (not exists $CWNDLOG{$host}) {
+                open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-cwnd.log") or die;
+                $CWNDLOG{$host} = $handle;
+            }
+            print {$CWNDLOG{$host}} "$ms\t$1\t$2\n";
+        } elsif ($rest =~ /ledbat (\-?\d+)\-(\-?\d+)/) {
+            if (not exists $OWDLOG{$host}) {
+                open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-owd.log") or die;
+                $OWDLOG{$host} = $handle;
+            }
+            print {$OWDLOG{$host}} "$ms\t$1\t$2\n";
+        } elsif ($rest =~ /rtt (\d+) dev (\d+)/) {
+            if (not exists $RTTLOG{$host}) {
+                open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-rtt.log") or die;
+                $RTTLOG{$host} = $handle;
+            }
+            print {$RTTLOG{$host}} "$ms\t$1\t$2\n";
+        }
+    } elsif ($event eq "Tdata") {
+        if (not exists $TDATALOG{$host}) {
+            open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-tdata.log") or die;
+            $TDATALOG{$host} = $handle;
+        }
+        print {$TDATALOG{$host}} "$ms\n";
+    } elsif ($event eq "Rdata") {
+        if (not exists $RDATALOG{$host}) {
+            open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-rdata.log") or die;
+            $RDATALOG{$host} = $handle;
+        }
+        print {$RDATALOG{$host}} "$ms\n";
+    } elsif ($event eq "-data" && $rest =~ /\(0,(\d+)\)/) {
+       my $bin = $1;
+       if (not exists $INDATALOG{$host}) {
+            open(my $handle, '>', "$SERVER-harvest/$SERVER-$host-indata.log") or die;
+            $INDATALOG{$host} = $handle;
+       }
+       print {$INDATALOG{$host}} "$bin\t$ms\n";
+    }
+    $EVENTS{"$host"} = { "+hs"=>0 } if not exists $EVENTS{"$host"};
+    
+    print "$time $SERVER $host$channel $event $rest\n";
+
+    # DO STATS
+    $EVENTS{"$host"}{"$event"} = 0 if not exists $EVENTS{"$host"}{"$event"};
+    $EVENTS{"$host"}{"$event"}++;
+
+}
+
+for $host (keys %CWNDLOG) {
+    close($CWNDLOG{$host});
+}
+for $host (keys %OWDLOG) {
+    close ($OWDLOG{$host});
+}
+for $host (keys %RTTLOG) {
+    close ($RTTLOG{$host});
+}
+for $host (keys %TDATALOG) {
+    close ($TDATALOG{$host});
+}
+for $host (keys %RDATALOG) {
+    close ($RDATALOG{$host});
+}
+for $host (keys %INDATALOG) {
+    close ($INDATALOG{$host});
+}
+
+open(LEGEND,"> $SERVER-harvest/$SERVER-legend.txt") or die;
+
+for $channel (keys %CHANN) {
+    my $host = $CHANN{"$channel"};
+    print LEGEND "$channel\t$host\n";
+    open(STATS,"> $SERVER-harvest/$SERVER-$host.stat") or die;
+    my %events = %{ $EVENTS{"$host"} };
+    for $event ( keys %events ) {
+        print STATS "$event\t".($events{"$event"})."\n";
+    }
+    close STATS;
+    open(HTML,"> $SERVER-harvest/$SERVER-$host.html") or die;
+    print HTML "<table class='channel'><th><td>sent</td><td>rcvd</td></th>\n";
+    my $rcvd = $RCVD{$host};
+    my $sent = $SENT{$host};
+    $rcvd=0.001 if not $rcvd;
+    $sent=0.001 if not $sent;
+    printf HTML 
+        "<tr class='bytes'><td>bytes</td><td>%i/<pp>%.1f%%</pp></td>".
+        "<td>%i/<pp>%.1f%%</pp></td></tr>\n",
+        $sent, $SENTB?$sent/$SENTB*100:0, $rcvd, $RCVDB?$rcvd/$RCVDB*100:0;
+    print HTML
+        "<tr><td>dgrams</td><td>".$DSENT{$host}."</td><td>".$DRCVD{$host}."</td></tr>\n";
+    printf HTML
+        "<tr><td>data</td><td>%i/<pp><b>%.1f%%</b></pp></td><td>%i/<pp><b>%.1f%%</b></pp></td></tr>\n",
+        $events{"+data"}, ($events{"+data"}*1029)/$sent*100,
+        $events{"-data"}, ($events{"-data"}*1029)/$rcvd*100;
+    printf HTML
+        "<tr><td>hash</td><td>%i/<pp>%.1f%%</pp></td><td>%i/<pp>%.1f%%</pp></td></tr>\n",
+        $events{"+hash"}, ($events{"+hash"}*25)/$sent*100,
+        $events{"-hash"}, ($events{"-hash"}*25)/$rcvd*100;
+    printf HTML
+        "<tr><td>ack</td><td>%i/<pp>%.1f%%</pp></td><td>%i/<pp>%.1f%%</pp></td></tr>\n",
+        $events{"+ack"}, ($events{"+ack"}*5)/$sent*100,
+        $events{"-ack"}, ($events{"-ack"}*5)/$rcvd*100;
+    printf HTML
+        "<tr><td>hint</td><td>%i/<pp>%.1f%%</pp></td><td>%i/<pp>%.1f%%</pp></td></tr>\n",
+        $events{"+hint"}, ($events{"+hint"}*5)/$sent*100,
+        $events{"-hint"}, ($events{"-hint"}*5)/$rcvd*100;
+    printf HTML
+        "<tr><td>hs</td><td>%i</td><td>%i</td></tr>\n",
+        $events{"+hs"}, $events{"-hs"};
+    my $losses = $events{"+data"}>0 ? 
+        ($events{"Rdata"}+$events{"Tdata"})/$events{"+data"}*100 : 0;
+    printf HTML
+        "<tr><td>losses</td><td colspan='2'>R:%i+T:%i=%i/<pp>%.1f%%</pp></td></tr>\n",
+        $events{"Rdata"}, $events{"Tdata"},
+        $events{"Rdata"}+$events{"Tdata"}, $losses;
+
+    print HTML "</table>\n";
+    close HTML;
+}
+close LEGEND;
diff --git a/src/libswift_udp/mfold/logreport b/src/libswift_udp/mfold/logreport
new file mode 100755 (executable)
index 0000000..47b0a71
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+if [ ! $SERVERS ]; then
+    export SERVERS="servers.txt"
+fi
+
+cd harvest
+
+echo '<html><head>'
+echo '<link rel="stylesheet" href="report.css" type="text/css">'
+echo '<title>Manifold: swarm tomography' `date` `git log --summary | head -1` '</title>'
+echo '</head><body>'
+echo '<table id="main"><th>'
+for tostr in `grep -v '#' ../$SERVERS`; do
+    to=${tostr%:*}
+    echo '<td class="host">&gt;'$to'</td>'
+done
+echo '</th>'
+for fromstr in `grep -v '#' ../$SERVERS`; do
+    from=${fromstr%:*}
+    echo '<tr><td class="host">'$from'&gt;</td>'
+    for tostr in `grep -v '#' ../$SERVERS`; do
+       to=${tostr%:*}
+        echo '<td>'
+        cat $from-$to.html
+        if [ -e "$from-$to.big.png" ]; then 
+            echo "<a href='$from-$to.big.png'>"
+            echo "<img class='thumb' src='$from-$to.thumb.png'/>"
+            echo "</a>"
+        fi
+        echo '</td>'
+    done
+    echo '</tr>'
+done
+echo '<body></html>'
diff --git a/src/libswift_udp/mfold/net.aussie.sh b/src/libswift_udp/mfold/net.aussie.sh
new file mode 100644 (file)
index 0000000..7d0bb29
--- /dev/null
@@ -0,0 +1,3 @@
+echo eth0 > .netem-on
+
+sudo tc qdisc add dev eth0 root netem delay 400ms
diff --git a/src/libswift_udp/mfold/net.lossy.sh b/src/libswift_udp/mfold/net.lossy.sh
new file mode 100644 (file)
index 0000000..8fb9ed8
--- /dev/null
@@ -0,0 +1,3 @@
+echo eth0 > .netem-on
+
+sudo tc qdisc add dev eth0 root netem delay 100ms loss 5.0%
diff --git a/src/libswift_udp/mfold/net.messy.sh b/src/libswift_udp/mfold/net.messy.sh
new file mode 100644 (file)
index 0000000..5cfd8aa
--- /dev/null
@@ -0,0 +1,3 @@
+echo eth0 > .netem-on
+
+sudo tc qdisc add dev eth0 root netem delay 100ms 20ms 25%
diff --git a/src/libswift_udp/mfold/netclean.default.sh b/src/libswift_udp/mfold/netclean.default.sh
new file mode 100644 (file)
index 0000000..33d08cc
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Cleans configuration made with netem script.
+if [ ! $EMIF ] ; then
+    exit
+fi
+
+if [ ! $SWFTPORT ]; then
+    exit
+fi
+
+TC="sudo tc "
+CLASSID=$(($SWFTPORT - 9900))
+
+echo cleaning filter and class id 1:$CLASSID from ifb0
+$TC filter del dev ifb0 protocol ip prio 1 handle 800::$CLASSID u32 \
+    flowid 1:$CLASSID
+$TC class del dev ifb0 classid 1:$CLASSID
+
+echo cleaning filter and class id 1:$CLASSID from lo
+$TC filter del dev lo protocol ip prio 1 handle 800::$CLASSID u32 \
+    flowid 1:$CLASSID
+$TC class del dev lo classid 1:$CLASSID
+
+echo cleaning filter and class id 1:$CLASSID from $EMIF
+$TC filter del dev $EMIF protocol ip prio 1 handle 800::$CLASSID u32 \
+    flowid 1:$CLASSID
+$TC class del dev $EMIF classid 1:$CLASSID
diff --git a/src/libswift_udp/mfold/netcleanroot.default.sh b/src/libswift_udp/mfold/netcleanroot.default.sh
new file mode 100644 (file)
index 0000000..42f0c9c
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Cleans configurations made with netroot and netem scripts.
+
+if [ ! $EMIF ] ; then
+    exit
+fi
+
+TC="sudo tc "
+
+echo cleanup
+$TC qdisc del dev $EMIF root
+$TC qdisc del dev $EMIF ingress
+$TC qdisc del dev ifb0 root
+$TC qdisc del dev lo root
+
+exit 0
diff --git a/src/libswift_udp/mfold/netem.default.sh b/src/libswift_udp/mfold/netem.default.sh
new file mode 100755 (executable)
index 0000000..830cc83
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/bash
+# Sets HTB/Netem parameters for the server interfaces. netroot script
+# must be run before this.
+
+if [ ! $EMIF ] ; then
+    exit
+fi
+
+if [ ! $SWFTPORT ]; then
+    echo No swift port defined!
+    exit 1
+fi
+
+if [ ! $EMLOSS ]; then
+    EMLOSS=0%
+fi
+
+if [ ! $EMDELAY ]; then
+    EMDELAY=10ms
+fi
+
+if [ ! $EMBW ]; then
+    EMBW=10mbit
+fi
+
+if [ ! $EMJTTR ]; then
+    EMJTTR=0ms
+fi
+
+# ingress params
+if [ ! $EMLOSS_IN ]; then
+    EMLOSS_IN=$EMLOSS
+fi
+
+if [ ! $EMDELAY_IN ]; then
+    EMDELAY_IN=$EMDELAY
+fi
+
+# zero delay in lo may affect htb performance accuracy (?)
+if [ $EMDELAY_IN == 0ms ]; then
+    EMDELAY_LO_IN=0.1ms
+else
+    EMDELAY_LO_IN=$EMDELAY_IN
+fi
+
+if [ ! $EMBW_IN ]; then
+    EMBW_IN=$EMBW
+fi
+
+if [ ! $EMJTTR_IN ]; then
+    EMJTTR_IN=$EMJTTR
+fi
+
+# egress params
+if [ ! $EMLOSS_OUT ]; then
+    EMLOSS_OUT=$EMLOSS
+fi
+
+if [ ! $EMDELAY_OUT ]; then
+    EMDELAY_OUT=$EMDELAY
+fi
+
+if [ ! $EMBW_OUT ]; then
+    EMBW_OUT=$EMBW
+fi
+
+if [ ! $EMJTTR_OUT ]; then
+    EMJTTR_OUT=$EMJTTR
+fi
+
+TC="sudo tc "
+
+CLASSID=$(($SWFTPORT - 9900))
+HANDLEID=1$CLASSID
+
+# ingress config
+echo adding htb class 1:$CLASSID with rate $EMBW_IN to ifb0
+$TC class add dev ifb0 parent 1: classid 1:$CLASSID htb rate $EMBW_IN || exit 2
+echo adding filter for destination port $SWFTPORT for to ifb0
+$TC filter add dev ifb0 protocol ip prio 1 handle ::$CLASSID u32 \
+    match ip dport $SWFTPORT 0xffff flowid 1:$CLASSID || exit 3
+echo adding downlink netem handle $HANDLEID for $EMDELAY_IN, $EMLOSS_IN to ifb0
+$TC qdisc add dev ifb0 parent 1:$CLASSID handle $HANDLEID \
+    netem delay $EMDELAY_IN $EMJTTR_IN 25% loss $EMLOSS_IN || exit 4
+
+echo adding htb class 1:$CLASSID with rate $EMBW_IN to lo
+$TC class add dev lo parent 1: classid 1:$CLASSID htb rate $EMBW_IN || exit 5
+echo adding filter for destination port $SWFTPORT for to lo
+$TC filter add dev lo protocol ip prio 1 handle ::$CLASSID u32 \
+    match ip dport $SWFTPORT 0xffff flowid 1:$CLASSID || exit 6
+echo adding downlink netem handle $HANDLEID for $EMDELAY_LO_IN, $EMLOSS_IN to lo
+$TC qdisc add dev lo parent 1:$CLASSID handle $HANDLEID \
+    netem delay $EMDELAY_LO_IN $EMJTTR_IN 25% loss $EMLOSS_IN || exit 7
+
+#egress config
+echo adding htb class 1:$CLASSID with rate $EMBW_OUT to $EMIF
+$TC class add dev $EMIF parent 1: classid 1:$CLASSID htb rate $EMBW_OUT || exit 8
+echo adding filter for source port $SWFTPORT for to $EMIF
+$TC filter add dev $EMIF protocol ip prio 1 handle ::$CLASSID u32 \
+    match ip sport $SWFTPORT 0xffff flowid 1:$CLASSID || exit 9
+echo adding uplink netem handle $HANDLEID for $EMDELAY_OUT, $EMLOSS_OUT to $EMIF
+$TC qdisc add dev $EMIF parent 1:$CLASSID handle $HANDLEID \
+    netem delay $EMDELAY_OUT $EMJTTR_OUT 25% loss $EMLOSS_OUT || exit 10
diff --git a/src/libswift_udp/mfold/netroot.default.sh b/src/libswift_udp/mfold/netroot.default.sh
new file mode 100644 (file)
index 0000000..1c39aa8
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+if [ ! $EMIF ] ; then
+    exit
+fi
+
+TC="sudo tc "
+
+# echo cleanup
+# $TC qdisc del dev $EMIF root
+# $TC qdisc del dev $EMIF ingress
+# $TC qdisc del dev ifb0 root
+
+echo ifb0 up
+sudo modprobe ifb
+sudo ip link set dev ifb0 up
+
+echo set lo mtu to 1500
+sudo ifconfig lo mtu 1500 || exit 1
+# Should return OK, when using multiple peers in same host
+echo adding ingress
+$TC qdisc add dev $EMIF ingress || exit 0
+
+echo redirecting to ifb
+$TC filter add dev $EMIF parent ffff: protocol ip prio 1 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0 || exit 3
+
+echo adding ifb0 root htb
+$TC qdisc add dev ifb0 handle 1: root htb || exit 4
+
+echo adding $EMIF root htb
+$TC qdisc add dev $EMIF handle 1: root htb || exit 5
+
+echo adding lo root htb
+$TC qdisc add dev lo handle 1: root htb || exit 6
diff --git a/src/libswift_udp/mfold/ps.default.sh b/src/libswift_udp/mfold/ps.default.sh
new file mode 100644 (file)
index 0000000..42cb6d5
--- /dev/null
@@ -0,0 +1,4 @@
+if ps -ef | grep l[e]echer > /dev/null; then
+    echo `hostname` has a running leecher
+    return 1
+fi
diff --git a/src/libswift_udp/mfold/report.css b/src/libswift_udp/mfold/report.css
new file mode 100644 (file)
index 0000000..5e13963
--- /dev/null
@@ -0,0 +1,35 @@
+table#main table {
+    background: #ffe;
+}
+
+td.host {
+    text-align: center;
+    font: 24pt "Courier";
+}
+
+table.channel {
+/*    border: 1 dotted #aa5; */
+    font-size: smaller;
+}
+
+td {
+    border-top: 1 dotted #aa5;
+    border-spacing: 0;
+}
+
+pp {
+    color: #a00;
+}
+
+tr.bytes {
+    background: #fed;
+}
+
+img.thumb {
+    width: 160pt;
+    border-style: none;
+}
+
+table#main tr td {
+    vertical-align: top;
+}
diff --git a/src/libswift_udp/mfold/run.default.sh b/src/libswift_udp/mfold/run.default.sh
new file mode 100644 (file)
index 0000000..f35a282
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+# This script runs a leecher at some server;
+# env variables are set in env.default.sh
+
+export LD_LIBRARY_PATH=$HOME/lib
+
+ulimit -c 1024000
+cd swift || exit 1
+rm -f core
+rm -f $HOST-chunk
+sleep $(( $RANDOM % 5 ))
+bin/swift-o2 -w -h $HASH -f $HOST-chunk -t $SEEDER:$SEEDERPORT \
+    -l 0.0.0.0:$SWFTPORT -p -D 2>$HOST-lerr | gzip > $HOST-lout.gz || exit 2
diff --git a/src/libswift_udp/mfold/run.node300.das2.ewi.tudelft.nl.sh b/src/libswift_udp/mfold/run.node300.das2.ewi.tudelft.nl.sh
new file mode 100644 (file)
index 0000000..965c68d
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+ulimit -c 1024000
+cd swift || exit 2
+#wget -c http://video.ted.com/talks/podcast/ScottKim_2008P.mp4 || exit 1
+
+./exec/seeder ScottKim_2008P.mp4 0.0.0.0:20000 >lout 2> lerr
diff --git a/src/libswift_udp/mfold/run.seeder.sh b/src/libswift_udp/mfold/run.seeder.sh
new file mode 100644 (file)
index 0000000..941aebf
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+export LD_LIBRARY_PATH=$HOME/lib
+
+ulimit -c 1024000
+cd swift || exit 2
+if [ ! -e ScottKim_2008P.mp4 ]; then
+    wget -c http://video.ted.com/talks/podcast/ScottKim_2008P.mp4 || exit 1
+fi
+
+bin/swift-o2 -w -f ScottKim_2008P.mp4 -p -D \
+    -l 0.0.0.0:$SEEDERPORT 2>$HOST-lerr | gzip > $HOST-lout.gz || exit 2
+exit
diff --git a/src/libswift_udp/mfold/servers.txt b/src/libswift_udp/mfold/servers.txt
new file mode 100644 (file)
index 0000000..8065016
--- /dev/null
@@ -0,0 +1,43 @@
+node300
+aussie
+lossy
+messy
+node304
+node305
+node306
+node308
+node309
+node310
+node312
+node313
+node314
+node315
+node317
+node319
+node320
+node322
+node323
+node324
+node325
+node326
+node327
+node328
+node329
+node330
+node331
+vtt1
+vtt2
+vtt3
+vtt4
+vtt5
+vtt6
+vtt7
+vtt8
+vtt9
+vttA
+vttB
+vttC
+vttD
+canada
+media
+itanium
diff --git a/src/libswift_udp/mfold/ssh-config b/src/libswift_udp/mfold/ssh-config
new file mode 100644 (file)
index 0000000..c8e345d
--- /dev/null
@@ -0,0 +1,129 @@
+Host node300
+HostName 130.161.211.200
+
+Host aussie 
+HostName 130.161.211.201
+
+Host lossy
+HostName 130.161.211.202
+
+Host messy
+HostName 130.161.211.203
+
+Host node304
+HostName 130.161.211.204
+
+Host node305
+HostName 130.161.211.205
+
+Host node306
+HostName 130.161.211.206
+
+Host node308
+HostName 130.161.211.208
+
+Host node309
+HostName 130.161.211.209
+
+Host node310
+HostName 130.161.211.210
+
+Host node312
+HostName 130.161.211.212
+
+Host node313
+HostName 130.161.211.213
+
+Host node314
+HostName 130.161.211.214
+
+Host node315
+HostName 130.161.211.215
+
+Host node317
+HostName 130.161.211.217
+
+Host node319
+HostName 130.161.211.219
+
+Host node320
+HostName 130.161.211.220
+
+Host node322
+HostName 130.161.211.222
+
+Host node323
+HostName 130.161.211.223
+
+Host node324
+HostName 130.161.211.224
+
+Host node325
+HostName 130.161.211.225
+
+Host node326
+HostName 130.161.211.226
+
+Host node327
+HostName 130.161.211.227
+
+Host node328
+HostName 130.161.211.228
+
+Host node329
+HostName 130.161.211.229
+
+Host node330
+HostName 130.161.211.230
+
+Host node331
+HostName 130.161.211.231
+
+Host vtt1
+HostName 130.188.225.81
+
+Host vtt2
+HostName 130.188.225.82
+
+Host vtt3
+HostName 130.188.225.83
+
+Host vtt4
+HostName 130.188.225.84
+
+Host vtt5
+HostName 130.188.225.85
+
+Host vtt6
+HostName 130.188.225.86
+
+Host vtt7
+HostName 130.188.225.87
+
+Host vtt8
+HostName 130.188.225.88
+
+Host vtt9
+HostName 130.188.225.89
+
+Host vttA
+HostName 130.188.225.90
+
+Host vttB
+HostName 130.188.225.91
+
+Host vttC
+HostName 130.188.225.97
+
+Host vttD
+HostName 130.188.225.98
+
+Host canada
+HostName 72.55.184.147
+
+Host media
+HostName 83.96.143.114
+
+Host itanium
+HostName 194.226.235.181
+User RUNC\gritzko
diff --git a/src/libswift_udp/mfold/status.default.sh b/src/libswift_udp/mfold/status.default.sh
new file mode 100644 (file)
index 0000000..fb85fe6
--- /dev/null
@@ -0,0 +1 @@
+echo -e $HOST"\t"`tail -1 swift/$HOST-lerr`
diff --git a/src/libswift_udp/mfold/tcinfo.default.sh b/src/libswift_udp/mfold/tcinfo.default.sh
new file mode 100644 (file)
index 0000000..22cc299
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+# A convenience script for use with net* scripts.
+
+echo --- qdisc info of dev $EMIF ---
+sudo tc qdisc show dev $EMIF
+echo --- class info of dev $EMIF ---
+sudo tc class show dev $EMIF
+echo --- filter info of dev $EMIF ---
+sudo tc filter show dev $EMIF
+echo --- qdisc info of dev ifb0 ---
+sudo tc qdisc show dev ifb0
+echo --- class info of dev ifb0 ---
+sudo tc class show dev ifb0
+echo --- filter info of dev ifb0 ---
+sudo tc filter show dev ifb0
+echo --- qdisc info of dev lo ---
+sudo tc qdisc show dev lo
+echo --- class info of dev lo ---
+sudo tc class show dev lo
+echo --- filter info of dev lo ---
+sudo tc filter show dev lo
diff --git a/src/libswift_udp/mfold/test.default.sh b/src/libswift_udp/mfold/test.default.sh
new file mode 100644 (file)
index 0000000..bc450f3
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+hostname
diff --git a/src/libswift_udp/mfold/vtt.txt b/src/libswift_udp/mfold/vtt.txt
new file mode 100644 (file)
index 0000000..91d563d
--- /dev/null
@@ -0,0 +1,15 @@
+130.188.225.81  vtt1
+130.188.225.82  vtt2
+130.188.225.83  vtt3
+130.188.225.84  vtt4
+130.188.225.85  vtt5
+130.188.225.86  vtt6
+130.188.225.87  vtt7
+130.188.225.88  vtt8
+130.188.225.89  vtt9
+130.188.225.90  vtt10
+130.188.225.91  vtt11
+130.188.225.97  vtt12
+130.188.225.98  vtt13
+193.166.160.195 willab1
+193.166.160.196 willab2
diff --git a/src/libswift_udp/nat_test.cpp b/src/libswift_udp/nat_test.cpp
new file mode 100644 (file)
index 0000000..11d9fef
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *  nat_test.cpp
+ *  NAT type testing.
+ *
+ *  Created by Gertjan Halkes.
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#ifdef _WIN32
+#include <iphlpapi.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include <netinet/in.h>
+#endif
+
+#define REQUEST_MAGIC 0x5a9e5fa1
+#define REPLY_MAGIC 0xa655c5d5
+#define REPLY_SEC_MAGIC 0x85e4a5ca
+#define MAX_TRIES 3
+namespace swift {
+
+static void on_may_receive(SOCKET sock);
+static void on_may_send(SOCKET sock);
+static tint test_start;
+static int tries;
+static int packets_since_last_try;
+
+static sckrwecb_t callbacks(0, on_may_receive, on_may_send, NULL);
+/* Note that we lookup the addresses when we actually send, because Windows requires that
+   the winsock library is first intialized. If we use Address type variables here, the
+   lookup would be tried before that initialization, which fails... */
+//FIXME: Change addresses to actual addresses used in test (at least 2 should be provided!)
+static const char *servers[] = { "dutigp.st.ewi.tudelft.nl:18375" ,
+    "127.0.0.3:18375" };
+
+static void on_may_receive(SOCKET sock) {
+    Datagram data(sock);
+
+    data.Recv();
+
+    uint32_t magic = data.Pull32();
+    if ((magic != REPLY_MAGIC && magic != REPLY_SEC_MAGIC) ||
+            (magic == REPLY_MAGIC && data.size() != 6) || (magic == REPLY_SEC_MAGIC && data.size() != 0))
+    {
+        dprintf("%s #0 NATTEST weird packet %s \n", tintstr(), data.address().str());
+        return;
+    }
+
+    if (magic == REPLY_MAGIC) {
+        uint32_t ip = data.Pull32();
+        uint16_t port = data.Pull16();
+        Address reported(ip, port);
+        dprintf("%s #0 NATTEST incoming %s %s\n", tintstr(), data.address().str(), reported.str());
+    } else {
+        dprintf("%s #0 NATTEST incoming secondary %s\n", tintstr(), data.address().str());
+    }
+    packets_since_last_try++;
+}
+
+static void on_may_send(SOCKET sock) {
+    callbacks.may_write = NULL;
+    Datagram::Listen3rdPartySocket(callbacks);
+
+    for (size_t i = 0; i < (sizeof(servers)/sizeof(servers[0])); i++) {
+        Datagram request(sock, Address(servers[i]));
+
+        request.Push32(REQUEST_MAGIC);
+        request.Send();
+    }
+    test_start = NOW;
+
+    struct sockaddr_in name;
+    socklen_t namelen = sizeof(name);
+    if (getsockname(sock, (struct sockaddr *) &name, &namelen) < 0) {
+        dprintf("%s #0 NATTEST could not get local address\n", tintstr());
+    } else {
+        Address local(ntohl(name.sin_addr.s_addr), ntohs(name.sin_port));
+        dprintf("%s #0 NATTEST local %s\n", tintstr(), local.str());
+    }
+}
+
+static void printAddresses(void) {
+#ifdef _WIN32
+    IP_ADAPTER_INFO *adapterInfo = NULL;
+    IP_ADAPTER_INFO *adapter = NULL;
+    DWORD retval = 0;
+    UINT i;
+    ULONG size = 0;
+
+    if ((retval = GetAdaptersInfo(adapterInfo, &size)) != ERROR_BUFFER_OVERFLOW) {
+        dprintf("ERROR: %d\n", (int) retval);
+        return;
+    }
+
+    adapterInfo = (IP_ADAPTER_INFO *) malloc(size);
+    if (adapterInfo == NULL) {
+        dprintf("ERROR: out of memory\n");
+        return;
+    }
+
+    if ((retval = GetAdaptersInfo(adapterInfo, &size)) == NO_ERROR) {
+        adapter = adapterInfo;
+        while (adapter) {
+            IP_ADDR_STRING *address;
+            for (address = &adapter->IpAddressList; address != NULL; address = address->Next) {
+                if (address->IpAddress.String[0] != 0)
+                    dprintf("ADDRESS: %s\n", address->IpAddress.String);
+            }
+            adapter = adapter->Next;
+        }
+    } else {
+        dprintf("ERROR: %d\n", (int) retval);
+    }
+    free(adapterInfo);
+#else
+    struct ifaddrs *addrs, *ptr;
+    if (getifaddrs(&addrs) < 0) {
+        dprintf("ERROR: %s\n", strerror(errno));
+        return;
+    }
+
+    for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next) {
+        if (ptr->ifa_addr->sa_family == AF_INET) {
+            dprintf("ADDRESS: %s\n", inet_ntoa(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr));
+        }
+    }
+    freeifaddrs(addrs);
+#endif
+}
+
+
+void nat_test_update(void) {
+    static bool initialized;
+    if (!initialized) {
+        initialized = true;
+        printAddresses();
+    }
+
+    if (tries < MAX_TRIES && NOW - test_start > 30 * TINT_SEC) {
+        if (tries == 0) {
+            Address any;
+            SOCKET sock = Datagram::Bind(any, callbacks);
+            callbacks.sock = sock;
+        } else if (packets_since_last_try == 0) {
+            // Keep on trying if we didn't receive _any_ packet in response to our last request
+            tries--;
+        }
+        tries++;
+        callbacks.may_write = on_may_send;
+        Datagram::Listen3rdPartySocket(callbacks);
+    }
+}
+
+}
diff --git a/src/libswift_udp/nat_test_server.c b/src/libswift_udp/nat_test_server.c
new file mode 100644 (file)
index 0000000..b2558c3
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *  nat_test_server.c
+ *  NAT type testing (server).
+ *
+ *  Created by Gertjan Halkes.
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
+ *
+ */
+
+//FIXME: add timestamp to log output
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#define REQUEST_MAGIC 0x5a9e5fa1
+#define REPLY_MAGIC 0xa655c5d5
+#define REPLY_SEC_MAGIC 0x85e4a5ca
+
+static int has_secondary;
+
+/** Alert the user of a fatal error and quit.
+    @param fmt The format string for the message. See fprintf(3) for details.
+    @param ... The arguments for printing.
+*/
+void fatal(const char *fmt, ...) {
+    va_list args;
+
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
+    exit(EXIT_FAILURE);
+}
+
+const char *getTimestamp(void) {
+    static char timeBuffer[1024];
+    struct timeval now;
+    double nowF;
+
+    gettimeofday(&now, NULL);
+    nowF = (double) now.tv_sec + (double) now.tv_usec / 1000000;
+    snprintf(timeBuffer, 1024, "%.4f", nowF);
+    return timeBuffer;
+}
+
+int main(int argc, char *argv[]) {
+    struct sockaddr_in local, remote, secondary;
+    uint32_t packet[3];
+    int c, sock, sock2, sock3, sock4;
+    ssize_t result;
+
+    local.sin_addr.s_addr = INADDR_ANY;
+
+    while ((c = getopt(argc, argv, "s:")) > 0) {
+        switch (c) {
+            case 's':
+                has_secondary = 1;
+                secondary.sin_addr.s_addr = inet_addr(optarg);
+                break;
+            default:
+                fatal("Unknown option %c\n", c);
+                break;
+        }
+    }
+
+    if (argc - optind != 3)
+        fatal("Usage: nat_test_server [<options>] <primary address> <primary port> <secondary port>\n");
+
+    local.sin_family = AF_INET;
+    local.sin_addr.s_addr = inet_addr(argv[optind++]);
+    local.sin_port = htons(atoi(argv[optind++]));
+
+    if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+        fatal("Error opening primary socket: %m\n");
+    if (bind(sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+        fatal("Error binding primary socket: %m\n");
+
+    if (has_secondary) {
+        secondary.sin_family = AF_INET;
+        secondary.sin_port = local.sin_port;
+
+        if ((sock3 = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+            fatal("Error opening primary socket on secondary address: %m\n");
+        if (bind(sock3, (struct sockaddr *) &secondary, sizeof(secondary)) < 0)
+            fatal("Error binding primary socket on secondary address: %m\n");
+    }
+
+    local.sin_port = htons(atoi(argv[optind++]));
+
+    if ((sock2 = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+        fatal("Error opening secondary socket: %m\n");
+    if (bind(sock2, (struct sockaddr *) &local, sizeof(local)) < 0)
+        fatal("Error binding secondary socket: %m\n");
+
+    if (has_secondary) {
+        secondary.sin_port = local.sin_port;
+
+        if ((sock4 = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+            fatal("Error opening secondary socket on secondary address: %m\n");
+        if (bind(sock4, (struct sockaddr *) &secondary, sizeof(secondary)) < 0)
+            fatal("Error binding secondary socket on secondary address: %m\n");
+    }
+
+    while (1) {
+        socklen_t socklen = sizeof(remote);
+        if ((result = recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *) &remote, &socklen)) < 0) {
+            if (errno == EAGAIN)
+                continue;
+            fatal("%s: Error receiving packet: %m\n", getTimestamp());
+        } else if (result != 4 || ntohl(packet[0]) != REQUEST_MAGIC) {
+            fprintf(stderr, "Strange packet received from %s\n", inet_ntoa(remote.sin_addr));
+        } else {
+            fprintf(stderr, "%s: Received packet from %s:%d\n", getTimestamp(), inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
+            packet[0] = htonl(REPLY_MAGIC);
+            packet[1] = remote.sin_addr.s_addr;
+            *(uint16_t *)(packet + 2) = remote.sin_port;
+    retry:
+            if (sendto(sock, packet, 10, 0, (const struct sockaddr *) &remote, socklen) < 10) {
+                if (errno == EAGAIN)
+                    goto retry;
+                fprintf(stderr, "%s: Error sending packet on primary socket: %m\n", getTimestamp());
+            }
+    retry2:
+            if (sendto(sock2, packet, 10, 0, (const struct sockaddr *) &remote, socklen) < 10) {
+                if (errno == EAGAIN)
+                    goto retry2;
+                fprintf(stderr, "%s: Error sending packet on secondary socket: %m\n", getTimestamp());
+            }
+
+            if (has_secondary) {
+                packet[0] = htonl(REPLY_SEC_MAGIC);
+        retry3:
+                if (sendto(sock3, packet, 4, 0, (const struct sockaddr *) &remote, socklen) < 4) {
+                    if (errno == EAGAIN)
+                        goto retry3;
+                    fprintf(stderr, "%s: Error sending packet on primary socket on secondary address: %m\n", getTimestamp());
+                }
+        retry4:
+                if (sendto(sock4, packet, 4, 0, (const struct sockaddr *) &remote, socklen) < 4) {
+                    if (errno == EAGAIN)
+                        goto retry4;
+                    fprintf(stderr, "%s: Error sending packet on secondary socket on secondary address: %m\n", getTimestamp());
+                }
+            }
+
+        }
+    }
+    return 0;
+}
diff --git a/src/libswift_udp/send_control.cpp b/src/libswift_udp/send_control.cpp
new file mode 100644 (file)
index 0000000..02aff71
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  send_control.cpp
+ *  congestion control logic for the swift protocol
+ *
+ *  Created by Victor Grishchenko on 12/10/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#include <cassert>
+
+using namespace swift;
+using namespace std;
+
+tint Channel::MIN_DEV = 50*TINT_MSEC;
+tint Channel::MAX_SEND_INTERVAL = TINT_SEC*58;
+tint Channel::LEDBAT_TARGET = TINT_MSEC*25;
+float Channel::LEDBAT_GAIN = 1.0/LEDBAT_TARGET;
+tint Channel::LEDBAT_DELAY_BIN = TINT_SEC*30;
+tint Channel::MAX_POSSIBLE_RTT = TINT_SEC*10;
+const char* Channel::SEND_CONTROL_MODES[] = {"keepalive", "pingpong",
+    "slowstart", "standard_aimd", "ledbat", "closing"};
+
+
+tint    Channel::NextSendTime () {
+    TimeoutDataOut(); // precaution to know free cwnd
+    switch (send_control_) {
+        case KEEP_ALIVE_CONTROL: return KeepAliveNextSendTime();
+        case PING_PONG_CONTROL:  return PingPongNextSendTime();
+        case SLOW_START_CONTROL: return SlowStartNextSendTime();
+        case AIMD_CONTROL:       return AimdNextSendTime();
+        case LEDBAT_CONTROL:     return LedbatNextSendTime();
+        case CLOSE_CONTROL:      return TINT_NEVER;
+        default:                 fprintf(stderr,"send_control.cpp: unknown control %d\n", send_control_); return TINT_NEVER;
+    }
+}
+
+tint    Channel::SwitchSendControl (int control_mode) {
+    dprintf("%s #%u sendctrl switch %s->%s\n",tintstr(),id(),
+            SEND_CONTROL_MODES[send_control_],SEND_CONTROL_MODES[control_mode]);
+    switch (control_mode) {
+        case KEEP_ALIVE_CONTROL:
+            send_interval_ = rtt_avg_; //max(TINT_SEC/10,rtt_avg_);
+            dev_avg_ = max(TINT_SEC,rtt_avg_);
+            data_out_cap_ = bin_t::ALL;
+            cwnd_ = 1;
+            break;
+        case PING_PONG_CONTROL:
+            dev_avg_ = max(TINT_SEC,rtt_avg_);
+            data_out_cap_ = bin_t::ALL;
+            cwnd_ = 1;
+            break;
+        case SLOW_START_CONTROL:
+            cwnd_ = 1;
+            break;
+        case AIMD_CONTROL:
+            break;
+        case LEDBAT_CONTROL:
+            break;
+        case CLOSE_CONTROL:
+            break;
+        default:
+            assert(false);
+    }
+    send_control_ = control_mode;
+    return NextSendTime();
+}
+
+tint    Channel::KeepAliveNextSendTime () {
+    if (sent_since_recv_>=3 && last_recv_time_<NOW-3*MAX_SEND_INTERVAL)
+        return SwitchSendControl(CLOSE_CONTROL);
+    if (ack_rcvd_recent_)
+        return SwitchSendControl(SLOW_START_CONTROL);
+    if (data_in_.time!=TINT_NEVER)
+        return NOW;
+       /* Gertjan fix 5f51e5451e3785a74c058d9651b2d132c5a94557
+    "Do not increase send interval in keep-alive mode when previous Reschedule
+    was already in the future.
+    The problem this solves is that when we keep on receiving packets in keep-alive
+    mode, the next packet will be pushed further and further into the future, which is
+    not what we want. The scheduled time for the next packet should be unchanged
+    on reception."
+    */
+    if (!reverse_pex_out_.empty())
+        return reverse_pex_out_.front().time;
+    if (NOW < next_send_time_)
+        return next_send_time_;
+
+    // Arno: Fix that doesn't do exponential growth always, only after sends
+    // without following recvs
+
+    // fprintf(stderr,"KeepAliveNextSendTime: gotka %d sentka %d ss %d si %lli\n", lastrecvwaskeepalive_, lastsendwaskeepalive_, sent_since_recv_, send_interval_);
+
+    if (lastrecvwaskeepalive_ && lastsendwaskeepalive_)
+    {
+       send_interval_ <<= 1;
+    }
+    else if (lastrecvwaskeepalive_ || lastsendwaskeepalive_)
+    {
+        // Arno, 2011-11-29: we like eachother again, start fresh
+       // Arno, 2012-01-25: Unless we're talking to a dead peer.
+        if (sent_since_recv_ < 4) {
+           send_interval_ = rtt_avg_;
+        } else 
+            send_interval_ <<= 1;
+    }
+    else if (sent_since_recv_ <= 1) 
+    {
+       send_interval_ = rtt_avg_;
+    }
+    else if (sent_since_recv_ > 1)
+    {
+        send_interval_ <<= 1;
+    }
+    if (send_interval_>MAX_SEND_INTERVAL)
+        send_interval_ = MAX_SEND_INTERVAL;
+    return last_send_time_ + send_interval_;
+}
+
+tint    Channel::PingPongNextSendTime () { // FIXME INFINITE LOOP
+    if (dgrams_sent_>=10)
+        return SwitchSendControl(KEEP_ALIVE_CONTROL);
+    if (ack_rcvd_recent_)
+        return SwitchSendControl(SLOW_START_CONTROL);
+    if (data_in_.time!=TINT_NEVER)
+        return NOW;
+    if (last_recv_time_>last_send_time_)
+        return NOW;
+    if (!last_send_time_)
+        return NOW;
+    return last_send_time_ + ack_timeout(); // timeout
+}
+
+tint    Channel::CwndRateNextSendTime () {
+    if (data_in_.time!=TINT_NEVER)
+        return NOW; // TODO: delayed ACKs
+    //if (last_recv_time_<NOW-rtt_avg_*4)
+    //    return SwitchSendControl(KEEP_ALIVE_CONTROL);
+    send_interval_ = rtt_avg_/cwnd_;
+    if (send_interval_>max(rtt_avg_,TINT_SEC)*4)
+        return SwitchSendControl(KEEP_ALIVE_CONTROL);
+    if (data_out_.size()<cwnd_) {
+        dprintf("%s #%u sendctrl next in %llius (cwnd %.2f, data_out %i)\n",
+                tintstr(),id_,send_interval_,cwnd_,(int)data_out_.size());
+        return last_data_out_time_ + send_interval_;
+    } else {
+        assert(data_out_.front().time!=TINT_NEVER);
+        return data_out_.front().time + ack_timeout();
+    }
+}
+
+void    Channel::BackOffOnLosses (float ratio) {
+    ack_rcvd_recent_ = 0;
+    ack_not_rcvd_recent_ =  0;
+    if (last_loss_time_<NOW-rtt_avg_) {
+        cwnd_ *= ratio;
+        last_loss_time_ = NOW;
+        dprintf("%s #%u sendctrl backoff %3.2f\n",tintstr(),id_,cwnd_);
+    }
+}
+
+tint    Channel::SlowStartNextSendTime () {
+    if (ack_not_rcvd_recent_) {
+        BackOffOnLosses();
+        return SwitchSendControl(LEDBAT_CONTROL);//AIMD_CONTROL);
+    }
+    if (rtt_avg_/cwnd_<TINT_SEC/10)
+        return SwitchSendControl(LEDBAT_CONTROL);//AIMD_CONTROL);
+    cwnd_+=ack_rcvd_recent_;
+    ack_rcvd_recent_=0;
+    return CwndRateNextSendTime();
+}
+
+tint    Channel::AimdNextSendTime () {
+    if (ack_not_rcvd_recent_)
+        BackOffOnLosses();
+    if (ack_rcvd_recent_) {
+        if (cwnd_>1)
+            cwnd_ += ack_rcvd_recent_/cwnd_;
+        else
+            cwnd_ *= 2;
+    }
+    ack_rcvd_recent_=0;
+    return CwndRateNextSendTime();
+}
+
+tint Channel::LedbatNextSendTime () {
+    float oldcwnd = cwnd_;
+
+    tint owd_cur(TINT_NEVER), owd_min(TINT_NEVER);
+    for(int i=0; i<4; i++) {
+        if (owd_min>owd_min_bins_[i])
+            owd_min = owd_min_bins_[i];
+        if (owd_cur>owd_current_[i])
+            owd_cur = owd_current_[i];
+    }
+    if (ack_not_rcvd_recent_)
+        BackOffOnLosses(0.8);
+    ack_rcvd_recent_ = 0;
+    tint queueing_delay = owd_cur - owd_min;
+    tint off_target = LEDBAT_TARGET - queueing_delay;
+    cwnd_ += LEDBAT_GAIN * off_target / cwnd_;
+    if (cwnd_<1) 
+        cwnd_ = 1;
+    if (owd_cur==TINT_NEVER || owd_min==TINT_NEVER) 
+        cwnd_ = 1;
+
+    //Arno, 2012-02-02: Somehow LEDBAT gets stuck at cwnd_ == 1 sometimes
+    // This hack appears to work to get it back on the right track quickly.
+    if (oldcwnd == 1 && cwnd_ == 1)
+       cwnd_count1_++;
+    else
+       cwnd_count1_ = 0;
+    if (cwnd_count1_ > 10)
+    {
+        dprintf("%s #%u sendctrl ledbat stuck, reset\n",tintstr(),id() );
+       cwnd_count1_ = 0;
+        for(int i=0; i<4; i++) {
+            owd_min_bins_[i] = TINT_NEVER;
+            owd_current_[i] = TINT_NEVER;
+        }
+    }
+
+    dprintf("%s #%u sendctrl ledbat %lli-%lli => %3.2f\n",
+            tintstr(),id_,owd_cur,owd_min,cwnd_);
+    return CwndRateNextSendTime();
+}
+
+
+
diff --git a/src/libswift_udp/sendrecv.cpp b/src/libswift_udp/sendrecv.cpp
new file mode 100644 (file)
index 0000000..e64d3ee
--- /dev/null
@@ -0,0 +1,1089 @@
+/*
+ *  sendrecv.cpp
+ *  most of the swift's state machine
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include "bin_utils.h"
+#include "swift.h"
+#include <algorithm>  // kill it
+#include <cassert>
+#include <math.h>
+#include <cfloat>
+#include "compat.h"
+
+using namespace swift;
+using namespace std;
+
+struct event_base *Channel::evbase;
+struct event Channel::evrecv;
+
+#define DEBUGTRAFFIC   0
+
+/** Arno: Victor's design allows a sender to choose some data to push to
+ * a receiver, if that receiver is not HINTing at data. Should be disabled
+ * when the receiver has a download rate limit.
+ */
+#define ENABLE_SENDERSIZE_PUSH 0
+
+
+/** Arno, 2011-11-24: When rate limit is on and the download is in progress
+ * we send HINTs for 2 chunks at the moment. This constant can be used to
+ * get greater granularity. Set to 0 for original behaviour.
+ */
+#define HINT_GRANULARITY       16 // chunks
+
+/*
+ TODO  25 Oct 18:55
+ - range: ALL
+ - randomized testing of advanced ops (new testcase)
+ */
+
+void    Channel::AddPeakHashes (struct evbuffer *evb) {
+    for(int i=0; i<file().peak_count(); i++) {
+        bin_t peak = file().peak(i);
+        evbuffer_add_8(evb, SWIFT_HASH);
+        evbuffer_add_32be(evb, bin_toUInt32(peak));
+        evbuffer_add_hash(evb, file().peak_hash(i));
+        char bin_name_buf[32];
+        dprintf("%s #%u +phash %s\n",tintstr(),id_,peak.str(bin_name_buf));
+    }
+}
+
+
+void    Channel::AddUncleHashes (struct evbuffer *evb, bin_t pos) {
+
+    char bin_name_buf2[32];
+    dprintf("%s #%u +uncle hash for %s\n",tintstr(),id_,pos.str(bin_name_buf2));
+
+    bin_t peak = file().peak_for(pos);
+    while (pos!=peak && ((NOW&3)==3 || !pos.parent().contains(data_out_cap_)) &&
+            ack_in_.is_empty(pos.parent()) ) {
+        bin_t uncle = pos.sibling();
+        evbuffer_add_8(evb, SWIFT_HASH);
+        evbuffer_add_32be(evb, bin_toUInt32(uncle));
+        evbuffer_add_hash(evb,  file().hash(uncle) );
+        char bin_name_buf[32];
+        dprintf("%s #%u +hash %s\n",tintstr(),id_,uncle.str(bin_name_buf));
+        pos = pos.parent();
+    }
+}
+
+
+bin_t           Channel::ImposeHint () {
+    uint64_t twist = peer_channel_id_;  // got no hints, send something randomly
+
+    twist &= file().peak(0).toUInt(); // FIXME may make it semi-seq here
+
+    bin_t my_pick = binmap_t::find_complement(ack_in_, file().ack_out(), twist);
+
+    my_pick.to_twisted(twist);
+    while (my_pick.base_length()>max(1,(int)cwnd_))
+        my_pick = my_pick.left();
+
+    return my_pick.twisted(twist);
+}
+
+
+bin_t        Channel::DequeueHint (bool *retransmitptr) {
+    bin_t send = bin_t::NONE;
+
+    // Arno, 2012-01-23: Extra protection against channel loss, don't send DATA
+    if (last_recv_time_ < NOW-(3*TINT_SEC))
+       return bin_t::NONE;
+
+    // Arno, 2012-01-18: Reenable Victor's retransmit
+    if (!data_out_tmo_.empty())
+    {
+        tintbin tb = data_out_tmo_.front();
+        send = tb.bin;
+        data_out_tmo_.pop_front();  
+        *retransmitptr = true;
+    }
+    else
+        *retransmitptr = false;
+
+    if (ENABLE_SENDERSIZE_PUSH && send.is_none() && hint_in_.empty() && last_recv_time_>NOW-rtt_avg_-TINT_SEC) {
+        bin_t my_pick = ImposeHint(); // FIXME move to the loop
+        if (!my_pick.is_none()) {
+            hint_in_.push_back(my_pick);
+            char bin_name_buf[32];
+            dprintf("%s #%u *hint %s\n",tintstr(),id_,my_pick.str(bin_name_buf));
+        }
+    }
+    
+    while (!hint_in_.empty() && send.is_none()) {
+        bin_t hint = hint_in_.front().bin;
+        tint time = hint_in_.front().time;
+        hint_in_.pop_front();
+        while (!hint.is_base()) { // FIXME optimize; possible attack
+            hint_in_.push_front(tintbin(time,hint.right()));
+            hint = hint.left();
+        }
+        //if (time < NOW-TINT_SEC*3/2 )
+        //    continue;  bad idea
+        if (!ack_in_.is_filled(hint))
+            send = hint;
+    }
+    uint64_t mass = 0;
+    // Arno, 2012-03-09: Is mucho expensive on busy server.
+    //for(int i=0; i<hint_in_.size(); i++)
+    //    mass += hint_in_[i].bin.base_length();
+    char bin_name_buf[32];
+    dprintf("%s #%u dequeued %s [%lli]\n",tintstr(),id_,send.str(bin_name_buf),mass);
+    return send;
+}
+
+
+void    Channel::AddHandshake (struct evbuffer *evb) {
+    if (!peer_channel_id_) { // initiating
+        evbuffer_add_8(evb, SWIFT_HASH);
+        evbuffer_add_32be(evb, bin_toUInt32(bin_t::ALL));
+        evbuffer_add_hash(evb, file().root_hash());
+        dprintf("%s #%u +hash ALL %s\n",
+                tintstr(),id_,file().root_hash().hex().c_str());
+    }
+    evbuffer_add_8(evb, SWIFT_HANDSHAKE);
+    int encoded = -1;
+    if (send_control_==CLOSE_CONTROL) {
+       encoded = 0;
+    }
+    else
+       encoded = EncodeID(id_);
+    evbuffer_add_32be(evb, encoded);
+    dprintf("%s #%u +hs %x\n",tintstr(),id_,encoded);
+    have_out_.clear();
+}
+
+
+void    Channel::Send () {
+
+       dprintf("%s #%u Send called \n",tintstr(),id_);
+
+    struct evbuffer *evb = evbuffer_new();
+    evbuffer_add_32be(evb, peer_channel_id_);
+    bin_t data = bin_t::NONE;
+    int evbnonadplen = 0;
+    if ( is_established() ) {
+       if (send_control_!=CLOSE_CONTROL) {
+                       // FIXME: seeder check
+                       AddHave(evb);
+                       AddAck(evb);
+                       if (!file().is_complete()) {
+                               AddHint(evb);
+                               /* Gertjan fix: 7aeea65f3efbb9013f601b22a57ee4a423f1a94d
+                               "Only call Reschedule for 'reverse PEX' if the channel is in keep-alive mode"
+                                */
+                               AddPexReq(evb);
+                       }
+                       AddPex(evb);
+                       TimeoutDataOut();
+                       data = AddData(evb);
+       } else {
+               // Arno: send explicit close
+               AddHandshake(evb);
+       }
+    } else {
+        AddHandshake(evb);
+        AddHave(evb); // Arno, 2011-10-28: from AddHandShake. Why double?
+        AddHave(evb);
+        AddAck(evb);
+    }
+
+    lastsendwaskeepalive_ = (evbuffer_get_length(evb) == 4);
+
+    if (evbuffer_get_length(evb)==4) {// only the channel id; bare keep-alive
+        data = bin_t::ALL;
+    }
+    dprintf("%s #%u sent %ib %s:%x\n",
+            tintstr(),id_,(int)evbuffer_get_length(evb),peer().str(),
+            peer_channel_id_);
+    int r = SendTo(socket_,peer(),evb);
+    if (r==-1)
+        print_error("can't send datagram");
+    else
+       raw_bytes_up_ += r;
+    last_send_time_ = NOW;
+    sent_since_recv_++;
+    dgrams_sent_++;
+    evbuffer_free(evb);
+    Reschedule();
+}
+
+void    Channel::AddHint (struct evbuffer *evb) {
+
+       // RATELIMIT
+       // Policy is to not send hints when we are above speed limit
+       if (transfer().GetCurrentSpeed(DDIR_DOWNLOAD) > transfer().GetMaxSpeed(DDIR_DOWNLOAD)) {
+               if (DEBUGTRAFFIC)
+                       fprintf(stderr,"hint: forbidden#");
+               return;
+       }
+
+
+       // 1. Calc max of what we are allowed to request, uncongested bandwidth wise
+    tint plan_for = max(TINT_SEC,rtt_avg_*4);
+
+    tint timed_out = NOW - plan_for*2;
+    while ( !hint_out_.empty() && hint_out_.front().time < timed_out ) {
+        hint_out_size_ -= hint_out_.front().bin.base_length();
+        hint_out_.pop_front();
+    }
+
+    int first_plan_pck = max ( (tint)1, plan_for / dip_avg_ );
+
+    // Riccardo, 2012-04-04: Actually allowed is max minus what we already asked for
+    int queue_allowed_hints = max(0,first_plan_pck-(int)hint_out_size_);
+
+
+    // RATELIMIT
+    // 2. Calc max of what is allowed by the rate limiter
+    int rate_allowed_hints = LONG_MAX;
+    if (transfer().GetMaxSpeed(DDIR_DOWNLOAD) < DBL_MAX)
+    {
+               uint64_t rough_global_hint_out_size = 0; // rough estimate, as hint_out_ clean up is not done for all channels
+               std::set<Channel *>::iterator iter;
+               for (iter=transfer().mychannels_.begin(); iter!=transfer().mychannels_.end(); iter++)
+               {
+                       Channel *c = *iter;
+                       if (c != NULL)
+                               rough_global_hint_out_size += c->hint_out_size_;
+               }
+
+               // Policy: this channel is allowed to hint at the limit - global_hinted_at
+               // Handle MaxSpeed = unlimited
+               double rate_hints_limit_float = transfer().GetMaxSpeed(DDIR_DOWNLOAD)/((double)file().chunk_size());
+
+               int rate_hints_limit = (int)min((double)LONG_MAX,rate_hints_limit_float);
+
+               // Actually allowed is max minus what we already asked for, globally (=all channels)
+               rate_allowed_hints = max(0,rate_hints_limit-(int)rough_global_hint_out_size);
+    }
+
+    // 3. Take the smallest allowance from rate and queue limit
+    uint64_t plan_pck = (uint64_t)min(rate_allowed_hints,queue_allowed_hints);
+
+    // 4. Ask allowance in blocks of chunks to get pipelining going from serving peer.
+    if (hint_out_size_ == 0 || plan_pck > HINT_GRANULARITY)
+    {
+        bin_t hint = transfer().picker().Pick(ack_in_,plan_pck,NOW+plan_for*2);
+        if (!hint.is_none()) {
+               if (DEBUGTRAFFIC)
+               {
+                       char binstr[32];
+                       fprintf(stderr,"hint c%d: ask %s\n", id(), hint.str(binstr) );
+               }
+            evbuffer_add_8(evb, SWIFT_HINT);
+            evbuffer_add_32be(evb, bin_toUInt32(hint));
+            char bin_name_buf[32];
+            dprintf("%s #%u +hint %s [%lli]\n",tintstr(),id_,hint.str(bin_name_buf),hint_out_size_);
+            hint_out_.push_back(hint);
+            hint_out_size_ += hint.base_length();
+            //fprintf(stderr,"send c%d: HINTLEN %i\n", id(), hint.base_length());
+            //fprintf(stderr,"HL %i ", hint.base_length());
+        }
+        else
+            dprintf("%s #%u Xhint\n",tintstr(),id_);
+
+    }
+}
+
+
+bin_t        Channel::AddData (struct evbuffer *evb) {
+       // RATELIMIT
+       if (transfer().GetCurrentSpeed(DDIR_UPLOAD) > transfer().GetMaxSpeed(DDIR_UPLOAD)) {
+               transfer().OnSendNoData();
+               return bin_t::NONE;
+       }
+
+    if (!file().size()) // know nothing
+        return bin_t::NONE;
+
+    bin_t tosend = bin_t::NONE;
+    bool isretransmit = false;
+    tint luft = send_interval_>>4; // may wake up a bit earlier
+    if (data_out_.size()<cwnd_ &&
+            last_data_out_time_+send_interval_<=NOW+luft) {
+        tosend = DequeueHint(&isretransmit);
+        if (tosend.is_none()) {
+            dprintf("%s #%u sendctrl no idea what data to send\n",tintstr(),id_);
+            if (send_control_!=KEEP_ALIVE_CONTROL && send_control_!=CLOSE_CONTROL)
+                SwitchSendControl(KEEP_ALIVE_CONTROL);
+        }
+    } else
+        dprintf("%s #%u sendctrl wait cwnd %f data_out %i next %s\n",
+                tintstr(),id_,cwnd_,(int)data_out_.size(),tintstr(last_data_out_time_+NOW-send_interval_));
+
+    if (tosend.is_none())// && (last_data_out_time_>NOW-TINT_SEC || data_out_.empty()))
+        return bin_t::NONE; // once in a while, empty data is sent just to check rtt FIXED
+
+    if (ack_in_.is_empty() && file().size())
+        AddPeakHashes(evb);
+
+    //NETWVSHASH
+    if (file().get_check_netwvshash())
+       AddUncleHashes(evb,tosend);
+
+    if (!ack_in_.is_empty()) // TODO: cwnd_>1
+        data_out_cap_ = tosend;
+
+    // Arno, 2011-11-03: May happen when first data packet is sent to empty
+    // leech, then peak + uncle hashes may be so big that they don't fit in eth
+    // frame with DATA. Send 2 datagrams then, one with peaks so they have
+    // a better chance of arriving. Optimistic violation of atomic datagram
+    // principle.
+    if (file().chunk_size() == SWIFT_DEFAULT_CHUNK_SIZE && evbuffer_get_length(evb) > SWIFT_MAX_NONDATA_DGRAM_SIZE) {
+        dprintf("%s #%u fsent %ib %s:%x\n",
+                tintstr(),id_,(int)evbuffer_get_length(evb),peer().str(),
+                peer_channel_id_);
+       int ret = Channel::SendTo(socket_,peer(),evb); // kind of fragmentation
+       if (ret > 0)
+               raw_bytes_up_ += ret;
+        evbuffer_add_32be(evb, peer_channel_id_);
+    }
+
+    if (file().chunk_size() != SWIFT_DEFAULT_CHUNK_SIZE && isretransmit) {
+       /* FRAGRAND
+        * Arno, 2012-01-17: We observe strange behaviour when using
+        * fragmented UDP packets. When ULANC sends a specific datagram ("995"),
+        * the 2nd IP packet carrying it gets lost structurally. When
+        * downloading from the same asset hosted on a Linux 32-bit machine
+        * using a Win7 32-bit client (behind a NAT), one specific full
+        * datagram never gets delivered (6970 one before do). A workaround
+        * is to add some random data to the datagram. Hence we introduce
+        * the SWIFT_RANDOMIZE message, that is added to the datagram carrying
+        * the DATA on a retransmit.
+        */
+            char binstr[32];
+         fprintf(stderr,"AddData: retransmit of randomized chunk %s\n",tosend.str(binstr) );
+         evbuffer_add_8(evb, SWIFT_RANDOMIZE);
+         evbuffer_add_32be(evb, (int)rand() );
+    }
+
+    evbuffer_add_8(evb, SWIFT_DATA);
+    evbuffer_add_32be(evb, bin_toUInt32(tosend));
+
+    struct evbuffer_iovec vec;
+    if (evbuffer_reserve_space(evb, file().chunk_size(), &vec, 1) < 0) {
+       print_error("error on evbuffer_reserve_space");
+       return bin_t::NONE;
+    }
+    size_t r = pread(file().file_descriptor(),(char *)vec.iov_base,
+                    file().chunk_size(),tosend.base_offset()*file().chunk_size());
+    // TODO: corrupted data, retries, caching
+    if (r<0) {
+        print_error("error on reading");
+        vec.iov_len = 0;
+        evbuffer_commit_space(evb, &vec, 1);
+        return bin_t::NONE;
+    }
+    // assert(dgram.space()>=r+4+1);
+    vec.iov_len = r;
+    if (evbuffer_commit_space(evb, &vec, 1) < 0) {
+        print_error("error on evbuffer_commit_space");
+        return bin_t::NONE;
+    }
+
+    last_data_out_time_ = NOW;
+    data_out_.push_back(tosend);
+    bytes_up_ += r;
+    global_bytes_up += r;
+
+    char bin_name_buf[32];
+    dprintf("%s #%u +data %s\n",tintstr(),id_,tosend.str(bin_name_buf));
+
+    // RATELIMIT
+    // ARNOSMPTODO: count overhead bytes too? Move to Send() then.
+       transfer_->OnSendData(file().chunk_size());
+
+    return tosend;
+}
+
+
+void    Channel::AddAck (struct evbuffer *evb) {
+    if (data_in_==tintbin())
+       //if (data_in_.bin==bin64_t::NONE)
+        return;
+    // sometimes, we send a HAVE (e.g. in case the peer did repetitive send)
+    evbuffer_add_8(evb, data_in_.time==TINT_NEVER?SWIFT_HAVE:SWIFT_ACK);
+    evbuffer_add_32be(evb, bin_toUInt32(data_in_.bin));
+    if (data_in_.time!=TINT_NEVER)
+        evbuffer_add_64be(evb, data_in_.time);
+
+
+       if (DEBUGTRAFFIC)
+               fprintf(stderr,"send c%d: ACK %i\n", id(), bin_toUInt32(data_in_.bin));
+
+    have_out_.set(data_in_.bin);
+    char bin_name_buf[32];
+    dprintf("%s #%u +ack %s %s\n",
+        tintstr(),id_,data_in_.bin.str(bin_name_buf),tintstr(data_in_.time));
+    if (data_in_.bin.layer()>2)
+        data_in_dbl_ = data_in_.bin;
+
+    //fprintf(stderr,"data_in_ c%d\n", id() );
+    data_in_ = tintbin();
+    //data_in_ = tintbin(NOW,bin64_t::NONE);
+}
+
+
+void    Channel::AddHave (struct evbuffer *evb) {
+    if (!data_in_dbl_.is_none()) { // TODO: do redundancy better
+        evbuffer_add_8(evb, SWIFT_HAVE);
+        evbuffer_add_32be(evb, bin_toUInt32(data_in_dbl_));
+        data_in_dbl_=bin_t::NONE;
+    }
+    if (DEBUGTRAFFIC)
+               fprintf(stderr,"send c%d: HAVE ",id() );
+    for(int count=0; count<4; count++) {
+        bin_t ack = binmap_t::find_complement(have_out_, file().ack_out(), 0); // FIXME: do rotating queue
+        if (ack.is_none())
+            break;
+        ack = file().ack_out().cover(ack);
+        have_out_.set(ack);
+        evbuffer_add_8(evb, SWIFT_HAVE);
+        evbuffer_add_32be(evb, bin_toUInt32(ack));
+
+       if (DEBUGTRAFFIC)
+               fprintf(stderr," %i", bin_toUInt32(ack));
+
+        char bin_name_buf[32];
+        dprintf("%s #%u +have %s\n",tintstr(),id_,ack.str(bin_name_buf));
+    }
+       if (DEBUGTRAFFIC)
+               fprintf(stderr,"\n");
+
+}
+
+
+void    Channel::Recv (struct evbuffer *evb) {
+    dprintf("%s #%u recvd %ib\n",tintstr(),id_,(int)evbuffer_get_length(evb)+4);
+    dgrams_rcvd_++;
+
+    lastrecvwaskeepalive_ = (evbuffer_get_length(evb) == 0);
+    if (lastrecvwaskeepalive_)
+       // Update speed measurements such that they decrease when DL stops
+       transfer().OnRecvData(0);
+
+    if (last_send_time_ && rtt_avg_==TINT_SEC && dev_avg_==0) {
+        rtt_avg_ = NOW - last_send_time_;
+        dev_avg_ = rtt_avg_;
+        dip_avg_ = rtt_avg_;
+        dprintf("%s #%u sendctrl rtt init %lli\n",tintstr(),id_,rtt_avg_);
+    }
+
+    bin_t data = evbuffer_get_length(evb) ? bin_t::NONE : bin_t::ALL;
+
+       if (DEBUGTRAFFIC)
+               fprintf(stderr,"recv c%d: size %d ", id(), evbuffer_get_length(evb));
+
+       while (evbuffer_get_length(evb)) {
+        uint8_t type = evbuffer_remove_8(evb);
+
+        if (DEBUGTRAFFIC)
+               fprintf(stderr," %d", type);
+
+        switch (type) {
+            case SWIFT_HANDSHAKE: OnHandshake(evb); break;
+            case SWIFT_DATA:      data=OnData(evb); break;
+            case SWIFT_HAVE:      OnHave(evb); break;
+            case SWIFT_ACK:       OnAck(evb); break;
+            case SWIFT_HASH:      OnHash(evb); break;
+            case SWIFT_HINT:      OnHint(evb); break;
+            case SWIFT_PEX_ADD:   OnPex(evb); break;
+            case SWIFT_PEX_REQ:   OnPexReq(); break;
+            case SWIFT_RANDOMIZE: OnRandomize(evb); break; //FRAGRAND
+            default:
+                dprintf("%s #%u ?msg id unknown %i\n",tintstr(),id_,(int)type);
+                return;
+        }
+    }
+    if (DEBUGTRAFFIC)
+    {
+       fprintf(stderr,"\n");
+    }
+
+
+    last_recv_time_ = NOW;
+    sent_since_recv_ = 0;
+    Reschedule();
+}
+
+/*
+ * Arno: FAXME: HASH+DATA should be handled as a transaction: only when the
+ * hashes check out should they be stored in the hashtree, otherwise revert.
+ */
+void    Channel::OnHash (struct evbuffer *evb) {
+       bin_t pos = bin_fromUInt32(evbuffer_remove_32be(evb));
+    Sha1Hash hash = evbuffer_remove_hash(evb);
+    file().OfferHash(pos,hash);
+    char bin_name_buf[32];
+    dprintf("%s #%u -hash %s\n",tintstr(),id_,pos.str(bin_name_buf));
+
+    //fprintf(stderr,"HASH %lli hex %s\n",pos.toUInt(), hash.hex().c_str() );
+}
+
+
+void    Channel::CleanHintOut (bin_t pos) {
+    int hi = 0;
+    while (hi<hint_out_.size() && !hint_out_[hi].bin.contains(pos))
+        hi++;
+    if (hi==hint_out_.size())
+        return; // something not hinted or hinted in far past
+    while (hi--) { // removing likely snubbed hints
+        hint_out_size_ -= hint_out_.front().bin.base_length();
+        hint_out_.pop_front();
+    }
+    while (hint_out_.front().bin!=pos) {
+        tintbin f = hint_out_.front();
+
+        assert (f.bin.contains(pos));
+
+        if (pos < f.bin) {
+            f.bin.to_left();
+        } else {
+            f.bin.to_right();
+        }
+
+        hint_out_.front().bin = f.bin.sibling();
+        hint_out_.push_front(f);
+    }
+    hint_out_.pop_front();
+    hint_out_size_--;
+}
+
+
+bin_t Channel::OnData (struct evbuffer *evb) {  // TODO: HAVE NONE for corrupted data
+
+       char bin_name_buf[32];
+       bin_t pos = bin_fromUInt32(evbuffer_remove_32be(evb));
+
+    // Arno: Assuming DATA last message in datagram
+    if (evbuffer_get_length(evb) > file().chunk_size()) {
+       dprintf("%s #%u !data chunk size mismatch %s: exp %lu got " PRISIZET "\n",tintstr(),id_,pos.str(bin_name_buf), file().chunk_size(), evbuffer_get_length(evb));
+       fprintf(stderr,"WARNING: chunk size mismatch: exp %lu got " PRISIZET "\n",file().chunk_size(), evbuffer_get_length(evb));
+    }
+
+    int length = (evbuffer_get_length(evb) < file().chunk_size()) ? evbuffer_get_length(evb) : file().chunk_size();
+    if (!file().ack_out().is_empty(pos)) {
+        // Arno, 2012-01-24: print message for duplicate
+        dprintf("%s #%u Ddata %s\n",tintstr(),id_,pos.str(bin_name_buf));
+        evbuffer_drain(evb, length);
+        data_in_ = tintbin(TINT_NEVER,transfer().ack_out().cover(pos));
+
+        // Arno, 2012-01-24: Make sure data interarrival periods don't get
+        // screwed up because of these (ignored) duplicates.
+        UpdateDIP(pos);
+        return bin_t::NONE;
+    }
+    uint8_t *data = evbuffer_pullup(evb, length);
+    data_in_ = tintbin(NOW,bin_t::NONE);
+    if (!file().OfferData(pos, (char*)data, length)) {
+       evbuffer_drain(evb, length);
+        char bin_name_buf[32];
+        dprintf("%s #%u !data %s\n",tintstr(),id_,pos.str(bin_name_buf));
+        return bin_t::NONE;
+    }
+    evbuffer_drain(evb, length);
+    dprintf("%s #%u -data %s\n",tintstr(),id_,pos.str(bin_name_buf));
+
+    if (DEBUGTRAFFIC)
+       fprintf(stderr,"$ ");
+
+    bin_t cover = transfer().ack_out().cover(pos);
+    for(int i=0; i<transfer().cb_installed; i++)
+        if (cover.layer()>=transfer().cb_agg[i])
+            transfer().callbacks[i](transfer().fd(),cover);  // FIXME
+    if (cover.layer() >= 5) // Arno: tested with 32K, presently = 2 ** 5 * chunk_size CHUNKSIZE
+       transfer().OnRecvData( pow((double)2,(double)5)*((double)file().chunk_size()) );
+    data_in_.bin = pos;
+
+    UpdateDIP(pos);
+    CleanHintOut(pos);
+    bytes_down_ += length;
+    global_bytes_down += length;
+    return pos;
+}
+
+
+void Channel::UpdateDIP(bin_t pos)
+{
+       if (!pos.is_none()) {
+               if (last_data_in_time_) {
+                       tint dip = NOW - last_data_in_time_;
+                       dip_avg_ = ( dip_avg_*3 + dip ) >> 2;
+               }
+               last_data_in_time_ = NOW;
+       }
+}
+
+
+void    Channel::OnAck (struct evbuffer *evb) {
+    bin_t ackd_pos = bin_fromUInt32(evbuffer_remove_32be(evb));
+    tint peer_time = evbuffer_remove_64be(evb); // FIXME 32
+    // FIXME FIXME: wrap around here
+    if (ackd_pos.is_none())
+        return; // likely, broken chunk/ insufficient hashes
+    if (file().size() && ackd_pos.base_offset()>=file().size_in_chunks()) {
+        char bin_name_buf[32];
+        eprintf("invalid ack: %s\n",ackd_pos.str(bin_name_buf));
+        return;
+    }
+    ack_in_.set(ackd_pos);
+
+    //fprintf(stderr,"OnAck: got bin %s is_complete %d\n", ackd_pos.str(), (int)ack_in_.is_complete_arno( file().ack_out().get_height() ));
+
+    int di = 0, ri = 0;
+    // find an entry for the send (data out) event
+    while (  di<data_out_.size() && ( data_out_[di]==tintbin() ||
+           !ackd_pos.contains(data_out_[di].bin) )  )
+        di++;
+    // FUTURE: delayed acks
+    // rule out retransmits
+    while (  ri<data_out_tmo_.size() && !ackd_pos.contains(data_out_tmo_[ri].bin) )
+        ri++;
+    char bin_name_buf[32];
+    dprintf("%s #%u %cack %s %lli\n",tintstr(),id_,
+            di==data_out_.size()?'?':'-',ackd_pos.str(bin_name_buf),peer_time);
+    if (di!=data_out_.size() && ri==data_out_tmo_.size()) { // not a retransmit
+            // round trip time calculations
+        tint rtt = NOW-data_out_[di].time;
+        rtt_avg_ = (rtt_avg_*7 + rtt) >> 3;
+        dev_avg_ = ( dev_avg_*3 + tintabs(rtt-rtt_avg_) ) >> 2;
+        assert(data_out_[di].time!=TINT_NEVER);
+            // one-way delay calculations
+        tint owd = peer_time - data_out_[di].time;
+        owd_cur_bin_ = 0;//(owd_cur_bin_+1) & 3;
+        owd_current_[owd_cur_bin_] = owd;
+        if ( owd_min_bin_start_+TINT_SEC*30 < NOW ) {
+            owd_min_bin_start_ = NOW;
+            owd_min_bin_ = (owd_min_bin_+1) & 3;
+            owd_min_bins_[owd_min_bin_] = TINT_NEVER;
+        }
+        if (owd_min_bins_[owd_min_bin_]>owd)
+            owd_min_bins_[owd_min_bin_] = owd;
+        dprintf("%s #%u sendctrl rtt %lli dev %lli based on %s\n",
+                tintstr(),id_,rtt_avg_,dev_avg_,data_out_[di].bin.str(bin_name_buf));
+        ack_rcvd_recent_++;
+        // early loss detection by packet reordering
+        for (int re=0; re<di-MAX_REORDERING; re++) {
+            if (data_out_[re]==tintbin())
+                continue;
+            ack_not_rcvd_recent_++;
+            data_out_tmo_.push_back(data_out_[re].bin);
+            dprintf("%s #%u Rdata %s\n",tintstr(),id_,data_out_.front().bin.str(bin_name_buf));
+            data_out_cap_ = bin_t::ALL;
+            data_out_[re] = tintbin();
+        }
+    }
+    if (di!=data_out_.size())
+        data_out_[di]=tintbin();
+    // clear zeroed items
+    while (!data_out_.empty() && ( data_out_.front()==tintbin() ||
+            ack_in_.is_filled(data_out_.front().bin) ) )
+        data_out_.pop_front();
+    assert(data_out_.empty() || data_out_.front().time!=TINT_NEVER);
+}
+
+
+void Channel::TimeoutDataOut ( ) {
+    // losses: timeouted packets
+    tint timeout = NOW - ack_timeout();
+    while (!data_out_.empty() &&
+        ( data_out_.front().time<timeout || data_out_.front()==tintbin() ) ) {
+        if (data_out_.front()!=tintbin() && ack_in_.is_empty(data_out_.front().bin)) {
+            ack_not_rcvd_recent_++;
+            data_out_cap_ = bin_t::ALL;
+            data_out_tmo_.push_back(data_out_.front().bin);
+            char bin_name_buf[32];
+            dprintf("%s #%u Tdata %s\n",tintstr(),id_,data_out_.front().bin.str(bin_name_buf));
+        }
+        data_out_.pop_front();
+    }
+    // clear retransmit queue of older items
+    while (!data_out_tmo_.empty() && data_out_tmo_.front().time<NOW-MAX_POSSIBLE_RTT)
+        data_out_tmo_.pop_front();
+}
+
+
+void Channel::OnHave (struct evbuffer *evb) {
+    bin_t ackd_pos = bin_fromUInt32(evbuffer_remove_32be(evb));
+    if (ackd_pos.is_none())
+        return; // wow, peer has hashes
+
+    // PPPLUG
+    if (ENABLE_VOD_PIECEPICKER) {
+               // Ric: check if we should set the size in the file transfer
+               if (transfer().availability().size() <= 0 && file().size() > 0)
+               {
+                       transfer().availability().setSize(file().size_in_chunks());
+               }
+               // Ric: update the availability if needed
+               transfer().availability().set(id_, ack_in_, ackd_pos);
+    }
+
+    ack_in_.set(ackd_pos);
+    char bin_name_buf[32];
+    dprintf("%s #%u -have %s\n",tintstr(),id_,ackd_pos.str(bin_name_buf));
+
+    //fprintf(stderr,"OnHave: got bin %s is_complete %d\n", ackd_pos.str(), IsComplete() );
+
+}
+
+
+void    Channel::OnHint (struct evbuffer *evb) {
+    bin_t hint = bin_fromUInt32(evbuffer_remove_32be(evb));
+    // FIXME: wake up here
+    hint_in_.push_back(hint);
+    char bin_name_buf[32];
+    dprintf("%s #%u -hint %s\n",tintstr(),id_,hint.str(bin_name_buf));
+}
+
+
+void Channel::OnHandshake (struct evbuffer *evb) {
+
+       uint32_t pcid = evbuffer_remove_32be(evb);
+    dprintf("%s #%u -hs %x\n",tintstr(),id_,pcid);
+
+    if (is_established() && pcid == 0) {
+       // Arno: received explicit close
+       peer_channel_id_ = 0; // == established -> false
+       Close();
+       return;
+    }
+
+    peer_channel_id_ = pcid;
+    // self-connection check
+    if (!SELF_CONN_OK) {
+        uint32_t try_id = DecodeID(peer_channel_id_);
+        if (channel(try_id) && !channel(try_id)->peer_channel_id_) {
+            peer_channel_id_ = 0;
+            Close();
+            return; // this is a self-connection
+        }
+    }
+
+    // FUTURE: channel forking
+    if (is_established())
+        dprintf("%s #%u established %s\n", tintstr(), id_, peer().str());
+}
+
+
+void Channel::OnPex (struct evbuffer *evb) {
+    uint32_t ipv4 = evbuffer_remove_32be(evb);
+    uint16_t port = evbuffer_remove_16be(evb);
+    Address addr(ipv4,port);
+    dprintf("%s #%u -pex %s\n",tintstr(),id_,addr.str());
+    if (transfer().OnPexIn(addr))
+        useless_pex_count_ = 0;
+    else
+    {
+               dprintf("%s #%u already channel to %s\n", tintstr(),id_,addr.str());
+        useless_pex_count_++;
+    }
+    pex_request_outstanding_ = false;
+}
+
+
+//FRAGRAND
+void Channel::OnRandomize (struct evbuffer *evb) {
+    dprintf("%s #%u -rand\n",tintstr(),id_ );
+       // Payload is 4 random bytes
+    uint32_t r = evbuffer_remove_32be(evb);
+}
+
+
+void    Channel::AddPex (struct evbuffer *evb) {
+       // Gertjan fix: Reverse PEX
+    // PEX messages sent to facilitate NAT/FW puncturing get priority
+    if (!reverse_pex_out_.empty()) {
+        do {
+            tintbin pex_peer = reverse_pex_out_.front();
+            reverse_pex_out_.pop_front();
+            if (channels[(int) pex_peer.bin.toUInt()] == NULL)
+                continue;
+            Address a = channels[(int) pex_peer.bin.toUInt()]->peer();
+            // Arno, 2012-02-28: Don't send private addresses to non-private peers.
+            if (!a.is_private() || (a.is_private() && peer().is_private()))
+            {
+               evbuffer_add_8(evb, SWIFT_PEX_ADD);
+               evbuffer_add_32be(evb, a.ipv4());
+               evbuffer_add_16be(evb, a.port());
+               dprintf("%s #%u +pex (reverse) %s\n",tintstr(),id_,a.str());
+            }
+        } while (!reverse_pex_out_.empty() && (SWIFT_MAX_NONDATA_DGRAM_SIZE-evbuffer_get_length(evb)) >= 7);
+
+        // Arno: 2012-02-23: Don't think this is right. Bit of DoS thing,
+        // that you only get back the addr of people that got your addr.
+        // Disable for now.
+        //return;
+    }
+
+    if (!pex_requested_)
+        return;
+
+    // Arno, 2012-02-28: Don't send private addresses to non-private peers.
+    int chid = 0, tries=0;
+    Address a;
+    while (true)
+    {
+       // Arno, 2011-10-03: Choosing Gertjan's RandomChannel over RevealChannel here.
+       chid = transfer().RandomChannel(id_);
+       if (chid==-1 || chid==id_ || tries > 5) {
+               pex_requested_ = false;
+               return;
+       }
+       a = channels[chid]->peer();
+       if (!a.is_private() || (a.is_private() && peer().is_private()))
+               break;
+       tries++;
+    }
+
+    evbuffer_add_8(evb, SWIFT_PEX_ADD);
+    evbuffer_add_32be(evb, a.ipv4());
+    evbuffer_add_16be(evb, a.port());
+    dprintf("%s #%u +pex %s\n",tintstr(),id_,a.str());
+
+    pex_requested_ = false;
+    /* Ensure that we don't add the same id to the reverse_pex_out_ queue
+       more than once. */
+    for (tbqueue::iterator i = channels[chid]->reverse_pex_out_.begin();
+            i != channels[chid]->reverse_pex_out_.end(); i++)
+        if ((int) (i->bin.toUInt()) == id_)
+            return;
+
+    dprintf("%s #%u adding pex for channel %u at time %s\n", tintstr(), chid,
+        id_, tintstr(NOW + 2 * TINT_SEC));
+    // Arno, 2011-10-03: should really be a queue of (tint,channel id(= uint32_t)) pairs.
+    channels[chid]->reverse_pex_out_.push_back(tintbin(NOW + 2 * TINT_SEC, bin_t(id_)));
+    if (channels[chid]->send_control_ == KEEP_ALIVE_CONTROL &&
+            channels[chid]->next_send_time_ > NOW + 2 * TINT_SEC)
+        channels[chid]->Reschedule();
+}
+
+void Channel::OnPexReq(void) {
+    dprintf("%s #%u -pex req\n", tintstr(), id_);
+    if (NOW > MIN_PEX_REQUEST_INTERVAL + last_pex_request_time_)
+        pex_requested_ = true;
+}
+
+void Channel::AddPexReq(struct evbuffer *evb) {
+    // Rate limit the number of PEX requests
+    if (NOW < next_pex_request_time_)
+        return;
+
+    // If no answer has been received from a previous request, count it as useless
+    if (pex_request_outstanding_)
+        useless_pex_count_++;
+
+    pex_request_outstanding_ = false;
+
+    // Initiate at most SWIFT_MAX_CONNECTIONS connections
+    if (transfer().hs_in_.size() >= SWIFT_MAX_CONNECTIONS ||
+            // Check whether this channel has been providing useful peer information
+            useless_pex_count_ > 2)
+    {
+       // Arno, 2012-02-23: Fix: Code doesn't recover from useless_pex_count_ > 2,
+       // let's just try again in 30s
+               useless_pex_count_ = 0;
+               next_pex_request_time_ = NOW + 30 * TINT_SEC;
+
+        return;
+    }
+
+    dprintf("%s #%u +pex req\n", tintstr(), id_);
+    evbuffer_add_8(evb, SWIFT_PEX_REQ);
+    /* Add a little more than the minimum interval, such that the other party is
+       less likely to drop it due to too high rate */
+    next_pex_request_time_ = NOW + MIN_PEX_REQUEST_INTERVAL * 1.1;
+    pex_request_outstanding_ = true;
+}
+
+
+
+/*
+ * Channel class methods
+ */
+
+void Channel::LibeventReceiveCallback(evutil_socket_t fd, short event, void *arg) {
+       // Called by libevent when a datagram is received on the socket
+    Time();
+    RecvDatagram(fd);
+    event_add(&evrecv, NULL);
+}
+
+void    Channel::RecvDatagram (evutil_socket_t socket) {
+    struct evbuffer *evb = evbuffer_new();
+    Address addr;
+    RecvFrom(socket, addr, evb);
+    size_t evboriglen = evbuffer_get_length(evb);
+#define return_log(...) { fprintf(stderr,__VA_ARGS__); evbuffer_free(evb); return; }
+    if (evbuffer_get_length(evb)<4)
+        return_log("socket layer weird: datagram shorter than 4 bytes from %s (prob ICMP unreach)\n",addr.str());
+    uint32_t mych = evbuffer_remove_32be(evb);
+    Sha1Hash hash;
+    Channel* channel = NULL;
+    if (mych==0) { // peer initiates handshake
+        if (evbuffer_get_length(evb)<1+4+1+4+Sha1Hash::SIZE)
+            return_log ("%s #0 incorrect size %i initial handshake packet %s\n",
+                        tintstr(),(int)evbuffer_get_length(evb),addr.str());
+        uint8_t hashid = evbuffer_remove_8(evb);
+        if (hashid!=SWIFT_HASH)
+            return_log ("%s #0 no hash in the initial handshake %s\n",
+                        tintstr(),addr.str());
+        bin_t pos = bin_fromUInt32(evbuffer_remove_32be(evb));
+        if (!pos.is_all())
+            return_log ("%s #0 that is not the root hash %s\n",tintstr(),addr.str());
+        hash = evbuffer_remove_hash(evb);
+        FileTransfer* ft = FileTransfer::Find(hash);
+        if (!ft)
+            return_log ("%s #0 hash %s unknown, requested by %s\n",tintstr(),hash.hex().c_str(),addr.str());
+        dprintf("%s #0 -hash ALL %s\n",tintstr(),hash.hex().c_str());
+
+        // Arno, 2012-02-27: Check for duplicate channel
+        Channel* existchannel = ft->FindChannel(addr,NULL);
+        if (existchannel)
+        {
+                       // Arno: 2011-10-13: Ignore if established, otherwise consider
+                       // it a concurrent connection attempt.
+                       if (existchannel->is_established()) {
+                               // ARNOTODO: Read complete handshake here so we know whether
+                               // attempt is to new channel or to existing. Currently read
+                               // in OnHandshake()
+                               //
+                               return_log("%s #0 have a channel already to %s\n",tintstr(),addr.str());
+                       } else {
+                               channel = existchannel;
+                               //fprintf(stderr,"Channel::RecvDatagram: HANDSHAKE: reuse channel %s\n", channel->peer_.str() );
+                       }
+        }
+        if (channel == NULL) {
+               //fprintf(stderr,"Channel::RecvDatagram: HANDSHAKE: create new channel %s\n", addr.str() );
+               channel = new Channel(ft, socket, addr);
+        }
+        //fprintf(stderr,"CHANNEL INCOMING DEF hass %s is id %d\n",hash.hex().c_str(),channel->id());
+
+    } else { // peer responds to my handshake (and other messages)
+        mych = DecodeID(mych);
+        if (mych>=channels.size())
+            return_log("%s invalid channel #%u, %s\n",tintstr(),mych,addr.str());
+        channel = channels[mych];
+        if (!channel)
+            return_log ("%s #%u is already closed\n",tintstr(),mych);
+               if (channel->IsDiffSenderOrDuplicate(addr,mych)) {
+                       channel->Schedule4Close();
+                       return;
+               }
+        channel->own_id_mentioned_ = true;
+    }
+    channel->raw_bytes_down_ += evboriglen;
+    //dprintf("recvd %i bytes for %i\n",data.size(),channel->id);
+    bool wasestablished = channel->is_established();
+
+    dprintf("%s #%u peer %s recv_peer %s addr %s\n", tintstr(),mych, channel->peer().str(), channel->recv_peer().str(), addr.str() );
+
+    channel->Recv(evb);
+
+    evbuffer_free(evb);
+    //SAFECLOSE
+    if (wasestablished && !channel->is_established()) {
+       // Arno, 2012-01-26: Received an explict close, clean up channel, safely.
+       channel->Schedule4Close();
+    }
+}
+
+
+
+/*
+ * Channel instance methods
+ */
+
+void Channel::CloseChannelByAddress(const Address &addr)
+{
+       // fprintf(stderr,"CloseChannelByAddress: address is %s\n", addr.str() );
+    std::vector<Channel *>::iterator iter;
+    for (iter = channels.begin(); iter != channels.end(); iter++)
+    {
+               Channel *c = *iter;
+               if (c != NULL && c->peer_ == addr)
+               {
+                       // ARNOSMPTODO: will do another send attempt before not being
+                       // Rescheduled.
+                       c->peer_channel_id_ = 0; // established->false, do no more sending
+                       c->Schedule4Close();
+                       break;
+               }
+    }
+}
+
+
+void Channel::Close () {
+
+       this->SwitchSendControl(CLOSE_CONTROL);
+
+    if (is_established())
+       this->Send(); // Arno: send explicit close
+
+    if (ENABLE_VOD_PIECEPICKER) {
+               // Ric: remove it's binmap from the availability
+               transfer().availability().remove(id_, ack_in_);
+    }
+
+    // SAFECLOSE
+    // Arno: ensure LibeventSendCallback is no longer called with ptr to this Channel
+    ClearEvents();
+}
+
+
+void Channel::Reschedule () {
+
+       // Arno: CAREFUL: direct send depends on diff between next_send_time_ and
+       // NOW to be 0, so any calls to Time in between may put things off. Sigh.
+       Time();
+    next_send_time_ = NextSendTime();
+    if (next_send_time_!=TINT_NEVER) {
+
+       assert(next_send_time_<NOW+TINT_MIN);
+        tint duein = next_send_time_-NOW;
+        if (duein <= 0) {
+               // Arno, 2011-10-18: libevent's timer implementation appears to be
+               // really slow, i.e., timers set for 100 usec from now get called
+               // at least two times later :-( Hence, for sends after receives
+               // perform them directly.
+               dprintf("%s #%u requeue direct send\n",tintstr(),id_);
+               LibeventSendCallback(-1,EV_TIMEOUT,this);
+        }
+        else {
+               if (evsend_ptr_ != NULL) {
+                       struct timeval duetv = *tint2tv(duein);
+                       evtimer_add(evsend_ptr_,&duetv);
+                       dprintf("%s #%u requeue for %s in %lli\n",tintstr(),id_,tintstr(next_send_time_), duein);
+               }
+               else
+                       dprintf("%s #%u cannot requeue for %s, closed\n",tintstr(),id_,tintstr(next_send_time_));
+        }
+    } else {
+       // SAFECLOSE
+        dprintf("%s #%u resched, will close\n",tintstr(),id_);
+               this->Schedule4Close();
+    }
+}
+
+
+/*
+ * Channel class methods
+ */
+void Channel::LibeventSendCallback(int fd, short event, void *arg) {
+
+       // Called by libevent when it is the requested send time.
+    Time();
+    Channel * sender = (Channel*) arg;
+    if (NOW<sender->next_send_time_-TINT_MSEC)
+        dprintf("%s #%u suspicious send %s<%s\n",tintstr(),
+                sender->id(),tintstr(NOW),tintstr(sender->next_send_time_));
+    if (sender->next_send_time_ != TINT_NEVER)
+       sender->Send();
+}
+
diff --git a/src/libswift_udp/serialize.h b/src/libswift_udp/serialize.h
new file mode 100644 (file)
index 0000000..cc10b4b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * serialize.h
+ *
+ *  Created by Arno Bakker
+ *  Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#ifndef SWIFT_SERIALIZE_H_
+#define SWIFT_SERIALIZE_H_
+
+#include <stdio.h>
+
+#define fprintf_retiffail(...) { if (fprintf(__VA_ARGS__) < 0) { return -1; }}
+#define fscanf_retiffail(...) { if (fscanf(__VA_ARGS__) == EOF) { return -1; }}
+
+class Serializable {
+  public:
+       virtual int serialize(FILE *fp) = 0;
+       virtual int deserialize(FILE *fp) = 0;
+};
+
+#endif /* SWIFT_SERIALIZE_H_ */
diff --git a/src/libswift_udp/sha1.cpp b/src/libswift_udp/sha1.cpp
new file mode 100644 (file)
index 0000000..e465f53
--- /dev/null
@@ -0,0 +1,289 @@
+// licensed under the GPL v2 as part of the git project http://git-scm.com/
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+/* this is only to get definitions for memcpy(), ntohl() and htonl() */
+//#include "../git-compat-util.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n)    SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n)    SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r)    (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n)    SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n)    SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+  #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+  #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+  #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+    defined(__ppc__) || defined(__ppc64__) || \
+    defined(__powerpc__) || defined(__powerpc64__) || \
+    defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p)    ntohl(*(unsigned int *)(p))
+#define put_be32(p, v)    do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p)    ( \
+    (*((unsigned char *)(p) + 0) << 24) | \
+    (*((unsigned char *)(p) + 1) << 16) | \
+    (*((unsigned char *)(p) + 2) <<  8) | \
+    (*((unsigned char *)(p) + 3) <<  0) )
+#define put_be32(p, v)    do { \
+    unsigned int __v = (v); \
+    *((unsigned char *)(p) + 0) = __v >> 24; \
+    *((unsigned char *)(p) + 1) = __v >> 16; \
+    *((unsigned char *)(p) + 2) = __v >>  8; \
+    *((unsigned char *)(p) + 3) = __v >>  0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+    unsigned int TEMP = input(t); setW(t, TEMP); \
+    E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+    B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E)  SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) ,  0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+    unsigned int A,B,C,D,E;
+    unsigned int array[16];
+
+    A = ctx->H[0];
+    B = ctx->H[1];
+    C = ctx->H[2];
+    D = ctx->H[3];
+    E = ctx->H[4];
+
+    /* Round 1 - iterations 0-16 take their input from 'data' */
+    T_0_15( 0, A, B, C, D, E);
+    T_0_15( 1, E, A, B, C, D);
+    T_0_15( 2, D, E, A, B, C);
+    T_0_15( 3, C, D, E, A, B);
+    T_0_15( 4, B, C, D, E, A);
+    T_0_15( 5, A, B, C, D, E);
+    T_0_15( 6, E, A, B, C, D);
+    T_0_15( 7, D, E, A, B, C);
+    T_0_15( 8, C, D, E, A, B);
+    T_0_15( 9, B, C, D, E, A);
+    T_0_15(10, A, B, C, D, E);
+    T_0_15(11, E, A, B, C, D);
+    T_0_15(12, D, E, A, B, C);
+    T_0_15(13, C, D, E, A, B);
+    T_0_15(14, B, C, D, E, A);
+    T_0_15(15, A, B, C, D, E);
+
+    /* Round 1 - tail. Input from 512-bit mixing array */
+    T_16_19(16, E, A, B, C, D);
+    T_16_19(17, D, E, A, B, C);
+    T_16_19(18, C, D, E, A, B);
+    T_16_19(19, B, C, D, E, A);
+
+    /* Round 2 */
+    T_20_39(20, A, B, C, D, E);
+    T_20_39(21, E, A, B, C, D);
+    T_20_39(22, D, E, A, B, C);
+    T_20_39(23, C, D, E, A, B);
+    T_20_39(24, B, C, D, E, A);
+    T_20_39(25, A, B, C, D, E);
+    T_20_39(26, E, A, B, C, D);
+    T_20_39(27, D, E, A, B, C);
+    T_20_39(28, C, D, E, A, B);
+    T_20_39(29, B, C, D, E, A);
+    T_20_39(30, A, B, C, D, E);
+    T_20_39(31, E, A, B, C, D);
+    T_20_39(32, D, E, A, B, C);
+    T_20_39(33, C, D, E, A, B);
+    T_20_39(34, B, C, D, E, A);
+    T_20_39(35, A, B, C, D, E);
+    T_20_39(36, E, A, B, C, D);
+    T_20_39(37, D, E, A, B, C);
+    T_20_39(38, C, D, E, A, B);
+    T_20_39(39, B, C, D, E, A);
+
+    /* Round 3 */
+    T_40_59(40, A, B, C, D, E);
+    T_40_59(41, E, A, B, C, D);
+    T_40_59(42, D, E, A, B, C);
+    T_40_59(43, C, D, E, A, B);
+    T_40_59(44, B, C, D, E, A);
+    T_40_59(45, A, B, C, D, E);
+    T_40_59(46, E, A, B, C, D);
+    T_40_59(47, D, E, A, B, C);
+    T_40_59(48, C, D, E, A, B);
+    T_40_59(49, B, C, D, E, A);
+    T_40_59(50, A, B, C, D, E);
+    T_40_59(51, E, A, B, C, D);
+    T_40_59(52, D, E, A, B, C);
+    T_40_59(53, C, D, E, A, B);
+    T_40_59(54, B, C, D, E, A);
+    T_40_59(55, A, B, C, D, E);
+    T_40_59(56, E, A, B, C, D);
+    T_40_59(57, D, E, A, B, C);
+    T_40_59(58, C, D, E, A, B);
+    T_40_59(59, B, C, D, E, A);
+
+    /* Round 4 */
+    T_60_79(60, A, B, C, D, E);
+    T_60_79(61, E, A, B, C, D);
+    T_60_79(62, D, E, A, B, C);
+    T_60_79(63, C, D, E, A, B);
+    T_60_79(64, B, C, D, E, A);
+    T_60_79(65, A, B, C, D, E);
+    T_60_79(66, E, A, B, C, D);
+    T_60_79(67, D, E, A, B, C);
+    T_60_79(68, C, D, E, A, B);
+    T_60_79(69, B, C, D, E, A);
+    T_60_79(70, A, B, C, D, E);
+    T_60_79(71, E, A, B, C, D);
+    T_60_79(72, D, E, A, B, C);
+    T_60_79(73, C, D, E, A, B);
+    T_60_79(74, B, C, D, E, A);
+    T_60_79(75, A, B, C, D, E);
+    T_60_79(76, E, A, B, C, D);
+    T_60_79(77, D, E, A, B, C);
+    T_60_79(78, C, D, E, A, B);
+    T_60_79(79, B, C, D, E, A);
+
+    ctx->H[0] += A;
+    ctx->H[1] += B;
+    ctx->H[2] += C;
+    ctx->H[3] += D;
+    ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+    ctx->size = 0;
+
+    /* Initialize H with the magic constants (see FIPS180 for constants) */
+    ctx->H[0] = 0x67452301;
+    ctx->H[1] = 0xefcdab89;
+    ctx->H[2] = 0x98badcfe;
+    ctx->H[3] = 0x10325476;
+    ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+    int lenW = ctx->size & 63;
+
+    ctx->size += len;
+
+    /* Read the data into W and process blocks as they get full */
+    if (lenW) {
+        int left = 64 - lenW;
+        if (len < left)
+            left = len;
+        memcpy(lenW + (char *)ctx->W, data, left);
+        lenW = (lenW + left) & 63;
+        len -= left;
+        data = ((const char *)data + left);
+        if (lenW)
+            return;
+        blk_SHA1_Block(ctx, ctx->W);
+    }
+    while (len >= 64) {
+        blk_SHA1_Block(ctx, (const unsigned int*)data);
+        data = ((const char *)data + 64);
+        len -= 64;
+    }
+    if (len)
+        memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+    static const unsigned char pad[64] = { 0x80 };
+    unsigned int padlen[2];
+    int i;
+
+    /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+    padlen[0] = htonl(ctx->size >> 29);
+    padlen[1] = htonl(ctx->size << 3);
+
+    i = ctx->size & 63;
+    blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+    blk_SHA1_Update(ctx, padlen, 8);
+
+    /* Output hash */
+    for (i = 0; i < 5; i++)
+        put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/src/libswift_udp/sha1.h b/src/libswift_udp/sha1.h
new file mode 100644 (file)
index 0000000..3b804b0
--- /dev/null
@@ -0,0 +1,23 @@
+// licensed under the GPL v2 as part of the git project http://git-scm.com/
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+#ifndef GIT_SHA1
+#define GIT_SHA1
+
+typedef struct {
+    unsigned long long size;
+    unsigned int H[5];
+    unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#endif
+
diff --git a/src/libswift_udp/statsgw.cpp b/src/libswift_udp/statsgw.cpp
new file mode 100644 (file)
index 0000000..2d77c68
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ *  statsgw.cpp
+ *  HTTP server for showing some DL stats via SwarmPlayer 3000's webUI,
+ *  libevent based
+ *
+ *  Created by Victor Grishchenko, Arno Bakker
+ *  Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#include <event2/http.h>
+
+using namespace swift;
+
+int statsgw_reqs_count = 0;
+
+
+uint64_t statsgw_last_down;
+uint64_t statsgw_last_up;
+tint statsgw_last_time = 0;
+bool statsgw_quit_process=false;
+struct evhttp *statsgw_event;
+struct evhttp_bound_socket *statsgw_handle;
+
+
+const char *top_page = "<!doctype html> \
+<html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
+<head> \
+       <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> \
+       <meta http-equiv=\"cache-control\" content=\"Private\" /> \
+       <meta http-equiv=\"Refresh\" content=\"2;url=http://127.0.0.1:6876/webUI\" /> \
+       <title>Swift Web Interface</title> \
+</head> \
+<body style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;padding:20px; margin:20px;\"> \
+<div style=\"padding:40;\"> \
+<div style=\"width:400; float:left; padding:20px\"> \
+<h1 style=\"font-size: 20px;\"> Swift swarms: </h1>";
+
+const char *swarm_page_templ = " \
+<h2 style=\"font-size: 18px; padding-top: 10px; padding-left: 20px; margin-bottom: 0px; color:#30bf00;\">Root hash: %s</h2> \
+   <ul style=\"padding-left: 40px;\"> \
+    <li>Progress:       %d%c \
+    <li>Download speed: %d KB/s \
+    <li>Upload speed:   %d KB/s \
+</ul>";
+
+
+const char *bottom_page = " \
+<button style=\"color:white; background-color:#4f84dc; width:80;height:50; font-size:18px; font-weight:bold; text-shadow: #6374AB 2px 2px 2px;\" \
+onClick=\"window.location='http://127.0.0.1:6876/webUI/exit';\">Quit Swift</button> \
+</div> \
+</div> \
+</body> \
+</html>";
+
+
+const char *exit_page = "<!doctype html> \
+<html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
+<head> \
+       <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> \
+       <meta http-equiv=\"cache-control\" content=\"Private\" /> \
+       <title>Swift Web Interface</title> \
+</head> \
+<body style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;padding:20px; margin:20px;\"> \
+<div style=\"padding:40;\"> \
+<div style=\"width:400; float:left; padding:20px\"> \
+<h1 style=\"font-size: 20px;\"> Swift is no longer running. </h1> \
+</div> \
+</div> \
+</body> \
+</html>";
+
+
+static void StatsGwNewRequestCallback (struct evhttp_request *evreq, void *arg);
+
+
+void StatsExitCallback(struct evhttp_request *evreq)
+{
+       char contlenstr[1024];
+       sprintf(contlenstr,"%i",strlen(exit_page));
+       struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
+       evhttp_add_header(headers, "Connection", "close" );
+       evhttp_add_header(headers, "Content-Type", "text/html" );
+       evhttp_add_header(headers, "Content-Length", contlenstr );
+       evhttp_add_header(headers, "Accept-Ranges", "none" );
+
+    // Construct evbuffer and send via chunked encoding
+    struct evbuffer *evb = evbuffer_new();
+    int ret = evbuffer_add(evb,exit_page,strlen(exit_page));
+    if (ret < 0) {
+       print_error("statsgw: ExitCallback: error evbuffer_add");
+        return;
+    }
+
+       evhttp_send_reply(evreq, 200, "OK", evb);
+       evbuffer_free(evb);
+}
+
+
+bool StatsQuit()
+{
+       return statsgw_quit_process;
+}
+
+
+void StatsOverviewCallback(struct evhttp_request *evreq)
+{
+       tint nu = NOW;
+       uint64_t down = Channel::global_raw_bytes_down;
+       uint64_t up = Channel::global_raw_bytes_up;
+
+       int dspeed = 0, uspeed = 0;
+       tint tdiff = (nu - statsgw_last_time)/1000000;
+       if (tdiff > 0) {
+               dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
+               uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
+       }
+    //statsgw_last_down = down;
+    //statsgw_last_up = up;
+
+
+       char bodystr[102400];
+    strcpy(bodystr,"");
+    strcat(bodystr,top_page);
+
+    for (int i=0; i<swift::FileTransfer::files.size(); i++)
+    {
+       FileTransfer *ft = swift::FileTransfer::files[i];
+       if (ft != NULL)
+       {
+               int fd = ft->fd();
+                       uint64_t total = (int)swift::Size(fd);
+                       uint64_t down  = (int)swift::Complete(fd);
+                       int perc = (int)((down * 100) / total);
+
+                       char roothashhexstr[256];
+                       sprintf(roothashhexstr,"%s", RootMerkleHash(fd).hex().c_str() );
+
+                       char templ[1024];
+                       sprintf(templ,swarm_page_templ,roothashhexstr, perc, '%', dspeed, uspeed );
+                       strcat(bodystr,templ);
+       }
+    }
+
+       strcat(bodystr,bottom_page);
+
+       char contlenstr[1024];
+       sprintf(contlenstr,"%i",strlen(bodystr));
+       struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
+       evhttp_add_header(headers, "Connection", "close" );
+       evhttp_add_header(headers, "Content-Type", "text/html" );
+       evhttp_add_header(headers, "Content-Length", contlenstr );
+       evhttp_add_header(headers, "Accept-Ranges", "none" );
+
+       // Construct evbuffer and send via chunked encoding
+       struct evbuffer *evb = evbuffer_new();
+       int ret = evbuffer_add(evb,bodystr,strlen(bodystr));
+       if (ret < 0) {
+               print_error("statsgw: OverviewCallback: error evbuffer_add");
+               return;
+       }
+
+       evhttp_send_reply(evreq, 200, "OK", evb);
+       evbuffer_free(evb);
+}
+
+
+void StatsGetSpeedCallback(struct evhttp_request *evreq)
+{
+       if (statsgw_last_time == 0)
+       {
+               statsgw_last_time = NOW-1000000;
+       }
+
+       tint nu = Channel::Time();
+       uint64_t down = Channel::global_raw_bytes_down;
+       uint64_t up = Channel::global_raw_bytes_up;
+
+       int dspeed = 0, uspeed = 0;
+       tint tdiff = (nu - statsgw_last_time)/1000000;
+       if (tdiff > 0) {
+               dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
+               uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
+       }
+    statsgw_last_down = down;
+    statsgw_last_up = up;
+    statsgw_last_time = nu;
+
+    // Arno: PDD+ wants content speeds too
+    double contentdownspeed = 0.0, contentupspeed = 0.0;
+    uint32_t nleech=0,nseed=0;
+    for (int i=0; i<swift::FileTransfer::files.size(); i++)
+    {
+       FileTransfer *ft = swift::FileTransfer::files[i];
+       if (ft != NULL)
+       {
+               contentdownspeed += ft->GetCurrentSpeed(DDIR_DOWNLOAD);
+               contentupspeed += ft->GetCurrentSpeed(DDIR_UPLOAD);
+               nleech += ft->GetNumLeechers();
+               nseed += ft->GetNumSeeders();
+       }
+    }
+    int cdownspeed = (int)(contentdownspeed/1024.0);
+    int cupspeed = (int)(contentupspeed/1024.0);
+
+       char speedstr[1024];
+       sprintf(speedstr,"{\"downspeed\": %d, \"success\": \"true\", \"upspeed\": %d, \"cdownspeed\": %d, \"cupspeed\": %d, \"nleech\": %d, \"nseed\": %d}", dspeed, uspeed, cdownspeed, cupspeed, nleech, nseed );
+
+       char contlenstr[1024];
+       sprintf(contlenstr,"%i",strlen(speedstr));
+       struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
+       evhttp_add_header(headers, "Connection", "close" );
+       evhttp_add_header(headers, "Content-Type", "application/json" );
+       evhttp_add_header(headers, "Content-Length", contlenstr );
+       evhttp_add_header(headers, "Accept-Ranges", "none" );
+
+       // Construct evbuffer and send via chunked encoding
+       struct evbuffer *evb = evbuffer_new();
+       int ret = evbuffer_add(evb,speedstr,strlen(speedstr));
+       if (ret < 0) {
+               print_error("statsgw: GetSpeedCallback: error evbuffer_add");
+               return;
+       }
+
+       evhttp_send_reply(evreq, 200, "OK", evb);
+       evbuffer_free(evb);
+}
+
+
+void StatsGwNewRequestCallback (struct evhttp_request *evreq, void *arg) {
+
+    dprintf("%s @%i http new request\n",tintstr(),statsgw_reqs_count);
+    statsgw_reqs_count++;
+
+    if (evhttp_request_get_command(evreq) != EVHTTP_REQ_GET) {
+            return;
+    }
+
+    // Parse URI
+    const char *uri = evhttp_request_get_uri(evreq);
+    //struct evkeyvalq *headers =      evhttp_request_get_input_headers(evreq);
+    //const char *contentrangestr =evhttp_find_header(headers,"Content-Range");
+
+    fprintf(stderr,"statsgw: GOT %s\n", uri);
+
+    if (strstr(uri,"get_speed_info") != NULL)
+    {
+       StatsGetSpeedCallback(evreq);
+    }
+    else if (!strncmp(uri,"/webUI/exit",strlen("/webUI/exit")) || statsgw_quit_process)
+    {
+       statsgw_quit_process = true;
+       StatsExitCallback(evreq);
+    }
+    else if (!strncmp(uri,"/webUI",strlen("/webUI")))
+    {
+       StatsOverviewCallback(evreq);
+    }
+}
+
+
+bool InstallStatsGateway (struct event_base *evbase,Address bindaddr) {
+       // Arno, 2011-10-04: From libevent's http-server.c example
+
+       /* Create a new evhttp object to handle requests. */
+       statsgw_event = evhttp_new(evbase);
+       if (!statsgw_event) {
+               print_error("statsgw: evhttp_new failed");
+               return false;
+       }
+
+       /* Install callback for all requests */
+       evhttp_set_gencb(statsgw_event, StatsGwNewRequestCallback, NULL);
+
+       /* Now we tell the evhttp what port to listen on */
+       statsgw_handle = evhttp_bind_socket_with_handle(statsgw_event, bindaddr.ipv4str(), bindaddr.port());
+       if (!statsgw_handle) {
+               print_error("statsgw: evhttp_bind_socket_with_handle failed");
+               return false;
+       }
+
+       return true;
+}
diff --git a/src/libswift_udp/swift.cpp b/src/libswift_udp/swift.cpp
new file mode 100644 (file)
index 0000000..c4e5b3b
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ *  swift.cpp
+ *  swift the multiparty transport protocol
+ *
+ *  Created by Victor Grishchenko on 2/15/10.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "compat.h"
+#include "swift.h"
+#include <cfloat>
+
+using namespace swift;
+
+
+// Local constants
+#define RESCAN_DIR_INTERVAL    30 // seconds
+
+
+// Local prototypes
+#define quit(...) {fprintf(stderr,__VA_ARGS__); exit(1); }
+int OpenSwiftFile(const TCHAR* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, uint32_t chunk_size);
+int OpenSwiftDirectory(const TCHAR* dirname, Address tracker, bool force_check_diskvshash, uint32_t chunk_size);
+
+void ReportCallback(int fd, short event, void *arg);
+void EndCallback(int fd, short event, void *arg);
+void RescanDirCallback(int fd, short event, void *arg);
+
+
+// Gateway stuff
+bool InstallHTTPGateway(struct event_base *evbase,Address addr,uint32_t chunk_size, double *maxspeed);
+bool InstallStatsGateway(struct event_base *evbase,Address addr);
+bool InstallCmdGateway (struct event_base *evbase,Address cmdaddr,Address httpaddr);
+bool HTTPIsSending();
+bool StatsQuit();
+void CmdGwUpdateDLStatesCallback();
+
+
+// Global variables
+struct event evreport, evrescan, evend;
+int single_fd = -1;
+bool file_enable_checkpoint = false;
+bool file_checkpointed = false;
+bool report_progress = false;
+bool quiet=false;
+bool exitoncomplete=false;
+bool httpgw_enabled=false,cmdgw_enabled=false;
+// Gertjan fix
+bool do_nat_test = false;
+
+char *scan_dirname = 0;
+uint32_t chunk_size = SWIFT_DEFAULT_CHUNK_SIZE;
+Address tracker;
+
+
+int main (int argc, char** argv)
+{
+    static struct option long_options[] =
+    {
+        {"hash",    required_argument, 0, 'h'},
+        {"file",    required_argument, 0, 'f'},
+        {"dir",     required_argument, 0, 'd'}, // SEEDDIR reuse
+        {"listen",  required_argument, 0, 'l'},
+        {"tracker", required_argument, 0, 't'},
+        {"debug",   no_argument, 0, 'D'},
+        {"progress",no_argument, 0, 'p'},
+        {"httpgw",  required_argument, 0, 'g'},
+        {"wait",    optional_argument, 0, 'w'},
+        {"nat-test",no_argument, 0, 'N'},
+        {"statsgw", required_argument, 0, 's'}, // SWIFTPROC
+        {"cmdgw",   required_argument, 0, 'c'}, // SWIFTPROC
+        {"destdir", required_argument, 0, 'o'}, // SWIFTPROC
+        {"uprate",  required_argument, 0, 'u'}, // RATELIMIT
+        {"downrate",required_argument, 0, 'y'}, // RATELIMIT
+        {"checkpoint",no_argument, 0, 'H'},
+        {"chunksize",required_argument, 0, 'z'}, // CHUNKSIZE
+        {"printurl",no_argument, 0, 'm'},
+        {0, 0, 0, 0}
+    };
+
+    Sha1Hash root_hash;
+    char* filename = 0;
+    const char *destdir = 0, *trackerargstr = 0; // UNICODE?
+    bool printurl=false;
+    Address bindaddr;
+    Address httpaddr;
+    Address statsaddr;
+    Address cmdaddr;
+    tint wait_time = 0;
+    double maxspeed[2] = {DBL_MAX,DBL_MAX};
+
+    LibraryInit();
+    Channel::evbase = event_base_new();
+
+    int c,n;
+    while ( -1 != (c = getopt_long (argc, argv, ":h:f:d:l:t:D:pg:s:c:o:u:y:z:wBNHm", long_options, 0)) ) {
+        switch (c) {
+            case 'h':
+                if (strlen(optarg)!=40)
+                    quit("SHA1 hash must be 40 hex symbols\n");
+                root_hash = Sha1Hash(true,optarg); // FIXME ambiguity
+                if (root_hash==Sha1Hash::ZERO)
+                    quit("SHA1 hash must be 40 hex symbols\n");
+                break;
+            case 'f':
+                filename = strdup(optarg);
+                break;
+            case 'd':
+                scan_dirname = strdup(optarg);
+                break;
+            case 'l':
+                bindaddr = Address(optarg);
+                if (bindaddr==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                wait_time = TINT_NEVER;
+                break;
+            case 't':
+                tracker = Address(optarg);
+                trackerargstr = strdup(optarg); // UNICODE
+                if (tracker==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                SetTracker(tracker);
+                break;
+            case 'D':
+                Channel::debug_file = optarg ? fopen(optarg,"a") : stderr;
+                break;
+            // Arno hack: get opt diff Win32 doesn't allow -D without arg
+            case 'B':
+               fprintf(stderr,"SETTING DEBUG TO STDOUT\n");
+                Channel::debug_file = stderr;
+                break;
+            case 'p':
+                report_progress = true;
+                break;
+            case 'g':
+               httpgw_enabled = true;
+                httpaddr = Address(optarg);
+                               wait_time = TINT_NEVER; // seed
+                break;
+            case 'w':
+                if (optarg) {
+                    char unit = 'u';
+                    if (sscanf(optarg,"%lli%c",&wait_time,&unit)!=2)
+                        quit("time format: 1234[umsMHD], e.g. 1M = one minute\n");
+
+                    switch (unit) {
+                        case 'D': wait_time *= 24;
+                        case 'H': wait_time *= 60;
+                        case 'M': wait_time *= 60;
+                        case 's': wait_time *= 1000;
+                        case 'm': wait_time *= 1000;
+                        case 'u': break;
+                        default:  quit("time format: 1234[umsMHD], e.g. 1D = one day\n");
+                    }
+                } else
+                    wait_time = TINT_NEVER;
+                break;
+            case 'N': // Gertjan fix
+                do_nat_test = true;
+                break;
+            case 's': // SWIFTPROC
+                statsaddr = Address(optarg);
+                if (statsaddr==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                break;
+            case 'c': // SWIFTPROC
+               cmdgw_enabled = true;
+                cmdaddr = Address(optarg);
+                if (cmdaddr==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                wait_time = TINT_NEVER; // seed
+                break;
+            case 'o': // SWIFTPROC
+                destdir = strdup(optarg); // UNICODE
+                break;
+            case 'u': // RATELIMIT
+               n = sscanf(optarg,"%lf",&maxspeed[DDIR_UPLOAD]);
+               if (n != 1)
+                       quit("uprate must be KiB/s as float\n");
+               maxspeed[DDIR_UPLOAD] *= 1024.0;
+               break;
+            case 'y': // RATELIMIT
+               n = sscanf(optarg,"%lf",&maxspeed[DDIR_DOWNLOAD]);
+               if (n != 1)
+                       quit("downrate must be KiB/s as float\n");
+               maxspeed[DDIR_DOWNLOAD] *= 1024.0;
+               break;
+            case 'H': //CHECKPOINT
+                file_enable_checkpoint = true;
+                break;
+            case 'z': // CHUNKSIZE
+               n = sscanf(optarg,"%i",&chunk_size);
+               if (n != 1)
+                       quit("chunk size must be bytes as int\n");
+               break;
+            case 'm': // printurl
+               printurl = true;
+               quiet = true;
+               wait_time = 0;
+               break;
+        }
+
+    }   // arguments parsed
+
+
+    if (httpgw_enabled)
+    {
+       // Change current directory to a temporary one
+#ifdef _WIN32
+       if (destdir == 0) {
+               std::string destdirstr = gettmpdir();
+               !::SetCurrentDirectory(destdirstr.c_str());
+       }
+       else
+               !::SetCurrentDirectory(destdir);
+        TCHAR szDirectory[MAX_PATH] = "";
+
+        !::GetCurrentDirectory(sizeof(szDirectory) - 1, szDirectory);
+        fprintf(stderr,"CWD %s\n",szDirectory);
+#else
+        if (destdir == 0)
+               chdir(gettmpdir().c_str());
+        else
+               chdir(destdir);
+#endif
+    }
+      
+    if (bindaddr!=Address()) { // seeding
+        if (Listen(bindaddr)<=0)
+            quit("cant listen to %s\n",bindaddr.str())
+    } else if (tracker!=Address() || httpgw_enabled || cmdgw_enabled) { // leeching
+       evutil_socket_t sock = INVALID_SOCKET;
+        for (int i=0; i<=10; i++) {
+            bindaddr = Address((uint32_t)INADDR_ANY,0);
+            sock = Listen(bindaddr);
+            if (sock>0)
+                break;
+            if (i==10)
+                quit("cant listen on %s\n",bindaddr.str());
+        }
+        if (!quiet)
+               fprintf(stderr,"swift: My listen port is %d\n", BoundAddress(sock).port() );
+    }
+
+    if (tracker!=Address())
+        SetTracker(tracker);
+
+    if (httpgw_enabled)
+        InstallHTTPGateway(Channel::evbase,httpaddr,chunk_size,maxspeed);
+    if (cmdgw_enabled)
+               InstallCmdGateway(Channel::evbase,cmdaddr,httpaddr);
+
+    // TRIALM36: Allow browser to retrieve stats via AJAX and as HTML page
+    if (statsaddr != Address())
+       InstallStatsGateway(Channel::evbase,statsaddr);
+
+
+    if (!cmdgw_enabled && !httpgw_enabled)
+    {
+               int ret = -1;
+               if (filename || root_hash != Sha1Hash::ZERO) {
+                       // Single file
+                       if (root_hash!=Sha1Hash::ZERO && !filename)
+                               filename = strdup(root_hash.hex().c_str());
+
+                       single_fd = OpenSwiftFile(filename,root_hash,Address(),false,chunk_size);
+                       if (single_fd < 0)
+                               quit("cannot open file %s",filename);
+                       if (printurl) {
+                               if (trackerargstr == NULL)
+                                       trackerargstr = "";
+                               printf("tswift://%s/%s$%i\n", trackerargstr, RootMerkleHash(single_fd).hex().c_str(), chunk_size);
+
+                               // Arno, 2012-01-04: LivingLab: Create checkpoint such that content
+                               // can be copied to scanned dir and quickly loaded
+                               swift::Checkpoint(single_fd);
+                       }
+                       else
+                               printf("Root hash: %s\n", RootMerkleHash(single_fd).hex().c_str());
+
+                       // RATELIMIT
+                       FileTransfer *ft = FileTransfer::file(single_fd);
+                       ft->SetMaxSpeed(DDIR_DOWNLOAD,maxspeed[DDIR_DOWNLOAD]);
+                       ft->SetMaxSpeed(DDIR_UPLOAD,maxspeed[DDIR_UPLOAD]);
+
+                       ret = single_fd;
+               }
+               else if (scan_dirname != NULL)
+                       ret = OpenSwiftDirectory(scan_dirname,Address(),false,chunk_size);
+               else
+                       ret = -1;
+
+               // No file/dir nor HTTP gateway nor CMD gateway, will never know what to swarm
+               if (ret == -1) {
+                       fprintf(stderr,"Usage:\n");
+                       fprintf(stderr,"  -h, --hash\troot Merkle hash for the transmission\n");
+                       fprintf(stderr,"  -f, --file\tname of file to use (root hash by default)\n");
+                       fprintf(stderr,"  -l, --listen\t[ip:|host:]port to listen to (default: random)\n");
+                       fprintf(stderr,"  -t, --tracker\t[ip:|host:]port of the tracker (default: none)\n");
+                       fprintf(stderr,"  -D, --debug\tfile name for debugging logs (default: stdout)\n");
+                       fprintf(stderr,"  -B\tdebugging logs to stdout (win32 hack)\n");
+                       fprintf(stderr,"  -p, --progress\treport transfer progress\n");
+                       fprintf(stderr,"  -g, --httpgw\t[ip:|host:]port to bind HTTP content gateway to (no default)\n");
+                       fprintf(stderr,"  -s, --statsgw\t[ip:|host:]port to bind HTTP stats listen socket to (no default)\n");
+                       fprintf(stderr,"  -c, --cmdgw\t[ip:|host:]port to bind CMD listen socket to (no default)\n");
+                       fprintf(stderr,"  -o, --destdir\tdirectory for saving data (default: none)\n");
+                       fprintf(stderr,"  -u, --uprate\tupload rate limit in KiB/s (default: unlimited)\n");
+                       fprintf(stderr,"  -y, --downrate\tdownload rate limit in KiB/s (default: unlimited)\n");
+                       fprintf(stderr,"  -w, --wait\tlimit running time, e.g. 1[DHMs] (default: infinite with -l, -g)\n");
+                       fprintf(stderr,"  -H, --checkpoint\tcreate checkpoint of file when complete for fast restart\n");
+                       fprintf(stderr,"  -z, --chunksize\tchunk size in bytes (default: %d)\n", SWIFT_DEFAULT_CHUNK_SIZE);
+                       fprintf(stderr,"  -m, --printurl\tcompose URL from tracker, file and chunksize\n");
+                       return 1;
+               }
+    }
+
+    // Arno, 2012-01-04: Allow download and quit mode
+    if (single_fd != -1 && root_hash != Sha1Hash::ZERO && wait_time == 0) {
+       wait_time = TINT_NEVER;
+       exitoncomplete = true;
+    }
+
+    // End after wait_time
+    if ((long)wait_time > 0) {
+       evtimer_assign(&evend, Channel::evbase, EndCallback, NULL);
+       evtimer_add(&evend, tint2tv(wait_time));
+    }
+
+    // Enter mainloop, if daemonizing
+    if (wait_time == TINT_NEVER || (long)wait_time > 0) {
+               // Arno: always, for statsgw, rate control, etc.
+               evtimer_assign(&evreport, Channel::evbase, ReportCallback, NULL);
+               evtimer_add(&evreport, tint2tv(TINT_SEC));
+
+
+               // Arno:
+               if (scan_dirname != NULL) {
+                       evtimer_assign(&evrescan, Channel::evbase, RescanDirCallback, NULL);
+                       evtimer_add(&evrescan, tint2tv(RESCAN_DIR_INTERVAL*TINT_SEC));
+               }
+
+
+               fprintf(stderr,"swift: Mainloop\n");
+               // Enter libevent mainloop
+               event_base_dispatch(Channel::evbase);
+
+               // event_base_loopexit() was called, shutting down
+    }
+
+    // Arno, 2012-01-03: Close all transfers
+       for (int i=0; i<FileTransfer::files.size(); i++) {
+               if (FileTransfer::files[i] != NULL)
+            Close(FileTransfer::files[i]->fd());
+    }
+
+    if (Channel::debug_file)
+        fclose(Channel::debug_file);
+
+    swift::Shutdown();
+
+    return 0;
+}
+
+
+
+int OpenSwiftFile(const TCHAR* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, uint32_t chunk_size)
+{
+       std::string bfn;
+       bfn.assign(filename);
+       bfn.append(".mbinmap");
+       const char *binmap_filename = bfn.c_str();
+
+       // Arno, 2012-01-03: Hack to discover root hash of a file on disk, such that
+       // we don't load it twice while rescanning a dir of content.
+       HashTree *ht = new HashTree(true,binmap_filename);
+
+       //      fprintf(stderr,"swift: parsedir: File %s may have hash %s\n", filename, ht->root_hash().hex().c_str() );
+
+       int fd = swift::Find(ht->root_hash());
+       if (fd == -1) {
+               if (!quiet)
+                       fprintf(stderr,"swift: parsedir: Opening %s\n", filename);
+
+               fd = swift::Open(filename,hash,tracker,force_check_diskvshash,true,chunk_size);
+       }
+       else if (!quiet)
+               fprintf(stderr,"swift: parsedir: Ignoring loaded %s\n", filename);
+       return fd;
+}
+
+
+int OpenSwiftDirectory(const TCHAR* dirname, Address tracker, bool force_check_diskvshash, uint32_t chunk_size)
+{
+#ifdef _WIN32
+       HANDLE hFind;
+       WIN32_FIND_DATA FindFileData;
+
+       TCHAR pathsearch[MAX_PATH];
+       strcpy(pathsearch,dirname);
+       strcat(pathsearch,"\\*.*");
+       hFind = FindFirstFile(pathsearch, &FindFileData);
+       if(hFind != INVALID_HANDLE_VALUE) {
+           do {
+               //fprintf(stderr,"swift: parsedir: %s\n", FindFileData.cFileName);
+               if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && !strstr(FindFileData.cFileName,".mhash") && !strstr(FindFileData.cFileName,".mbinmap") ) {
+                               TCHAR path[MAX_PATH];
+                               strcpy(path,dirname);
+                               strcat(path,"\\");
+                               strcat(path,FindFileData.cFileName);
+
+                               int fd = OpenSwiftFile(path,Sha1Hash::ZERO,tracker,force_check_diskvshash,chunk_size);
+                               if (fd >= 0)
+                                       Checkpoint(fd);
+               }
+           } while(FindNextFile(hFind, &FindFileData));
+
+           FindClose(hFind);
+           return 1;
+       }
+       else
+               return -1;
+#else
+       DIR *dirp = opendir(dirname);
+       if (dirp == NULL)
+               return -1;
+
+       while(1)
+       {
+               struct dirent *de = readdir(dirp);
+               if (de == NULL)
+                       break;
+               if ((de->d_type & DT_DIR) !=0 || strstr(de->d_name,".mhash") || strstr(de->d_name,".mbinmap"))
+                       continue;
+               char path[PATH_MAX];
+               strcpy(path,dirname);
+               strcat(path,"/");
+               strcat(path,de->d_name);
+
+               int fd = OpenSwiftFile(path,Sha1Hash::ZERO,tracker,force_check_diskvshash,chunk_size);
+               if (fd >= 0)
+                       Checkpoint(fd);
+       }
+       closedir(dirp);
+       return 1;
+#endif
+}
+
+
+
+int CleanSwiftDirectory(const TCHAR* dirname)
+{
+       std::set<int>   delset;
+       std::vector<FileTransfer*>::iterator iter;
+       for (iter=FileTransfer::files.begin(); iter!=FileTransfer::files.end(); iter++)
+       {
+               FileTransfer *ft = *iter;
+               if (ft != NULL) {
+                       std::string filename = ft->file().filename();
+#ifdef WIN32
+                       struct _stat buf;
+#else
+                       struct stat buf;
+#endif
+                       fprintf(stderr,"swift: clean: Checking %s\n", filename.c_str() );
+                       int res = stat( filename.c_str(), &buf );
+                       if( res < 0 && errno == ENOENT) {
+                               fprintf(stderr,"swift: clean: Missing %s\n", filename.c_str() );
+                               delset.insert(ft->fd());
+                       }
+               }
+       }
+
+       std::set<int>::iterator iiter;
+       for (iiter=delset.begin(); iiter!=delset.end(); iiter++)
+       {
+               int fd = *iiter;
+               fprintf(stderr,"swift: clean: Deleting transfer %d\n", fd );
+               swift::Close(fd);
+       }
+
+       return 1;
+}
+
+
+
+
+
+void ReportCallback(int fd, short event, void *arg) {
+       // Called every second to print/calc some stats
+
+       if (single_fd  >= 0)
+       {
+               if (report_progress) {
+                       fprintf(stderr,
+                               "%s %lli of %lli (seq %lli) %lli dgram %lli bytes up, " \
+                               "%lli dgram %lli bytes down\n",
+                               IsComplete(single_fd ) ? "DONE" : "done",
+                               Complete(single_fd), Size(single_fd), SeqComplete(single_fd),
+                               Channel::global_dgrams_up, Channel::global_raw_bytes_up,
+                               Channel::global_dgrams_down, Channel::global_raw_bytes_down );
+               }
+
+        FileTransfer *ft = FileTransfer::file(single_fd);
+        if (report_progress) { // TODO: move up
+               fprintf(stderr,"upload %lf\n",ft->GetCurrentSpeed(DDIR_UPLOAD));
+               fprintf(stderr,"dwload %lf\n",ft->GetCurrentSpeed(DDIR_DOWNLOAD) );
+               //fprintf(stderr,"npeers %d\n",ft->GetNumLeechers()+ft->GetNumSeeders() );
+        }
+        // Update speed measurements such that they decrease when DL/UL stops
+        // Always
+       ft->OnRecvData(0);
+       ft->OnSendData(0);
+
+       // CHECKPOINT
+       if (file_enable_checkpoint && !file_checkpointed && IsComplete(single_fd))
+       {
+               std::string binmap_filename = ft->file().filename();
+               binmap_filename.append(".mbinmap");
+               fprintf(stderr,"swift: Complete, checkpointing %s\n", binmap_filename.c_str() );
+               FILE *fp = fopen(binmap_filename.c_str(),"wb");
+               if (!fp) {
+                       print_error("cannot open mbinmap for writing");
+                       return;
+               }
+               if (ft->file().serialize(fp) < 0)
+                       print_error("writing to mbinmap");
+               else
+                       file_checkpointed = true;
+               fclose(fp);
+       }
+
+
+       if (exitoncomplete && IsComplete(single_fd))
+               // Download and stop mode
+           event_base_loopexit(Channel::evbase, NULL);
+
+       }
+    if (httpgw_enabled)
+    {
+        fprintf(stderr,".");
+
+        // ARNOSMPTODO: Restore fail behaviour when used in SwarmPlayer 3000.
+        if (!HTTPIsSending()) {
+               // TODO
+               //event_base_loopexit(Channel::evbase, NULL);
+            return;
+        }
+    }
+    if (StatsQuit())
+    {
+       // SwarmPlayer 3000: User click "Quit" button in webUI.
+       struct timeval tv;
+       tv.tv_sec = 1;
+       int ret = event_base_loopexit(Channel::evbase,&tv);
+    }
+       // SWIFTPROC
+       // ARNOSMPTODO: SCALE: perhaps less than once a second if many swarms
+       CmdGwUpdateDLStatesCallback();
+
+       // Gertjan fix
+       // Arno, 2011-10-04: Temp disable
+    //if (do_nat_test)
+    //     nat_test_update();
+
+       evtimer_add(&evreport, tint2tv(TINT_SEC));
+}
+
+void EndCallback(int fd, short event, void *arg) {
+       // Called when wait timer expires == fixed time daemon
+    event_base_loopexit(Channel::evbase, NULL);
+}
+
+
+void RescanDirCallback(int fd, short event, void *arg) {
+
+       // SEEDDIR
+       // Rescan dir: CAREFUL: this is blocking, better prepare .m* files first
+       // by running swift separately and then copy content + *.m* to scanned dir,
+       // such that a fast restore from checkpoint is done.
+       //
+       OpenSwiftDirectory(scan_dirname,tracker,false,chunk_size);
+
+       CleanSwiftDirectory(scan_dirname);
+
+       evtimer_add(&evrescan, tint2tv(RESCAN_DIR_INTERVAL*TINT_SEC));
+}
+
+
+
+#ifdef _WIN32
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+    return main(__argc,__argv);
+}
+#endif
+
diff --git a/src/libswift_udp/swift.h b/src/libswift_udp/swift.h
new file mode 100644 (file)
index 0000000..00275a1
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ *  swift.h
+ *  the main header file for libswift, normally you should only read this one
+ *
+ *  Created by Victor Grishchenko on 3/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+/*
+
+  The swift protocol
+
+  Messages
+
+  HANDSHAKE    00, channelid
+  Communicates the channel id of the sender. The
+  initial handshake packet also has the root hash
+  (a HASH message).
+
+  DATA        01, bin_32, buffer
+  1K of data.
+
+  ACK        02, bin_32, timestamp_32
+  HAVE       03, bin_32
+  Confirms successfull delivery of data. Used for
+  congestion control, as well.
+
+  HINT        08, bin_32
+  Practical value of "hints" is to avoid overlap, mostly.
+  Hints might be lost in the network or ignored.
+  Peer might send out data without a hint.
+  Hint which was not responded (by DATA) in some RTTs
+  is considered to be ignored.
+  As peers cant pick randomly kilobyte here and there,
+  they send out "long hints" for non-base bins.
+
+  HASH        04, bin_32, sha1hash
+  SHA1 hash tree hashes for data verification. The
+  connection to a fresh peer starts with bootstrapping
+  him with peak hashes. Later, before sending out
+  any data, a peer sends the necessary uncle hashes.
+
+  PEX+/PEX-    05/06, ipv4 addr, port
+  Peer exchange messages; reports all connected and
+  disconected peers. Might has special meaning (as
+  in the case with swarm supervisors).
+
+*/
+#ifndef SWIFT_H
+#define SWIFT_H
+
+#include <deque>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <string>
+#include <math.h>
+
+#include "compat.h"
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/buffer.h>
+#include "bin.h"
+#include "binmap.h"
+#include "hashtree.h"
+#include "avgspeed.h"
+#include "availability.h"
+
+
+namespace swift {
+
+#define SWIFT_MAX_UDP_OVER_ETH_PAYLOAD         (1500-20-8)
+// Arno: Maximum size of non-DATA messages in a UDP packet we send.
+#define SWIFT_MAX_NONDATA_DGRAM_SIZE           (SWIFT_MAX_UDP_OVER_ETH_PAYLOAD-SWIFT_DEFAULT_CHUNK_SIZE-1-4)
+// Arno: Maximum size of a UDP packet we send. Note: depends on CHUNKSIZE 8192
+#define SWIFT_MAX_SEND_DGRAM_SIZE                      (SWIFT_MAX_NONDATA_DGRAM_SIZE+1+4+8192)
+// Arno: Maximum size of a UDP packet we are willing to accept. Note: depends on CHUNKSIZE 8192
+#define SWIFT_MAX_RECV_DGRAM_SIZE                      (SWIFT_MAX_SEND_DGRAM_SIZE*2)
+
+#define layer2bytes(ln,cs)     (uint64_t)( ((double)cs)*pow(2.0,(double)ln))
+#define bytes2layer(bn,cs)  (int)log2(  ((double)bn)/((double)cs) )
+
+// Arno, 2011-12-22: Enable Riccardo's VodPiecePicker
+#define ENABLE_VOD_PIECEPICKER         1
+
+
+/** IPv4 address, just a nice wrapping around struct sockaddr_in. */
+    struct Address {
+       struct sockaddr_in  addr;
+       static uint32_t LOCALHOST;
+       void set_port (uint16_t port) {
+           addr.sin_port = htons(port);
+       }
+       void set_port (const char* port_str) {
+           int p;
+           if (sscanf(port_str,"%i",&p))
+               set_port(p);
+       }
+       void set_ipv4 (uint32_t ipv4) {
+           addr.sin_addr.s_addr = htonl(ipv4);
+       }
+       void set_ipv4 (const char* ipv4_str) ;
+       //{    inet_aton(ipv4_str,&(addr.sin_addr));    }
+       void clear () {
+           memset(&addr,0,sizeof(struct sockaddr_in));
+           addr.sin_family = AF_INET;
+       }
+       Address() {
+           clear();
+       }
+       Address(const char* ip, uint16_t port)  {
+           clear();
+           set_ipv4(ip);
+           set_port(port);
+       }
+       Address(const char* ip_port);
+       Address(uint16_t port) {
+           clear();
+           set_ipv4((uint32_t)INADDR_ANY);
+           set_port(port);
+       }
+       Address(uint32_t ipv4addr, uint16_t port) {
+           clear();
+           set_ipv4(ipv4addr);
+           set_port(port);
+       }
+       Address(const struct sockaddr_in& address) : addr(address) {}
+       uint32_t ipv4 () const { return ntohl(addr.sin_addr.s_addr); }
+       uint16_t port () const { return ntohs(addr.sin_port); }
+       operator sockaddr_in () const {return addr;}
+       bool operator == (const Address& b) const {
+           return addr.sin_family==b.addr.sin_family &&
+               addr.sin_port==b.addr.sin_port &&
+               addr.sin_addr.s_addr==b.addr.sin_addr.s_addr;
+       }
+       const char* str () const {
+               // Arno, 2011-10-04: not thread safe, replace.
+           static char rs[4][32];
+           static int i;
+           i = (i+1) & 3;
+           sprintf(rs[i],"%i.%i.%i.%i:%i",ipv4()>>24,(ipv4()>>16)&0xff,
+                   (ipv4()>>8)&0xff,ipv4()&0xff,port());
+           return rs[i];
+       }
+       const char* ipv4str () const {
+               // Arno, 2011-10-04: not thread safe, replace.
+           static char rs[4][32];
+           static int i;
+           i = (i+1) & 3;
+           sprintf(rs[i],"%i.%i.%i.%i",ipv4()>>24,(ipv4()>>16)&0xff,
+                   (ipv4()>>8)&0xff,ipv4()&0xff);
+           return rs[i];
+       }
+       bool operator != (const Address& b) const { return !(*this==b); }
+       bool is_private() const {
+               // TODO IPv6
+               uint32_t no = ipv4(); uint8_t no0 = no>>24,no1 = (no>>16)&0xff;
+               if (no0 == 10) return true;
+               else if (no0 == 172 && no1 >= 16 && no1 <= 31) return true;
+               else if (no0 == 192 && no1 == 168) return true;
+               else return false;
+       }
+    };
+
+// Arno, 2011-10-03: Use libevent callback functions, no on_error?
+#define sockcb_t               event_callback_fn
+    struct sckrwecb_t {
+       sckrwecb_t (evutil_socket_t s=0, sockcb_t mr=NULL, sockcb_t mw=NULL,
+                   sockcb_t oe=NULL) :
+           sock(s), may_read(mr), may_write(mw), on_error(oe) {}
+       evutil_socket_t sock;
+       sockcb_t   may_read;
+       sockcb_t   may_write;
+       sockcb_t   on_error;
+    };
+
+    struct now_t  {
+       static tint now;
+    };
+
+#define NOW now_t::now
+
+    /** tintbin is basically a pair<tint,bin64_t> plus some nice operators.
+        Most frequently used in different queues (acknowledgements, requests,
+        etc). */
+    struct tintbin {
+        tint    time;
+        bin_t bin;
+        tintbin(const tintbin& b) : time(b.time), bin(b.bin) {}
+        tintbin() : time(TINT_NEVER), bin(bin_t::NONE) {}
+        tintbin(tint time_, bin_t bin_) : time(time_), bin(bin_) {}
+        tintbin(bin_t bin_) : time(NOW), bin(bin_) {}
+        bool operator < (const tintbin& b) const
+                       { return time > b.time; }
+        bool operator == (const tintbin& b) const
+                       { return time==b.time && bin==b.bin; }
+        bool operator != (const tintbin& b) const
+                       { return !(*this==b); }
+    };
+
+    typedef std::deque<tintbin> tbqueue;
+    typedef std::deque<bin_t> binqueue;
+    typedef Address   Address;
+
+    /** A heap (priority queue) for timestamped bin numbers (tintbins). */
+    class tbheap {
+        tbqueue data_;
+    public:
+        int size () const { return data_.size(); }
+        bool is_empty () const { return data_.empty(); }
+        tintbin         pop() {
+            tintbin ret = data_.front();
+            std::pop_heap(data_.begin(),data_.end());
+            data_.pop_back();
+            return ret;
+        }
+        void            push(const tintbin& tb) {
+            data_.push_back(tb);
+            push_heap(data_.begin(),data_.end());
+        }
+        const tintbin&  peek() const {
+            return data_.front();
+        }
+    };
+
+    /** swift protocol message types; these are used on the wire. */
+    typedef enum {
+        SWIFT_HANDSHAKE = 0,
+        SWIFT_DATA = 1,
+        SWIFT_ACK = 2,
+        SWIFT_HAVE = 3,
+        SWIFT_HASH = 4,
+        SWIFT_PEX_ADD = 5,
+        SWIFT_PEX_REQ = 6,
+        SWIFT_SIGNED_HASH = 7,
+        SWIFT_HINT = 8,
+        SWIFT_MSGTYPE_RCVD = 9,
+        SWIFT_RANDOMIZE = 10, //FRAGRAND
+        SWIFT_VERSION = 11, // Arno, 2011-10-19: TODO to match RFC-rev-03
+        SWIFT_MESSAGE_COUNT = 12
+    } messageid_t;
+
+    typedef enum {
+        DDIR_UPLOAD,
+        DDIR_DOWNLOAD
+    } data_direction_t;
+
+    class PiecePicker;
+    //class CongestionController; // Arno: Currently part of Channel. See ::NextSendTime
+    class PeerSelector;
+    class Channel;
+    typedef void (*ProgressCallback) (int transfer, bin_t bin);
+
+    /** A class representing single file transfer. */
+    class    FileTransfer {
+
+    public:
+
+        /** A constructor. Open/submit/retrieve a file.
+         *  @param file_name    the name of the file
+         *  @param root_hash    the root hash of the file; zero hash if the file
+                *                          is newly submitted
+                */
+        FileTransfer(const char *file_name, const Sha1Hash& root_hash=Sha1Hash::ZERO,bool force_check_diskvshash=true,bool check_netwvshash=true,uint32_t chunk_size=SWIFT_DEFAULT_CHUNK_SIZE);
+
+        /**    Close everything. */
+        ~FileTransfer();
+
+
+        /** While we need to feed ACKs to every peer, we try (1) avoid
+            unnecessary duplication and (2) keep minimum state. Thus,
+            we use a rotating queue of bin completion events. */
+        //bin64_t         RevealAck (uint64_t& offset);
+        /** Rotating queue read for channels of this transmission. */
+        // Jori
+        int             RevealChannel (int& i);
+        // Gertjan
+        int             RandomChannel (int own_id);
+
+
+        /** Find transfer by the root hash. */
+        static FileTransfer* Find (const Sha1Hash& hash);
+        /** Find transfer by the file descriptor. */
+        static FileTransfer* file (int fd) {
+            return fd<files.size() ? files[fd] : NULL;
+        }
+
+        /** The binmap for data already retrieved and checked. */
+        binmap_t&           ack_out ()  { return file_.ack_out(); }
+        /** Piece picking strategy used by this transfer. */
+        PiecePicker&    picker () { return *picker_; }
+        /** The number of channels working for this transfer. */
+        int             channel_count () const { return hs_in_.size(); }
+        /** Hash tree checked file; all the hashes and data are kept here. */
+        HashTree&       file() { return file_; }
+        /** File descriptor for the data file. */
+        int             fd () const { return file_.file_descriptor(); }
+        /** Root SHA1 hash of the transfer (and the data file). */
+        const Sha1Hash& root_hash () const { return file_.root_hash(); }
+        /** Ric: the availability in the swarm */
+        Availability&  availability() { return *availability_; }
+
+               // RATELIMIT
+        /** Arno: Call when n bytes are received. */
+        void                   OnRecvData(int n);
+        /** Arno: Call when n bytes are sent. */
+        void                   OnSendData(int n);
+        /** Arno: Call when no bytes are sent due to rate limiting. */
+        void                   OnSendNoData();
+        /** Arno: Return current speed for the given direction in bytes/s */
+               double                  GetCurrentSpeed(data_direction_t ddir);
+               /** Arno: Return maximum speed for the given direction in bytes/s */
+               double                  GetMaxSpeed(data_direction_t ddir);
+               /** Arno: Set maximum speed for the given direction in bytes/s */
+               void                    SetMaxSpeed(data_direction_t ddir, double m);
+               /** Arno: Return the number of non-seeders current channeled with. */
+               uint32_t                GetNumLeechers();
+               /** Arno: Return the number of seeders current channeled with. */
+               uint32_t                GetNumSeeders();
+               /** Arno: Return the set of Channels for this transfer. MORESTATS */
+               std::set<Channel *> GetChannels() { return mychannels_; }
+
+               /** Arno: set the tracker for this transfer. Reseting it won't kill
+                * any existing connections.
+                */
+               void SetTracker(Address tracker) { tracker_ = tracker; }
+
+               /** Arno: (Re)Connect to tracker for this transfer, or global Channel::tracker if not set */
+               void ConnectToTracker();
+
+               /** Arno: Reconnect to the tracker if no established peers and
+                * exp backoff allows it.
+                */
+               void ReConnectToTrackerIfAllowed(bool hasestablishedpeers);
+
+               /** Arno: Return the Channel to peer "addr" that is not equal to "notc". */
+               Channel * FindChannel(const Address &addr, Channel *notc);
+
+               // SAFECLOSE
+               static void LibeventCleanCallback(int fd, short event, void *arg);
+    protected:
+
+        HashTree        file_;
+
+        /** Piece picker strategy. */
+        PiecePicker*    picker_;
+
+        /** Channels working for this transfer. */
+        binqueue        hs_in_;                        // Arno, 2011-10-03: Should really be queue of channel ID (=uint32_t)
+
+        /** Messages we are accepting.    */
+        uint64_t        cap_out_;
+
+        tint            init_time_;
+        
+        // Ric: PPPLUG
+        /** Availability in the swarm */
+        Availability*  availability_;
+
+#define SWFT_MAX_TRANSFER_CB 8
+        ProgressCallback callbacks[SWFT_MAX_TRANSFER_CB];
+        uint8_t         cb_agg[SWFT_MAX_TRANSFER_CB];
+        int             cb_installed;
+
+               // RATELIMIT
+        std::set<Channel *>    mychannels_; // Arno, 2012-01-31: May be duplicate of hs_in_
+        MovingAverageSpeed     cur_speed_[2];
+        double                         max_speed_[2];
+        int                                    speedzerocount_;
+
+        // SAFECLOSE
+        struct event           evclean_;
+
+        Address                        tracker_; // Tracker for this transfer
+        tint                           tracker_retry_interval_;
+        tint                           tracker_retry_time_;
+
+    public:
+        void            OnDataIn (bin_t pos);
+        // Gertjan fix: return bool
+        bool            OnPexIn (const Address& addr);
+
+        static std::vector<FileTransfer*> files;
+
+
+        friend class Channel;
+        // Ric: maybe not really needed
+        friend class Availability;
+        friend uint64_t  Size (int fdes);
+        friend bool      IsComplete (int fdes);
+        friend uint64_t  Complete (int fdes);
+        friend uint64_t  SeqComplete (int fdes);
+        friend int     Open (const char* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size);
+        friend void    Close (int fd) ;
+        friend void AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg);
+        friend void RemoveProgressCallback (int transfer,ProgressCallback cb);
+        friend void ExternallyRetrieved (int transfer,bin_t piece);
+    };
+
+
+    /** PiecePicker implements some strategy of choosing (picking) what
+        to request next, given the possible range of choices:
+        data acknowledged by the peer minus data already retrieved.
+        May pick sequentially, do rarest first or in some other way. */
+    class PiecePicker {
+    public:
+        virtual void Randomize (uint64_t twist) = 0;
+        /** The piece picking method itself.
+         *  @param  offered     the data acknowledged by the peer
+         *  @param  max_width   maximum number of packets to ask for
+         *  @param  expires     (not used currently) when to consider request expired
+         *  @return             the bin number to request */
+        virtual bin_t Pick (binmap_t& offered, uint64_t max_width, tint expires) = 0;
+        virtual void LimitRange (bin_t range) = 0;
+        /** updates the playback position for streaming piece picking.
+         *  @param  amount             amount to increment in bin unit size (1KB default) */
+        virtual void updatePlaybackPos (int amount=1) = 0;
+        virtual ~PiecePicker() {}
+    };
+
+
+    class PeerSelector { // Arno: partically unused
+    public:
+        virtual void AddPeer (const Address& addr, const Sha1Hash& root) = 0;
+        virtual Address GetPeer (const Sha1Hash& for_root) = 0;
+    };
+
+
+    /* class DataStorer { // Arno: never implemented
+    public:
+        DataStorer (const Sha1Hash& id, size_t size);
+        virtual size_t    ReadData (bin_t pos,uint8_t** buf) = 0;
+        virtual size_t    WriteData (bin_t pos, uint8_t* buf, size_t len) = 0;
+    }; */
+
+
+    /**    swift channel's "control block"; channels loosely correspond to TCP
+          connections or FTP sessions; one channel is created for one file
+          being transferred between two peers. As we don't need buffers and
+          lots of other TCP stuff, sizeof(Channel+members) must be below 1K.
+          Normally, API users do not deal with this class. */
+    class Channel {
+
+#define DGRAM_MAX_SOCK_OPEN 128
+       static int sock_count;
+       static sckrwecb_t sock_open[DGRAM_MAX_SOCK_OPEN];
+
+    public:
+        Channel    (FileTransfer* file, int socket=INVALID_SOCKET, Address peer=Address());
+        ~Channel();
+
+        typedef enum {
+            KEEP_ALIVE_CONTROL,
+            PING_PONG_CONTROL,
+            SLOW_START_CONTROL,
+            AIMD_CONTROL,
+            LEDBAT_CONTROL,
+            CLOSE_CONTROL
+        } send_control_t;
+
+        static Address  tracker; // Global tracker for all transfers
+        struct event *evsend_ptr_; // Arno: timer per channel // SAFECLOSE
+        static struct event_base *evbase;
+        static struct event evrecv;
+        static const char* SEND_CONTROL_MODES[];
+
+           static tint epoch, start;
+           static uint64_t global_dgrams_up, global_dgrams_down, global_raw_bytes_up, global_raw_bytes_down, global_bytes_up, global_bytes_down;
+        static void CloseChannelByAddress(const Address &addr);
+
+        // SOCKMGMT
+        // Arno: channel is also a "singleton" class that manages all sockets
+        // for a swift process
+        static void LibeventSendCallback(int fd, short event, void *arg);
+        static void LibeventReceiveCallback(int fd, short event, void *arg);
+        static void RecvDatagram (evutil_socket_t socket); // Called by LibeventReceiveCallback
+           static int RecvFrom(evutil_socket_t sock, Address& addr, struct evbuffer *evb); // Called by RecvDatagram
+           static int SendTo(evutil_socket_t sock, const Address& addr, struct evbuffer *evb); // Called by Channel::Send()
+           static evutil_socket_t Bind(Address address, sckrwecb_t callbacks=sckrwecb_t());
+           static Address BoundAddress(evutil_socket_t sock);
+           static evutil_socket_t default_socket()
+            { return sock_count ? sock_open[0].sock : INVALID_SOCKET; }
+
+           /** close the port */
+           static void CloseSocket(evutil_socket_t sock);
+           static void Shutdown ();
+           /** the current time */
+           static tint Time();
+
+           // Arno: Per instance methods
+        void        Recv (struct evbuffer *evb);
+        void        Send ();  // Called by LibeventSendCallback
+        void        Close ();
+
+        void        OnAck (struct evbuffer *evb);
+        void        OnHave (struct evbuffer *evb);
+        bin_t       OnData (struct evbuffer *evb);
+        void        OnHint (struct evbuffer *evb);
+        void        OnHash (struct evbuffer *evb);
+        void        OnPex (struct evbuffer *evb);
+        void        OnHandshake (struct evbuffer *evb);
+        void        OnRandomize (struct evbuffer *evb); //FRAGRAND
+        void        AddHandshake (struct evbuffer *evb);
+        bin_t       AddData (struct evbuffer *evb);
+        void        AddAck (struct evbuffer *evb);
+        void        AddHave (struct evbuffer *evb);
+        void        AddHint (struct evbuffer *evb);
+        void        AddUncleHashes (struct evbuffer *evb, bin_t pos);
+        void        AddPeakHashes (struct evbuffer *evb);
+        void        AddPex (struct evbuffer *evb);
+        void        OnPexReq(void);
+        void        AddPexReq(struct evbuffer *evb);
+        void        BackOffOnLosses (float ratio=0.5);
+        tint        SwitchSendControl (int control_mode);
+        tint        NextSendTime ();
+        tint        KeepAliveNextSendTime ();
+        tint        PingPongNextSendTime ();
+        tint        CwndRateNextSendTime ();
+        tint        SlowStartNextSendTime ();
+        tint        AimdNextSendTime ();
+        tint        LedbatNextSendTime ();
+        /** Arno: return true if this peer has complete file. May be fuzzy if Peak Hashes not in */
+        bool           IsComplete();
+        /** Arno: return (UDP) port for this channel */
+        uint16_t       GetMyPort();
+        bool           IsDiffSenderOrDuplicate(Address addr, uint32_t chid);
+
+        static int  MAX_REORDERING;
+        static tint TIMEOUT;
+        static tint MIN_DEV;
+        static tint MAX_SEND_INTERVAL;
+        static tint LEDBAT_TARGET;
+        static float LEDBAT_GAIN;
+        static tint LEDBAT_DELAY_BIN;
+        static bool SELF_CONN_OK;
+        static tint MAX_POSSIBLE_RTT;
+        static tint MIN_PEX_REQUEST_INTERVAL;
+        static FILE* debug_file;
+
+        const std::string id_string () const;
+        /** A channel is "established" if had already sent and received packets. */
+        bool        is_established () { return peer_channel_id_ && own_id_mentioned_; }
+        FileTransfer& transfer() { return *transfer_; }
+        HashTree&   file () { return transfer_->file(); }
+        const Address& peer() const { return peer_; }
+        const Address& recv_peer() const { return recv_peer_; }
+        tint ack_timeout () {
+               tint dev = dev_avg_ < MIN_DEV ? MIN_DEV : dev_avg_;
+               tint tmo = rtt_avg_ + dev * 4;
+               return tmo < 30*TINT_SEC ? tmo : 30*TINT_SEC;
+        }
+        uint32_t    id () const { return id_; }
+
+        // MORESTATS
+        uint64_t raw_bytes_up() { return raw_bytes_up_; }
+        uint64_t raw_bytes_down() { return raw_bytes_down_; }
+        uint64_t bytes_up() { return bytes_up_; }
+        uint64_t bytes_down() { return bytes_down_; }
+
+        static int  DecodeID(int scrambled);
+        static int  EncodeID(int unscrambled);
+        static Channel* channel(int i) {
+            return i<channels.size()?channels[i]:NULL;
+        }
+        static void CloseTransfer (FileTransfer* trans);
+
+        // SAFECLOSE
+        void           ClearEvents();
+        void           Schedule4Close() { scheduled4close_ = true; }
+        bool           IsScheduled4Close() { return scheduled4close_; }
+
+
+    protected:
+        /** Channel id: index in the channel array. */
+        uint32_t    id_;
+        /**    Socket address of the peer. */
+        Address     peer_;
+        /**    The UDP socket fd. */
+        evutil_socket_t      socket_;
+        /**    Descriptor of the file in question. */
+        FileTransfer*    transfer_;
+        /**    Peer channel id; zero if we are trying to open a channel. */
+        uint32_t    peer_channel_id_;
+        bool        own_id_mentioned_;
+        /**    Peer's progress, based on acknowledgements. */
+        binmap_t    ack_in_;
+        /**    Last data received; needs to be acked immediately. */
+        tintbin     data_in_;
+        bin_t       data_in_dbl_;
+        /** The history of data sent and still unacknowledged. */
+        tbqueue     data_out_;
+        /** Timeouted data (potentially to be retransmitted). */
+        tbqueue     data_out_tmo_;
+        bin_t       data_out_cap_;
+        /** Index in the history array. */
+        binmap_t    have_out_;
+        /**    Transmit schedule: in most cases filled with the peer's hints */
+        tbqueue     hint_in_;
+        /** Hints sent (to detect and reschedule ignored hints). */
+        tbqueue     hint_out_;
+        uint64_t    hint_out_size_;
+        /** Types of messages the peer accepts. */
+        uint64_t    cap_in_;
+        /** For repeats. */
+        //tint        last_send_time, last_recv_time;
+        /** PEX progress */
+        bool        pex_requested_;
+        tint        last_pex_request_time_;
+        tint        next_pex_request_time_;
+        bool        pex_request_outstanding_;
+        tbqueue     reverse_pex_out_;          // Arno, 2011-10-03: should really be a queue of (tint,channel id(= uint32_t)) pairs.
+        int         useless_pex_count_;
+        /** Smoothed averages for RTT, RTT deviation and data interarrival periods. */
+        tint        rtt_avg_, dev_avg_, dip_avg_;
+        tint        last_send_time_;
+        tint        last_recv_time_;
+        tint        last_data_out_time_;
+        tint        last_data_in_time_;
+        tint        last_loss_time_;
+        tint        next_send_time_;
+        /** Congestion window; TODO: int, bytes. */
+        float       cwnd_;
+        int         cwnd_count1_;
+        /** Data sending interval. */
+        tint        send_interval_;
+        /** The congestion control strategy. */
+        int         send_control_;
+        /** Datagrams (not data) sent since last recv.    */
+        int         sent_since_recv_;
+
+        /** Arno: Fix for KEEP_ALIVE_CONTROL */
+        bool           lastrecvwaskeepalive_;
+        bool           lastsendwaskeepalive_;
+
+        /** Recent acknowlegements for data previously sent.    */
+        int         ack_rcvd_recent_;
+        /** Recent non-acknowlegements (losses) of data previously sent.    */
+        int         ack_not_rcvd_recent_;
+        /** LEDBAT one-way delay machinery */
+        tint        owd_min_bins_[4];
+        int         owd_min_bin_;
+        tint        owd_min_bin_start_;
+        tint        owd_current_[4];
+        int         owd_cur_bin_;
+        /** Stats */
+        int         dgrams_sent_;
+        int         dgrams_rcvd_;
+        // Arno, 2011-11-28: for detailed, per-peer stats. MORESTATS
+        uint64_t raw_bytes_up_, raw_bytes_down_, bytes_up_, bytes_down_;
+
+        // SAFECLOSE
+        bool           scheduled4close_;
+        /** Arno: Socket address of the peer where packets are received from,
+         * when an IANA private address, otherwise 0.
+         * May not be equal to peer_. 2PEERSBEHINDSAMENAT */
+        Address     recv_peer_;
+
+        int         PeerBPS() const {
+            return TINT_SEC / dip_avg_ * 1024;
+        }
+        /** Get a request for one packet from the queue of peer's requests. */
+        bin_t       DequeueHint(bool *retransmitptr);
+        bin_t       ImposeHint();
+        void        TimeoutDataOut ();
+        void        CleanStaleHintOut();
+        void        CleanHintOut(bin_t pos);
+        void        Reschedule();
+        void           UpdateDIP(bin_t pos); // RETRANSMIT
+
+
+        static PeerSelector* peer_selector;
+
+        static tint     last_tick;
+        //static tbheap   send_queue;
+
+        static std::vector<Channel*> channels;
+
+        friend int      Listen (Address addr);
+        friend void     Shutdown (int sock_des);
+        friend void     AddPeer (Address address, const Sha1Hash& root);
+        friend void     SetTracker(const Address& tracker);
+        friend int      Open (const char*, const Sha1Hash&, Address tracker, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size) ; // FIXME
+    };
+
+
+
+    /*************** The top-level API ****************/
+    /** Start listening a port. Returns socket descriptor. */
+    int     Listen (Address addr);
+    /** Stop listening to a port. */
+    void    Shutdown (int sock_des=-1);
+
+    /** Open a file, start a transmission; fill it with content for a given
+        root hash and tracker (optional). If "force_check_diskvshash" is true, the
+        hashtree state will be (re)constructed from the file on disk (if any).
+        If not, open will try to reconstruct the hashtree state from
+        the .mhash and .mbinmap files on disk. .mhash files are created
+        automatically, .mbinmap files must be written by checkpointing the
+        transfer by calling FileTransfer::serialize(). If the reconstruction
+        fails, it will hashcheck anyway. Roothash is optional for new files or
+        files already hashchecked and checkpointed. If "check_netwvshash" is
+        false, no uncle hashes will be sent and no data will be verified against
+        then on receipt. In this mode, checking disk contents against hashes
+        no longer works on restarts, unless checkpoints are used.
+        */
+    int     Open (const char* filename, const Sha1Hash& hash=Sha1Hash::ZERO,Address tracker=Address(), bool force_check_diskvshash=true, bool check_netwvshash=true, uint32_t chunk_size=SWIFT_DEFAULT_CHUNK_SIZE);
+    /** Get the root hash for the transmission. */
+    const Sha1Hash& RootMerkleHash (int file) ;
+    /** Close a file and a transmission. */
+    void    Close (int fd) ;
+    /** Add a possible peer which participares in a given transmission. In the case
+        root hash is zero, the peer might be talked to regarding any transmission
+        (likely, a tracker, cache or an archive). */
+    void    AddPeer (Address address, const Sha1Hash& root=Sha1Hash::ZERO);
+
+    void    SetTracker(const Address& tracker);
+    /** Set the default tracker that is used when Open is not passed a tracker
+        address. */
+
+    /** Returns size of the file in bytes, 0 if unknown. Might be rounded up to a kilobyte
+        before the transmission is complete. */
+    uint64_t  Size (int fdes);
+    /** Returns the amount of retrieved and verified data, in bytes.
+        A 100% complete transmission has Size()==Complete(). */
+    uint64_t  Complete (int fdes);
+    bool      IsComplete (int fdes);
+    /** Returns the number of bytes that are complete sequentially, starting from the
+        beginning, till the first not-yet-retrieved packet. */
+    uint64_t  SeqComplete (int fdes);
+    /***/
+    int       Find (Sha1Hash hash);
+    /** Returns the number of bytes in a chunk for this transmission */
+    uint32_t     ChunkSize(int fdes);
+
+    /** Get the address bound to the socket descriptor returned by Listen() */
+    Address BoundAddress(evutil_socket_t sock);
+
+    void AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg);
+    void RemoveProgressCallback (int transfer,ProgressCallback cb);
+    void ExternallyRetrieved (int transfer,bin_t piece);
+
+
+    /** Must be called by any client using the library */
+    void LibraryInit(void);
+
+    int evbuffer_add_string(struct evbuffer *evb, std::string str);
+    int evbuffer_add_8(struct evbuffer *evb, uint8_t b);
+    int evbuffer_add_16be(struct evbuffer *evb, uint16_t w);
+    int evbuffer_add_32be(struct evbuffer *evb, uint32_t i);
+    int evbuffer_add_64be(struct evbuffer *evb, uint64_t l);
+    int evbuffer_add_hash(struct evbuffer *evb, const Sha1Hash& hash);
+
+    uint8_t evbuffer_remove_8(struct evbuffer *evb);
+    uint16_t evbuffer_remove_16be(struct evbuffer *evb);
+    uint32_t evbuffer_remove_32be(struct evbuffer *evb);
+    uint64_t evbuffer_remove_64be(struct evbuffer *evb);
+    Sha1Hash evbuffer_remove_hash(struct evbuffer* evb);
+
+    const char* tintstr(tint t=0);
+    std::string sock2str (struct sockaddr_in addr);
+ #define SWIFT_MAX_CONNECTIONS 20
+
+    void nat_test_update(void);
+
+    // Arno: Save transfer's binmap for zero-hashcheck restart
+    void Checkpoint(int fdes);
+
+} // namespace end
+
+// #define SWIFT_MUTE
+
+#ifndef SWIFT_MUTE
+#define dprintf(...) do { if (Channel::debug_file) fprintf(Channel::debug_file,__VA_ARGS__); } while (0)
+#define dflush() fflush(Channel::debug_file)
+#else
+#define dprintf(...) do {} while(0)
+#define dflush() do {} while(0)
+#endif
+#define eprintf(...) fprintf(stderr,__VA_ARGS__)
+
+#endif
diff --git a/src/libswift_udp/tests/SConscript b/src/libswift_udp/tests/SConscript
new file mode 100644 (file)
index 0000000..070c362
--- /dev/null
@@ -0,0 +1,95 @@
+import sys
+import os
+
+Import("DEBUG")
+Import("env")
+Import("libs")
+Import("libpath")
+
+
+if DEBUG and sys.platform == "win32":
+       libs = ['libswift','gtestd'] + libs  # order is important, crypto needs to be last
+else:
+       libs = ['libswift','gtest'] + libs  # order is important, crypto needs to be last
+
+cpppath = env["CPPPATH"]
+if sys.platform == "win32":
+       cpppath += '..;'
+else:
+       cpppath += '..:'
+env.Append(CPPPATH=cpppath)
+
+env.Program( 
+    target='binstest2',
+    source=['binstest2.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='binstest3',
+    source=['binstest3.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='dgramtest',
+    source=['dgramtest.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='hashtest',
+    source=['hashtest.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+# Arno: must be rewritten to libevent
+#env.Program( 
+#    target='ledbattest',
+#    source=['ledbattest.cpp'],
+#    CPPPATH=cpppath,
+#    LIBS=libs,
+#    LIBPATH=libpath )
+
+# Arno: must be rewritten to libevent
+#if sys.platform != "win32":
+#    # Arno: Needs getopt
+#    env.Program( 
+#        target='ledbattest2',
+#        source=['ledbattest2.cpp'],
+#        CPPPATH=cpppath,
+#        LIBS=libs,
+#        LIBPATH=libpath )
+
+env.Program( 
+    target='freemap',
+    source=['freemap.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='bin64test',
+    source=['bin64test.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='transfertest',
+    source=['transfertest.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
+env.Program( 
+    target='connecttest',
+    source=['connecttest.cpp'],
+    CPPPATH=cpppath,
+    LIBS=libs,
+    LIBPATH=libpath )
+
diff --git a/src/libswift_udp/tests/bin64test.cpp b/src/libswift_udp/tests/bin64test.cpp
new file mode 100644 (file)
index 0000000..77340f0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  bintest.cpp
+ *  bin++
+ *
+ *  Created by Victor Grishchenko on 3/9/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include "bin.h"
+#include "bin_utils.h"
+#include <gtest/gtest.h>
+
+TEST(Bin64Test,InitGet) {
+
+    EXPECT_EQ(0x1,bin_t(1,0).toUInt());
+    EXPECT_EQ(0xB,bin_t(2,1).toUInt());
+    EXPECT_EQ(0x2,bin_t(2,1).layer());
+    EXPECT_EQ(34,bin_t(34,2345).layer());
+    EXPECT_EQ(0x7ffffffffULL,bin_t(34,2345).layer_bits());
+    EXPECT_EQ(1,bin_t(2,1).layer_offset());
+    EXPECT_EQ(2345,bin_t(34,2345).layer_offset());
+    EXPECT_EQ((1<<1) - 1,bin_t(0,123).layer_bits());
+    EXPECT_EQ((1<<17) - 1,bin_t(16,123).layer_bits());
+
+}
+
+TEST(Bin64Test,Navigation) {
+
+    bin_t mid(4,18);
+    EXPECT_EQ(bin_t(5,9),mid.parent());
+    EXPECT_EQ(bin_t(3,36),mid.left());
+    EXPECT_EQ(bin_t(3,37),mid.right());
+    EXPECT_EQ(bin_t(5,9),bin_t(4,19).parent());
+    bin_t up32(30,1);
+    EXPECT_EQ(bin_t(31,0),up32.parent());
+
+}
+
+TEST(Bin64Test,Overflows) {
+
+    EXPECT_FALSE(bin_t::NONE.contains(bin_t(0,1)));
+    EXPECT_TRUE(bin_t::ALL.contains(bin_t(0,1)));
+    EXPECT_EQ(0,bin_t::NONE.base_length());
+    EXPECT_EQ(bin_t::NONE,bin_t::NONE.twisted(123));
+    /*EXPECT_EQ(bin64_t::NONE.parent(),bin64_t::NONE);
+    EXPECT_EQ(bin64_t::NONE.left(),bin64_t::NONE);
+    EXPECT_EQ(bin64_t::NONE.right(),bin64_t::NONE);
+    EXPECT_EQ(bin64_t::NONE,bin64_t(0,2345).left());
+    EXPECT_EQ(bin64_t::NONE,bin64_t::ALL.parent());
+*/
+}
+
+TEST(Bin64Test, Advanced) {
+
+    EXPECT_EQ(4,bin_t(2,3).base_length());
+    EXPECT_FALSE(bin_t(1,1234).is_base());
+    EXPECT_TRUE(bin_t(0,12345).is_base());
+    EXPECT_EQ(bin_t(0,2),bin_t(1,1).base_left());
+    bin_t peaks[64];
+    int peak_count = gen_peaks(7,peaks);
+    EXPECT_EQ(3,peak_count);
+    EXPECT_EQ(bin_t(2,0),peaks[0]);
+    EXPECT_EQ(bin_t(1,2),peaks[1]);
+    EXPECT_EQ(bin_t(0,6),peaks[2]);
+
+}
+
+TEST(Bin64Test, Bits) {
+    bin_t all = bin_t::ALL, none = bin_t::NONE, big = bin_t(40,18);
+    uint32_t a32 = bin_toUInt32(all), n32 = bin_toUInt32(none), b32 = bin_toUInt32(big);
+    EXPECT_EQ(0x7fffffff,a32);
+    EXPECT_EQ(0xffffffff,n32);
+    EXPECT_EQ(bin_t::NONE,bin_fromUInt32(b32));
+}
+
+int main (int argc, char** argv) {
+
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+
+}
diff --git a/src/libswift_udp/tests/binstest2.cpp b/src/libswift_udp/tests/binstest2.cpp
new file mode 100755 (executable)
index 0000000..d537591
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ *  binstest2.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include "binmap.h"
+#include "binheap.h"
+
+#include <time.h>
+#include <set>
+#include <gtest/gtest.h>
+
+
+using namespace swift;
+
+/*
+TEST(BinsTest,Routines) {
+
+    uint32_t cell = (3<<10) | (3<<14) | (3<<0);
+    uint16_t correct = (1<<5) | (1<<7) | (1<<0);
+    uint16_t joined  = binmap_t::join32to16(cell);
+    EXPECT_EQ(correct,joined);
+    
+    uint32_t split = binmap_t::split16to32(correct);
+    EXPECT_EQ(cell,split);
+    
+    EXPECT_EQ(binmap_t::NOJOIN,binmap_t::join32to16(cell|4));
+
+}
+*/
+
+
+TEST(BinsTest,SetGet) {
+
+    binmap_t bs;
+    bin_t b3(1,0), b2(0,1), b4(0,2), b6(1,1), b7(2,0);
+    bs.set(b3);
+    //bs.dump("set done");
+    EXPECT_TRUE(bs.is_filled(b3));
+    //bs.dump("set checkd");
+    EXPECT_TRUE(bs.is_filled(b2));
+    //bs.dump("get b2 done");
+    EXPECT_TRUE(bs.is_filled(b3));
+    //bs.dump("get b3 done");
+    EXPECT_TRUE(bs.is_empty(b4));
+    EXPECT_TRUE(bs.is_empty(b6));
+    EXPECT_FALSE(bs.is_filled(b7));
+    EXPECT_FALSE(bs.is_empty(b7));
+    EXPECT_TRUE(bs.is_filled(b3));
+    bs.set(bin_t(1,1));
+    EXPECT_TRUE(bs.is_filled(bin_t(2,0)));
+
+}
+
+/*
+TEST(BinsTest,Iterator) {
+    binmap_t b;
+    b.set(bin_t(3,1));
+    iterator i(&b,bin_t(0,0),false);
+    while (!i.solid())
+        i.left();
+    EXPECT_EQ(bin_t(3,0),i.bin());
+    EXPECT_EQ(false,i.deep());
+    EXPECT_EQ(true,i.solid());
+    EXPECT_EQ(binmap_t::EMPTY,*i);
+    i.next();
+    EXPECT_EQ(bin_t(3,1),i.bin());
+    EXPECT_EQ(false,i.deep());
+    EXPECT_EQ(true,i.solid());
+    EXPECT_EQ(binmap_t::FILLED,*i);
+    i.next();
+    EXPECT_TRUE(i.end());
+}
+*/
+
+TEST(BinsTest,Chess) {
+    binmap_t chess16;
+    for(int i=0; i<16; i++) {
+        if (i&1) {
+            chess16.set(bin_t(0,i));
+        } else {
+            chess16.reset(bin_t(0,i));
+        }
+    }
+
+    for(int i=0; i<16; i++) {
+        if (i&1) {
+            EXPECT_TRUE(chess16.is_filled(bin_t(0,i)));
+        } else {
+            EXPECT_TRUE(chess16.is_empty(bin_t(0,i)));
+        }
+    }
+    EXPECT_FALSE(chess16.is_empty(bin_t(4,0)));
+    for(int i=0; i<16; i+=2)
+        chess16.set(bin_t(0,i));
+    EXPECT_TRUE(chess16.is_filled(bin_t(4,0)));
+    EXPECT_TRUE(chess16.is_filled(bin_t(2,3)));
+
+    chess16.set(bin_t(4,1));
+    EXPECT_TRUE(chess16.is_filled(bin_t(5,0)));
+}
+
+TEST(BinsTest,Staircase) {
+    
+    const int TOPLAYR = 44;
+    binmap_t staircase;
+    for(int i=0;i<TOPLAYR;i++)
+        staircase.set(bin_t(i,1));
+    
+    EXPECT_FALSE(staircase.is_filled(bin_t(TOPLAYR,0)));
+    EXPECT_FALSE(staircase.is_empty(bin_t(TOPLAYR,0)));
+
+    staircase.set(bin_t(0,0));
+    EXPECT_TRUE(staircase.is_filled(bin_t(TOPLAYR,0)));
+
+}
+
+TEST(BinsTest,Hole) {
+    
+    binmap_t hole;
+    hole.set(bin_t(8,0));
+    hole.reset(bin_t(6,1));
+    hole.reset(bin_t(6,2));
+    EXPECT_TRUE(hole.is_filled(bin_t(6,0)));
+    EXPECT_TRUE(hole.is_filled(bin_t(6,3)));
+    EXPECT_FALSE(hole.is_filled(bin_t(8,0)));
+    EXPECT_FALSE(hole.is_empty(bin_t(8,0)));
+    EXPECT_TRUE(hole.is_empty(bin_t(6,1)));
+    
+}
+
+TEST(BinsTest,Find){
+    
+    binmap_t hole;
+    hole.set(bin_t(4,0));
+    hole.reset(bin_t(1,1));
+    hole.reset(bin_t(0,7));
+    bin_t f = hole.find_empty().base_left();
+    EXPECT_EQ(bin_t(0,2),f);
+    
+}
+
+/*
+TEST(BinsTest,Stripes) {
+    
+    binmap_t zebra;
+    zebra.set(bin_t(5,0));
+    zebra.reset(bin_t(3,1));
+    zebra.reset(bin_t(1,12));
+    zebra.reset(bin_t(1,14));
+    int count;
+    uint64_t *stripes = zebra.get_stripes(count);
+    EXPECT_EQ(9,count);
+    EXPECT_EQ(0,stripes[0]);
+    EXPECT_EQ(0,stripes[1]);
+    EXPECT_EQ(8,stripes[2]);
+    EXPECT_EQ(16,stripes[3]);
+    EXPECT_EQ(24,stripes[4]);
+    EXPECT_EQ(26,stripes[5]);
+    EXPECT_EQ(28,stripes[6]);
+    EXPECT_EQ(30,stripes[7]);
+    EXPECT_EQ(32,stripes[8]);
+    free(stripes);
+
+}
+*/
+/*
+TEST(BinsTest,StripesAgg) {
+    
+    binmap_t zebra;
+    zebra.set(bin_t(0,1));
+    zebra.set(bin_t(0,2));
+    int count;
+    uint64_t *stripes = zebra.get_stripes(count);
+    EXPECT_EQ(3,count);
+    EXPECT_EQ(0,stripes[0]);
+    EXPECT_EQ(1,stripes[1]);
+    EXPECT_EQ(3,stripes[2]);
+    free(stripes);
+    
+}    
+*/
+
+TEST(BinsTest,Alloc) {
+
+    binmap_t b;
+    b.set(bin_t(1,0));
+    b.set(bin_t(1,1));
+    b.reset(bin_t(1,0));
+    b.reset(bin_t(1,1));
+    EXPECT_EQ(1,b.cells_number());
+
+}
+
+/*
+TEST(BinsTest,Remove) {
+    
+    binmap_t b;
+    b.set(bin_t(5,0));
+    binmap_t c;
+    c.set(bin_t(2,0));
+    c.set(bin_t(2,2));
+    b.remove(c);
+    EXPECT_TRUE(b.is_empty(bin_t(2,0)));
+    EXPECT_TRUE(b.is_filled(bin_t(2,1)));
+    EXPECT_TRUE(b.is_empty(bin_t(2,2)));
+    EXPECT_TRUE(b.is_filled(bin_t(2,3)));
+    EXPECT_TRUE(b.is_filled(bin_t(4,1)));
+    
+    binmap_t b16, b1024, b8192;
+    b16.set(bin_t(3,1));
+    b1024.set(bin_t(3,1));
+    b1024.set(bin_t(4,2));
+    b1024.set(bin_t(8,3));
+    b8192.set(bin_t(8,3));
+    b8192.set(bin_t(10,7));
+    
+    b1024.remove(b16);
+    b1024.remove(b8192);
+    
+    EXPECT_TRUE(b1024.is_empty(bin_t(3,1)));
+    EXPECT_TRUE(b1024.is_empty(bin_t(5,0)));
+    EXPECT_TRUE(b1024.is_empty(bin_t(9,1)));
+    EXPECT_TRUE(b1024.is_empty(bin_t(12,1)));
+    EXPECT_TRUE(b1024.is_filled(bin_t(4,2)));
+    
+    b8192.set(bin_t(2,3));
+    b16.remove(b8192);
+    EXPECT_TRUE(b16.is_empty(bin_t(2,3)));
+    EXPECT_TRUE(b16.is_filled(bin_t(2,2)));
+    
+}
+*/
+
+TEST(BinsTest,FindFiltered) {
+    
+    binmap_t data, filter;
+    data.set(bin_t(2,0));
+    data.set(bin_t(2,2));
+    data.set(bin_t(1,7));
+    filter.set(bin_t(4,0));
+    filter.reset(bin_t(2,1));
+    filter.reset(bin_t(1,4));
+    filter.reset(bin_t(0,13));
+    
+    bin_t x = binmap_t::find_complement(data, filter, bin_t(4,0), 0);
+    EXPECT_EQ(bin_t(0,12),x);
+    
+}
+
+
+TEST(BinsTest, Cover) {
+    
+    binmap_t b;
+    b.set(bin_t(2,0));
+    b.set(bin_t(4,1));
+    EXPECT_EQ(bin_t(4,1),b.cover(bin_t(0,30)));
+    EXPECT_EQ(bin_t(2,0),b.cover(bin_t(0,3)));
+    EXPECT_EQ(bin_t(2,0),b.cover(bin_t(2,0)));
+    //binmap_t c;
+    //EXPECT_EQ(bin64_t::ALL,b.cover(bin64_t(0,30)));
+    
+}
+
+
+TEST(BinsTest,FindFiltered2) {
+    
+    binmap_t data, filter;
+    for(int i=0; i<1024; i+=2)
+        data.set(bin_t(0,i));
+    for(int j=0; j<1024; j+=2)
+        filter.set(bin_t(0,j));
+    data.reset(bin_t(0,500));
+    EXPECT_EQ(bin_t(0,500),binmap_t::find_complement(data, filter, bin_t(10,0), 0).base_left());
+    data.set(bin_t(0,500));
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(10,0), 0).base_left());
+    
+}
+    
+TEST(BinsTest,CopyRange) {
+    binmap_t data, add;
+    data.set(bin_t(2,0));
+    data.set(bin_t(2,2));
+    data.set(bin_t(1,7));
+    add.set(bin_t(2,1));
+    add.set(bin_t(1,4));
+    add.set(bin_t(0,13));
+    add.set(bin_t(5,118));
+    binmap_t::copy(data, add, bin_t(3,0));
+    EXPECT_FALSE(data.is_empty(bin_t(3,0)));
+    EXPECT_FALSE(data.is_filled(bin_t(3,0)));
+    EXPECT_TRUE(data.is_empty(bin_t(2,0)));
+    EXPECT_TRUE(data.is_filled(bin_t(2,1)));
+    EXPECT_TRUE(data.is_empty(bin_t(1,6)));
+    EXPECT_TRUE(data.is_filled(bin_t(1,7)));
+}
+
+/*
+TEST(BinsTest, Mass) {
+    binmap_t b;
+    b.set(bin_t(6,0));
+    b.reset(bin_t(0,0));
+    EXPECT_EQ(63,b.mass());
+    EXPECT_FALSE(b.is_empty());
+    b.clear();
+    EXPECT_TRUE(b.is_empty());
+    EXPECT_EQ(0,b.mass());
+
+    binmap_t b50;
+    for(int i=0; i<50; i++)
+        b50.set(bin_t(4,i*2));
+    EXPECT_EQ(50<<4,b50.mass());
+}
+*/
+
+/*
+TEST(BinsTest,Twist) {
+    binmap_t b;
+    b.set(bin_t(3,2));
+    EXPECT_TRUE(b.is_filled(bin_t(3,2)));
+    EXPECT_TRUE(b.is_empty(bin_t(3,3)));
+    b.twist(1<<3);
+    EXPECT_TRUE(b.is_filled(bin_t(3,3)));
+    EXPECT_TRUE(b.is_empty(bin_t(3,2)));
+    bin_t tw = b.find(bin_t(5,0),binmap_t::FILLED);
+    while (tw.base_length()>(1<<3))
+        tw = tw.left();
+    tw = tw.twisted(1<<3);
+    EXPECT_EQ(bin_t(3,2),tw);
+    b.twist(0);
+    EXPECT_TRUE(b.is_filled(bin_t(3,2)));
+    EXPECT_TRUE(b.is_empty(bin_t(3,3)));
+}
+*/
+
+TEST(BinsTest,SeqLength) {
+    binmap_t b;
+    b.set(bin_t(3,0));
+    b.set(bin_t(1,4));
+    b.set(bin_t(0,10));
+    b.set(bin_t(3,2));
+    EXPECT_EQ(11,b.find_empty().base_offset());
+}
+
+TEST(BinsTest,EmptyFilled) {
+    // 1112 3312  2111 ....
+    binmap_t b;
+    
+    EXPECT_TRUE(b.is_empty(bin_t::ALL));
+    
+    b.set(bin_t(1,0));
+    b.set(bin_t(0,2));
+    b.set(bin_t(0,6));
+    b.set(bin_t(1,5));
+    b.set(bin_t(0,9));
+    
+    EXPECT_FALSE(b.is_empty(bin_t::ALL));
+    
+    EXPECT_TRUE(b.is_empty(bin_t(2,3)));
+    EXPECT_FALSE(b.is_filled(bin_t(2,3)));
+    //EXPECT_TRUE(b.is_solid(bin_t(2,3),binmap_t::MIXED));
+    EXPECT_TRUE(b.is_filled(bin_t(1,0)));
+    EXPECT_TRUE(b.is_filled(bin_t(1,5)));
+    EXPECT_FALSE(b.is_filled(bin_t(1,3)));
+    
+    b.set(bin_t(0,3));
+    b.set(bin_t(0,7));
+    b.set(bin_t(0,8));
+    
+    EXPECT_TRUE(b.is_filled(bin_t(2,0)));
+    EXPECT_TRUE(b.is_filled(bin_t(2,2)));
+    EXPECT_FALSE(b.is_filled(bin_t(2,1)));
+
+    b.set(bin_t(1,2));
+    EXPECT_TRUE(b.is_filled(bin_t(2,1)));
+}
+
+TEST(BinheapTest,Eat) {
+    
+    binheap b;
+    b.push(bin_t(0,1));
+    b.push(bin_t(0,3));
+    b.push(bin_t(2,0));
+    b.push(bin_t(2,4));
+    
+    EXPECT_EQ(bin_t(2,0),b.pop());
+    EXPECT_EQ(bin_t(2,4),b.pop());
+    EXPECT_EQ(bin_t::NONE,b.pop());
+    
+    for (int i=0; i<64; i++) {
+        b.push(bin_t(0,i));
+    }
+    b.push(bin_t(5,0));
+    EXPECT_EQ(bin_t(5,0),b.pop());
+    for (int i=32; i<64; i++)
+        EXPECT_EQ(bin_t(0,i),b.pop());
+}
+
+/*TEST(BinsTest,RangeOpTest) {
+    binmap_t a, b;
+    a.set(bin_t(0,0));
+    a.set(bin_t(0,2));
+    b.set(bin_t(0,1));
+    b.set(bin_t(0,3));
+    a.range_or(b,bin_t(1,0));
+    EXPECT_TRUE(a.is_filled(bin_t(1,0)));
+    EXPECT_FALSE(a.is_filled(bin_t(1,1)));
+    
+    binmap_t f, s;
+    f.set(bin_t(3,0));
+    s.set(bin_t(0,1));
+    s.set(bin_t(0,4));
+    f.range_remove(s,bin_t(2,1));
+    
+    EXPECT_TRUE(f.is_filled(bin_t(2,0)));
+    EXPECT_FALSE(f.is_filled(bin_t(0,4)));
+    EXPECT_TRUE(f.is_filled(bin_t(0,5)));
+    
+    binmap_t z, x;
+    z.set(bin_t(1,0));
+    z.set(bin_t(1,2));
+    x.set(bin_t(0,1));
+    x.set(bin_t(0,1));
+}
+*/
+
+/*
+TEST(BinsTest,CoarseBitmap) {
+    binmap_t b;
+    b.set(bin_t(4,0));
+    union {uint16_t i16[2]; uint32_t i32;};
+    b.to_coarse_bitmap(i16,bin_t(5,0),0);
+    EXPECT_EQ((1<<16)-1,i32);
+    
+    b.set(bin_t(14,0));
+    i32=0;
+    b.to_coarse_bitmap(i16,bin_t(15,0),10);
+    EXPECT_EQ((1<<16)-1,i32);
+    
+    binmap_t rough;
+    rough.set(bin_t(1,0));
+    rough.set(bin_t(0,2));
+    i32=0;
+    rough.to_coarse_bitmap(i16,bin_t(6,0),1);
+    EXPECT_EQ(1,i32);
+    
+    binmap_t ladder;
+    ladder.set(bin_t(6,2));
+    ladder.set(bin_t(5,2));
+    ladder.set(bin_t(4,2));
+    ladder.set(bin_t(3,2));
+    ladder.set(bin_t(2,2));
+    ladder.set(bin_t(1,2));
+    ladder.set(bin_t(0,2));
+    i32=0;
+    ladder.to_coarse_bitmap(i16,bin_t(8,0),3);
+    EXPECT_EQ(0x00ff0f34,i32);
+    
+    binmap_t bin;
+    bin.set(bin_t(3,0));
+    bin.set(bin_t(0,8));
+    i32 = 0;
+    bin.to_coarse_bitmap(i16,bin_t(4,0),0);
+    EXPECT_EQ((1<<9)-1,i32);
+    
+    i32 = 0;
+    bin.to_coarse_bitmap(i16,bin_t(7,0),3);
+    EXPECT_EQ(1,i32);
+    
+    i32 = 0;
+    bin.to_coarse_bitmap(i16,bin_t(4,0),3);
+    EXPECT_EQ(1,i32);
+    
+    i32 = 0;
+    bin.to_coarse_bitmap(i16,bin_t(2,0),1);
+    EXPECT_EQ(3,i32&3);
+
+    uint64_t bigint;
+    bigint = 0;
+    binmap_t bm;
+    bm.set(bin_t(6,0));
+    bm.to_coarse_bitmap((uint16_t*)&bigint,bin_t(6,0),0);
+    EXPECT_EQ( 0xffffffffffffffffULL, bigint );
+    
+}
+*/
+
+/*TEST(BinsTest,AddSub) {
+       binmap_t b;
+       b|=15;
+       b-=1;
+       ASSERT_TRUE(b.contains(2));
+       ASSERT_TRUE(b.contains(14));
+       ASSERT_FALSE(b.contains(3));
+       ASSERT_FALSE(b.contains(22));
+       ASSERT_TRUE(b.contains(12));
+       b-=13;
+       ASSERT_FALSE(b.contains(12));
+       ASSERT_FALSE(b.contains(14));
+       ASSERT_FALSE(b.contains(11));
+       ASSERT_TRUE(b.contains(10));
+}
+
+
+TEST(BinsTest,Peaks) {
+       bin::vec peaks = bin::peaks(11);
+       ASSERT_EQ(3,peaks.size());
+       ASSERT_EQ(15,peaks[0]);
+       ASSERT_EQ(18,peaks[1]);
+       ASSERT_EQ(19,peaks[2]);
+}
+
+TEST(BinsTest,Performance) {
+       binmap_t b;
+       std::set<int> s;
+       clock_t start, end;
+       double b_time, s_time;
+       int b_size, s_size;
+       
+       start = clock();
+       for(int i=1; i<(1<<20); i++)
+               b |= bin(i);
+       //b_size = b.bits.size();
+       end = clock();  
+       b_time = ((double) (end - start)) / CLOCKS_PER_SEC;
+       //ASSERT_EQ(1,b.bits.size());
+       
+       start = clock();
+       for(int i=1; i<(1<<20); i++)
+               s.insert(i);
+       s_size = s.size();
+       end = clock();
+       s_time = ((double) (end - start)) / CLOCKS_PER_SEC;
+       
+       printf("bins: %f (%i), set: %f (%i)\n",b_time,b_size,s_time,s_size);
+}*/
+
+int main (int argc, char** argv) {
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff --git a/src/libswift_udp/tests/binstest3.cpp b/src/libswift_udp/tests/binstest3.cpp
new file mode 100644 (file)
index 0000000..8382d94
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ *  binstest3.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ *  ==========================
+ *  Extended by Arno and Riccardo to hunt a bug in find_complement() with
+ *  a range parameter.
+ */
+#include "binmap.h"
+#include "binheap.h"
+
+#include <time.h>
+#include <set>
+#include <gtest/gtest.h>
+
+
+using namespace swift;
+
+
+TEST(BinsTest,FindFiltered) {
+    
+    binmap_t data, filter;
+    data.set(bin_t(2,0));
+    data.set(bin_t(2,2));
+    data.set(bin_t(1,7));
+    filter.set(bin_t(4,0));
+    filter.reset(bin_t(2,1));
+    filter.reset(bin_t(1,4));
+    filter.reset(bin_t(0,13));
+    
+    bin_t x = binmap_t::find_complement(data, filter, bin_t(4,0), 0);
+    EXPECT_EQ(bin_t(0,12),x);
+    
+}
+
+TEST(BinsTest,FindFiltered1b) {
+
+    binmap_t data, filter;
+    data.set(bin_t(2,0));
+    data.set(bin_t(2,2));
+    data.set(bin_t(1,7));
+    filter.set(bin_t(4,0));
+    filter.reset(bin_t(2,1));
+    filter.reset(bin_t(1,4));
+    filter.reset(bin_t(0,13));
+
+    char binstr[32];
+
+    bin_t s = bin_t(3,1);
+    fprintf(stderr,"Searching 0,12 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    bin_t x = binmap_t::find_complement(data, filter, s, 0);
+    EXPECT_EQ(bin_t(0,12),x);
+
+}
+
+
+TEST(BinsTest,FindFiltered1c) {
+
+    binmap_t data, filter;
+    data.set(bin_t(2,0));
+    data.set(bin_t(2,2));
+    data.set(bin_t(1,7));
+
+    filter.set(bin_t(4,0));
+    filter.reset(bin_t(2,1));
+    filter.reset(bin_t(1,4));
+    //filter.reset(bin_t(0,13));
+
+    char binstr[32];
+
+    bin_t s = bin_t(3,1);
+    fprintf(stderr,"Searching 0,12x from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    bin_t x = binmap_t::find_complement(data, filter, s, 0);
+    EXPECT_EQ(bin_t(0,12),x);
+
+}
+
+
+TEST(BinsTest,FindFiltered2) {
+    
+    binmap_t data, filter;
+    for(int i=0; i<1024; i+=2)
+        data.set(bin_t(0,i));
+    for(int j=0; j<1024; j+=2)
+        filter.set(bin_t(0,j));
+
+    fprintf(stderr,"test: width %d\n", filter.cells_number() );
+    fprintf(stderr,"test: empty %llu\n", filter.find_empty().toUInt() );
+
+
+    data.reset(bin_t(0,500));
+    EXPECT_EQ(bin_t(0,500),binmap_t::find_complement(data, filter, bin_t(10,0), 0).base_left());
+    data.set(bin_t(0,500));
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(10,0), 0).base_left());
+    
+}
+
+
+// Range is strict subtree
+TEST(BinsTest,FindFiltered3) {
+
+    binmap_t data, filter;
+    for(int i=0; i<1024; i+=2)
+        data.set(bin_t(0,i));
+    for(int j=0; j<1024; j+=2)
+        filter.set(bin_t(0,j));
+    data.reset(bin_t(0,500));
+    EXPECT_EQ(bin_t(0,500),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+    data.set(bin_t(0,500));
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+
+}
+
+// 1036 leaf tree
+
+TEST(BinsTest,FindFiltered4) {
+
+    binmap_t data, filter;
+    for(int i=0; i<1036; i+=2)
+        data.set(bin_t(0,i));
+    for(int j=0; j<1036; j+=2)
+        filter.set(bin_t(0,j));
+    data.reset(bin_t(0,500));
+    EXPECT_EQ(bin_t(0,500),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+    data.set(bin_t(0,500));
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+
+}
+
+// Make 8 bin hole in 1036 tree
+
+TEST(BinsTest,FindFiltered5) {
+
+    binmap_t data, filter;
+    for(int i=0; i<1036; i++) //completely full
+        data.set(bin_t(0,i));
+    for(int j=0; j<1036; j++)
+        filter.set(bin_t(0,j));
+
+    for (int j=496; j<=503; j++)
+       data.reset(bin_t(0,j));
+
+    EXPECT_EQ(bin_t(3,62),binmap_t::find_complement(data, filter, bin_t(9,0), 0) );
+    EXPECT_EQ(bin_t(0,496),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+}
+
+
+// Use simple example tree from RFC
+TEST(BinsTest,FindFiltered6) {
+
+    binmap_t data, filter;
+    for(int i=0; i<14; i+=2)  //completely full example tree
+        data.set(bin_t(i));
+    for(int j=0; j<14; j+=2)
+        filter.set(bin_t(j));
+
+    for (int j=4; j<=6; j+=2) // reset leaves 4 and 6 (int)
+       data.reset(bin_t(j));
+
+    EXPECT_EQ(bin_t(1,1),binmap_t::find_complement(data, filter, bin_t(2,0), 0) );
+    EXPECT_EQ(bin_t(0,2),binmap_t::find_complement(data, filter, bin_t(2,0), 0).base_left());
+}
+
+
+// diff in right tree, range is left tree
+TEST(BinsTest,FindFiltered7) {
+
+    binmap_t data, filter;
+    for(int i=0; i<14; i+=2)  //completely full example tree
+        data.set(bin_t(i));
+    data.reset(bin_t(4));        // clear 4
+    for(int j=0; j<14; j+=2)
+        filter.set(bin_t(j));
+    filter.reset(bin_t(4));
+
+    for (int j=8; j<=10; j+=2) // make diff out of range
+       data.reset(bin_t(j));
+
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,0), 0) );
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,0), 0).base_left());
+}
+
+
+
+// diff in left tree, range is right tree
+TEST(BinsTest,FindFiltered8) {
+
+    binmap_t data, filter;
+    for(int i=0; i<14; i+=2)  //completely full example tree
+        data.set(bin_t(i));
+    data.reset(bin_t(4));        // clear 4
+    for(int j=0; j<14; j+=2)
+        filter.set(bin_t(j));
+    filter.reset(bin_t(4));
+
+    for (int j=4; j<=6; j+=2)  // make diff out of range
+       data.reset(bin_t(j));
+
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,1), 0) );
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,1), 0).base_left());
+}
+
+
+// reverse empty/full
+TEST(BinsTest,FindFiltered9) {
+
+    binmap_t data, filter;
+    for(int i=0; i<14; i+=2)  //completely empty example tree
+        data.reset(bin_t(i));
+    data.set(bin_t(4));          // clear 4
+    for(int j=0; j<14; j+=2)
+        filter.reset(bin_t(j));
+    filter.set(bin_t(4));
+
+    for (int j=4; j<=6; j+=2)  // make diff out of range
+       data.set(bin_t(j));
+
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,1), 0) );
+    EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(2,1), 0).base_left());
+}
+
+
+// Make 8 bin hole in 999 tree, left subtree
+
+TEST(BinsTest,FindFiltered10) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i++) //completely full
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j++)
+        filter.set(bin_t(0,j));
+
+    for (int j=496; j<=503; j++)
+       data.reset(bin_t(0,j));
+
+    EXPECT_EQ(bin_t(3,62),binmap_t::find_complement(data, filter, bin_t(9,0), 0) );
+    EXPECT_EQ(bin_t(0,496),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+}
+
+
+// Make 8 bin hole in 999 tree, right subtree, does not start a 8-bin substree
+TEST(BinsTest,FindFiltered11) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i++) //completely full
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j++)
+        filter.set(bin_t(0,j));
+
+    for (int j=514; j<=521; j++)
+       data.reset(bin_t(0,j));
+
+    EXPECT_EQ(bin_t(1,257),binmap_t::find_complement(data, filter, bin_t(9,1), 0) );
+    EXPECT_EQ(bin_t(0,514),binmap_t::find_complement(data, filter, bin_t(9,1), 0).base_left());
+}
+
+// Make 8 bin hole in 999 tree, move hole
+TEST(BinsTest,FindFiltered12) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i++) //completely full
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j++)
+        filter.set(bin_t(0,j));
+
+    for (int x=0; x<999-8; x++)
+    {
+       fprintf(stderr,"x%u ", x);
+       for (int j=x; j<=x+7; j++)
+               data.reset(bin_t(0,j));
+
+       int subtree = (x <= 511) ? 0 : 1;
+       EXPECT_EQ(bin_t(0,x),binmap_t::find_complement(data, filter, bin_t(9,subtree), 0).base_left());
+
+       // Restore
+       for (int j=x; j<=x+7; j++) {
+               data.set(bin_t(0,j));
+       }
+    }
+}
+
+
+// Make 8 bin hole in sparse 999 tree, move hole
+TEST(BinsTest,FindFiltered13) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i+=2) // sparse
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j+=2)
+        filter.set(bin_t(0,j));
+
+    for (int x=0; x<999-8; x++)
+    {
+       fprintf(stderr,"x%u ", x);
+       for (int j=x; j<=x+7; j++)
+               data.reset(bin_t(0,j));
+
+       int y = (x % 2) ? x+1 : x;
+       int subtree = (x <= 511) ? 0 : 1;
+       if (x < 511)
+               EXPECT_EQ(bin_t(0,y),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+       else if (x == 511) // sparse bitmap 101010101..., so actual diff in next subtree
+               EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+       else
+               EXPECT_EQ(bin_t(0,y),binmap_t::find_complement(data, filter, bin_t(9,1), 0).base_left());
+
+
+        for(int i=0; i<999; i+=2) // sparse
+            data.set(bin_t(0,i));
+    }
+}
+
+
+// Make 8 bin hole in sparse 999 tree, move hole
+TEST(BinsTest,FindFiltered14) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i+=2) // sparse
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j+=2)
+        filter.set(bin_t(0,j));
+
+    // Add other diff
+    filter.set(bin_t(0,995));
+
+    for (int x=0; x<999-8; x++)
+    {
+       fprintf(stderr,"x%u ", x);
+       for (int j=x; j<=x+7; j++)
+               data.reset(bin_t(0,j));
+
+       int y = (x % 2) ? x+1 : x;
+       int subtree = (x <= 511) ? 0 : 1;
+       if (x < 511)
+               EXPECT_EQ(bin_t(0,y),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+       else if (x == 511) // sparse bitmap 101010101..., so actual diff in next subtree
+               EXPECT_EQ(bin_t::NONE,binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+       else
+               EXPECT_EQ(bin_t(0,y),binmap_t::find_complement(data, filter, bin_t(9,1), 0).base_left());
+
+
+        for(int i=0; i<999; i+=2) // sparse
+            data.set(bin_t(0,i));
+    }
+}
+
+
+
+// Make holes at 292, problematic in a specific experiment
+TEST(BinsTest,FindFiltered15) {
+
+    binmap_t data, filter;
+    for(int i=0; i<999; i++) // completely full
+        data.set(bin_t(0,i));
+    for(int j=0; j<999; j++)
+        filter.set(bin_t(0,j));
+
+    data.reset(bin_t(292));
+    data.reset(bin_t(296));
+    data.reset(bin_t(514));
+    data.reset(bin_t(998));
+
+       EXPECT_EQ(bin_t(292),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+}
+
+
+
+// VOD like. Make first hole at 292.
+TEST(BinsTest,FindFiltered16) {
+
+    binmap_t data, filter;
+    for(int i=0; i<292/2; i++) // prefix full
+        data.set(bin_t(0,i));
+    for(int i=147; i<999; i+=21) // postfix sparse
+    {
+       for (int x=0; x<8; x++)
+               data.set(bin_t(0,i+x));
+    }
+
+    for(int j=0; j<999; j++)
+        filter.set(bin_t(0,j));
+
+       EXPECT_EQ(bin_t(292),binmap_t::find_complement(data, filter, bin_t(9,0), 0).base_left());
+}
+
+
+// VOD like. Make first hole at 292.
+TEST(BinsTest,FindFiltered17) {
+
+    binmap_t offer, ack_hint_out;
+    for(int i=0; i<999; i++) // offer completely full
+        offer.set(bin_t(0,i));
+
+    for(int i=0; i<292/2; i++) // request prefix full
+        ack_hint_out.set(bin_t(0,i));
+    for(int i=147; i<999; i+=21) // request postfix sparse
+    {
+       for (int x=0; x<8; x++)
+               ack_hint_out.set(bin_t(0,i+x));
+    }
+
+       binmap_t binmap;
+
+       // report the first bin we find
+       int layer = 0;
+       bin_t::uint_t twist = 0;
+       bin_t hint = bin_t::NONE;
+       while (hint.is_none() && layer <10)
+       {
+               char binstr[32];
+
+               bin_t curr = bin_t(layer++,0);
+               binmap.fill(offer);
+               binmap_t::copy(binmap, ack_hint_out, curr);
+               hint = binmap_t::find_complement(binmap, offer, twist);
+               binmap.clear();
+       }
+
+       EXPECT_EQ(bin_t(292),hint);
+}
+
+
+// VOD like. Make first hole at 292. Twisting + patching holes
+TEST(BinsTest,FindFiltered19) {
+
+    binmap_t offer, ack_hint_out;
+    for(int i=0; i<999; i++) // offer completely full
+        offer.set(bin_t(0,i));
+
+    for(int i=0; i<292/2; i++) // request prefix full
+        ack_hint_out.set(bin_t(0,i));
+    for(int i=147; i<999; i+=21) // request postfix sparse
+    {
+       for (int x=0; x<8; x++)
+               ack_hint_out.set(bin_t(0,i+x));
+    }
+
+       binmap_t binmap;
+
+       int layer = 0;
+       bin_t::uint_t twist = 0;
+       bin_t hint = bin_t::NONE;
+       while (!hint.contains(bin_t(292)))
+       {
+               char binstr[32];
+
+               twist = rand();
+
+               bin_t curr = bin_t(layer,0);
+               if (layer < 10)
+                       layer++;
+
+               binmap.fill(offer);
+               binmap_t::copy(binmap, ack_hint_out, curr);
+               hint = binmap_t::find_complement(binmap, offer, twist);
+
+               if (!hint.is_none())
+                       fprintf(stderr,"Found alt ");
+               binmap.clear();
+
+               //patch hole
+               ack_hint_out.set(hint);
+       }
+
+       char binstr[32],binstr2[32];
+       EXPECT_EQ(bin_t(292),hint);
+}
+
+
+void create_ack_hint_out(binmap_t &ack_hint_out)
+{
+       ack_hint_out.clear();
+    for(int i=0; i<292/2; i++) // request prefix full
+        ack_hint_out.set(bin_t(0,i));
+    for(int i=147; i<999; i+=21) // request postfix sparse
+    {
+       for (int x=0; x<8; x++)
+               ack_hint_out.set(bin_t(0,i+x));
+    }
+}
+
+
+
+// VOD like. Make first hole at 292. Twisting + patching holes. Stalled
+// at Playbackpos, looking increasingly higher layers.
+TEST(BinsTest,FindFiltered20) {
+
+    binmap_t offer, ack_hint_out;
+    for(int i=0; i<999; i++) // offer completely full
+        offer.set(bin_t(0,i));
+
+    create_ack_hint_out(ack_hint_out);
+
+       binmap_t binmap;
+
+       int layer = 0;
+       bin_t::uint_t twist = 0;
+       bin_t hint = bin_t::NONE;
+
+       for (layer=0; layer<=9; layer++)
+       {
+               fprintf(stderr,"Layer %d\n", layer );
+               while (!hint.contains(bin_t(292)))
+               {
+                       char binstr[32];
+
+                       twist = rand();
+
+                       bin_t curr = bin_t(0,292/2);
+                       for (int p=0; p<layer; p++)
+                               curr = curr.parent();
+
+                       binmap.fill(offer);
+                       binmap_t::copy(binmap, ack_hint_out, curr);
+                       hint = binmap_t::find_complement(binmap, offer, twist);
+
+                       if (!hint.is_none())
+                               fprintf(stderr,"Found alt %s ", hint.str(binstr) );
+                       binmap.clear();
+
+                       //patch hole
+                       ack_hint_out.set(hint);
+               }
+               create_ack_hint_out(ack_hint_out);
+       }
+
+       EXPECT_EQ(bin_t(292),hint);
+}
+
+
+void DoFindFilteredRiccardo(bin_t::uint_t twist)
+{
+    binmap_t data, filter;
+
+    for(int i=0; i<1024; i+=2)
+        filter.set(bin_t(0,i));
+
+    char binstr[32];
+
+    // Case 1
+    bin_t s(1,2);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,1);
+    fprintf(stderr,"Searching 0,6 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    bin_t got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,6),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 2
+    s = bin_t(1,8);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,4);
+    fprintf(stderr,"Searching 0,18 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,18),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 5
+    s = bin_t(1,80);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,40);
+    fprintf(stderr,"Searching 0,162 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,162),got);
+    EXPECT_EQ(true,s.contains(got));
+
+    // Case 6
+
+    s = bin_t(1,84);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,42);
+    fprintf(stderr,"Searching 0,168 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,170),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 3
+    s = bin_t(1,86);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,43);
+    fprintf(stderr,"Searching 0,174 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,174),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 7
+    s = bin_t(1,90);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+
+    s = bin_t(2,45);
+    fprintf(stderr,"Searching 0,182 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,182),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 4
+    s = bin_t(1,92);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,46);
+    fprintf(stderr,"Searching 0,184 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,186),got);
+    EXPECT_EQ(true,s.contains(got));
+
+
+    // Case 8
+    s = bin_t(1,94);
+    data.set(s);
+
+    fprintf(stderr,"Setting from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    s = bin_t(2,47);
+    fprintf(stderr,"Searching 0,188 from %s ", s.base_left().str(binstr ) );
+    fprintf(stderr,"to %s\n", s.base_right().str(binstr ) );
+
+    got = binmap_t::find_complement(data, filter, s, twist).base_left();
+    EXPECT_EQ(bin_t(0,190),got);
+    EXPECT_EQ(true,s.contains(got));
+}
+
+
+TEST(BinsTest,FindFilteredRiccardo3) {
+
+       DoFindFilteredRiccardo(0);
+}
+
+
+TEST(BinsTest,FindFilteredRiccardo3Twist) {
+
+       DoFindFilteredRiccardo( rand() );
+}
+
+
+
+
+
+
+int main (int argc, char** argv) {
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff --git a/src/libswift_udp/tests/congctrltest.cpp b/src/libswift_udp/tests/congctrltest.cpp
new file mode 100644 (file)
index 0000000..1bae950
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  congctrltest.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 7/13/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <stdint.h>
+#include <queue>
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+#include "p2tp.h"
+
+using namespace std;
+using namespace p2tp;
+
+class SimPeer;
+
+struct SimPacket {
+    SimPacket(int from, int to, const SimPacket* toack, bool data) ;
+    int peerfrom, peerto;
+    tint datatime;
+    tint acktime;
+    tint arrivaltime;
+};
+
+tint now = 0;
+
+/** very simplified; uplink is the bottleneck */
+class SimPeer {
+public:
+    SimPeer (tint tt, tint lt, int qlen) : travtime(tt), latency(lt), queue_length(qlen) {}
+    int queue_length;
+    int travtime;
+    tint freetime;
+    tint latency;
+    int unackd;
+    int rcvd, sent;
+    queue<SimPacket> packet_queue;
+    queue<SimPacket> dropped_queue;
+    CongestionControl congc;
+    
+    void send(SimPacket pck) {
+        if (packet_queue.size()==queue_length) {
+            dropped_queue.push(pck);
+            return;
+        }
+        tint start = max(now,freetime);
+        tint done = pck.datatime ? start+travtime : start;
+        freetime = done;
+        pck.arrivaltime = done + latency;
+        packet_queue.push(pck);
+    }
+    
+    SimPacket recv () {
+        assert(!packet_queue.empty());
+        SimPacket ret = packet_queue.front();
+        packet_queue.pop();
+        return ret;
+    }
+    
+    tint next_recv_time () const {
+        return packet_queue.empty() ? NEVER : packet_queue.front().arrivaltime;
+    }
+    
+    void    turn () {
+        SimPacket rp = recv();
+        SimPacket reply;
+        now = rp.arrivaltime;
+        if (rp.acktime) {
+            congc.RttSample(rp.arrivaltime-rp.acktime);
+            congc.OnCongestionEvent(CongestionControl::ACK_EV);
+            unackd--;
+            rcvd++;
+        }
+        if (rp.datatime) {
+            congc.OnCongestionEvent(CongestionControl::DATA_EV);
+            reply.acktime = reply.datatime;
+        }
+        if (!dropped_queue.empty() && dropped_queue.top().datatime<now+THR)
+            congc.OnCongestionEvent(CongestionControl::LOSS_EV);
+        if (congc.cwnd()>unackd) {
+            unackd++;
+            reply.datatime = now;
+            sent++;
+        }
+        rp.from->send(reply);
+    }
+};
+
+TEST(P2TP, TailDropTest) {
+    // two peers exchange packets over 100ms link with tail-drop discipline
+    // bw 1Mbits => travel time of 1KB is ~10ms
+    SimPeer a(10*MSEC,100*MSEC,20), b(10*MSEC,100*MSEC,20);
+    a.send(SimPacket(&b,now,0,0));
+    while (now<60*60*SEC) 
+        if (a.next_recv_time()<b.next_recv_time())
+            a.turn();
+        else
+            b.turn();
+}
+
+int main (int argc, char** argv) {
+       bin::init();
+       bins::init();
+       google::InitGoogleLogging(argv[0]);
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+       
+}
diff --git a/src/libswift_udp/tests/connecttest.cpp b/src/libswift_udp/tests/connecttest.cpp
new file mode 100644 (file)
index 0000000..6910660
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  connecttest.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/19/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <sys/stat.h>
+#include <time.h>
+#include <gtest/gtest.h>
+#include "swift.h"
+
+
+using namespace swift;
+
+struct event evcompl;
+int size, copy;
+
+void IsCompleteCallback(int fd, short event, void *arg) {
+    if (swift::SeqComplete(copy)!=size)
+       evtimer_add(&evcompl, tint2tv(TINT_SEC));
+    else
+       event_base_loopexit(Channel::evbase, NULL);
+}
+
+TEST(Connection,CwndTest) {
+
+    Channel::evbase = event_base_new();
+
+    srand ( time(NULL) );
+
+    unlink("test_file0-copy.dat");
+#ifdef WIN32
+    struct _stat st;
+#else
+    struct stat st;
+#endif
+    ASSERT_EQ(0,stat("test_file0.dat", &st));
+    size = st.st_size;//, sizek = (st.st_size>>10) + (st.st_size%1024?1:0) ;
+    Channel::SELF_CONN_OK = true;
+
+    int sock1 = swift::Listen(7001);
+    ASSERT_TRUE(sock1>=0);
+
+    int file = swift::Open("test_file0.dat");
+    FileTransfer* fileobj = FileTransfer::file(file);
+    //FileTransfer::instance++;
+
+    swift::SetTracker(Address("127.0.0.1",7001));
+
+    copy = swift::Open("test_file0-copy.dat",fileobj->root_hash());
+
+    evtimer_assign(&evcompl, Channel::evbase, IsCompleteCallback, NULL);
+    evtimer_add(&evcompl, tint2tv(TINT_SEC));
+
+    //swift::Loop(TINT_SEC);
+    event_base_dispatch(Channel::evbase);
+
+    //int count = 0;
+    //while (swift::SeqComplete(copy)!=size && count++<600)
+    //    swift::Loop(TINT_SEC);
+    ASSERT_EQ(size,swift::SeqComplete(copy));
+
+    swift::Close(file);
+    swift::Close(copy);
+
+    swift::Shutdown(sock1);
+
+}
+
+
+int main (int argc, char** argv) {
+
+    swift::LibraryInit();
+    testing::InitGoogleTest(&argc, argv);
+    Channel::debug_file = stdout;
+    int ret = RUN_ALL_TESTS();
+    return ret;
+
+}
diff --git a/src/libswift_udp/tests/dgramtest.cpp b/src/libswift_udp/tests/dgramtest.cpp
new file mode 100644 (file)
index 0000000..701b9c3
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  dgramtest.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/13/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <gtest/gtest.h>
+//#include <glog/logging.h>
+#include "swift.h" // Arno: for LibraryInit
+
+using namespace swift;
+
+struct event_base *evbase;
+struct event evrecv;
+
+void ReceiveCallback(int fd, short event, void *arg) {
+}
+
+TEST(Datagram, AddressTest) {
+    Address addr("127.0.0.1:1000");
+    EXPECT_EQ(INADDR_LOOPBACK,addr.ipv4());
+    EXPECT_EQ(1000,addr.port());
+    Address das2("node300.das2.ewi.tudelft.nl:20000");
+    Address das2b("130.161.211.200:20000");
+    EXPECT_EQ(das2.ipv4(),das2b.ipv4());
+    EXPECT_EQ(20000,das2.port());
+}
+
+
+TEST(Datagram, BinaryTest) {
+       evutil_socket_t socket = Channel::Bind(7001);
+       ASSERT_TRUE(socket>0);
+       struct sockaddr_in addr;
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(7001);
+       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       const char * text = "text";
+       const uint8_t num8 = 0xab;
+       const uint16_t num16 = 0xabcd;
+       const uint32_t num32 = 0xabcdef01;
+       const uint64_t num64 = 0xabcdefabcdeffULL;
+       char buf[1024];
+       int i;
+       struct evbuffer *snd = evbuffer_new();
+       evbuffer_add(snd, text, strlen(text));
+       evbuffer_add_8(snd, num8);
+       evbuffer_add_16be(snd, num16);
+       evbuffer_add_32be(snd, num32);
+       evbuffer_add_64be(snd, num64);
+       int datalen = evbuffer_get_length(snd);
+       unsigned char *data = evbuffer_pullup(snd, datalen);
+       for(i=0; i<datalen; i++)
+           sprintf(buf+i*2,"%02x",*(data+i));
+       buf[i*2] = 0;
+       EXPECT_STREQ("74657874ababcdabcdef01000abcdefabcdeff",buf);
+       ASSERT_EQ(datalen,Channel::SendTo(socket, addr, snd));
+       evbuffer_free(snd);
+       event_assign(&evrecv, evbase, socket, EV_READ, ReceiveCallback, NULL);
+       event_add(&evrecv, NULL);
+       event_base_dispatch(evbase);
+       struct evbuffer *rcv = evbuffer_new();
+       Address address;
+       ASSERT_EQ(datalen,Channel::RecvFrom(socket, address, rcv));
+       evbuffer_remove(rcv, buf, strlen(text));
+       buf[strlen(text)] = 0;
+       uint8_t rnum8 = evbuffer_remove_8(rcv);
+       uint16_t rnum16 = evbuffer_remove_16be(rcv);
+       uint32_t rnum32 = evbuffer_remove_32be(rcv);
+       uint64_t rnum64 = evbuffer_remove_64be(rcv);
+       EXPECT_STREQ("text",buf);
+       EXPECT_EQ(0xab,rnum8);
+       EXPECT_EQ(0xabcd,rnum16);
+       EXPECT_EQ(0xabcdef01,rnum32);
+       EXPECT_EQ(0xabcdefabcdeffULL,rnum64);
+       evbuffer_free(rcv);
+       Channel::CloseSocket(socket);
+}
+
+TEST(Datagram,TwoPortTest) {
+       int sock1 = Channel::Bind("0.0.0.0:10001");
+       int sock2 = Channel::Bind("0.0.0.0:10002");
+       ASSERT_TRUE(sock1>0);
+       ASSERT_TRUE(sock2>0);
+       /*struct sockaddr_in addr1, addr2;
+       addr1.sin_family = AF_INET;
+       addr1.sin_port = htons(10001);
+       addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       addr2.sin_family = AF_INET;
+       addr2.sin_port = htons(10002);
+       addr2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);*/
+       struct evbuffer *snd = evbuffer_new();
+       evbuffer_add_32be(snd, 1234);
+       Channel::SendTo(sock1,Address("127.0.0.1:10002"),snd);
+       evbuffer_free(snd);
+       event_assign(&evrecv, evbase, sock2, EV_READ, ReceiveCallback, NULL);
+       event_add(&evrecv, NULL);
+       event_base_dispatch(evbase);
+       struct evbuffer *rcv = evbuffer_new();
+       Address address;
+       Channel::RecvFrom(sock2, address, rcv);
+       uint32_t test = evbuffer_remove_32be(rcv);
+       ASSERT_EQ(1234,test);
+       evbuffer_free(rcv);
+       Channel::CloseSocket(sock1);
+       Channel::CloseSocket(sock2);
+}
+
+int main (int argc, char** argv) {
+       swift::LibraryInit();
+       evbase = event_base_new();
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff --git a/src/libswift_udp/tests/freemap.cpp b/src/libswift_udp/tests/freemap.cpp
new file mode 100755 (executable)
index 0000000..9c4d750
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  freemap.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <time.h>
+#include <gtest/gtest.h>
+#include <set>
+#include "binmap.h"
+
+using namespace swift;
+
+#ifdef _MSC_VER
+       #define RANDOM  rand
+#else
+       #define RANDOM  random
+#endif
+
+uint8_t rand_norm (uint8_t lim) {
+    long rnd = RANDOM() & ((1<<lim)-1);
+    uint8_t bits = 0;
+    while (rnd) {
+        bits += rnd&1;
+        rnd >>= 1;
+    }
+    return bits;
+}
+
+TEST(FreemapTest,Freemap) {
+    binmap_t space;
+    const bin_t top(30,0);
+    space.reset(top);
+    typedef std::pair<int,bin_t> timebin_t;
+    typedef std::set<timebin_t> ts_t;
+    ts_t to_free;
+    for (int t=0; t<1000000; t++) {
+
+       if ((t % 1000) == 0)
+               printf(".");
+
+        if (t<500000 || t>504000) {
+            uint8_t lr = rand_norm(28);
+            bin_t alloc = space.find_empty();
+            while (alloc.layer()>lr)
+                alloc = alloc.left();
+            ASSERT_NE(0ULL,~alloc.toUInt());
+            EXPECT_TRUE(space.is_empty(alloc));
+            space.set(alloc);
+            long dealloc_time = 1<<rand_norm(22);
+#ifdef SHOWPUTPUT
+            printf("alloc 2**%i starting at %lli for %li ticks\n",
+                (int)lr,alloc.toUInt(),dealloc_time);
+#endif
+            dealloc_time += t;
+            to_free.insert(timebin_t(dealloc_time,alloc));
+        }
+        // now, the red-black tree
+        while (to_free.begin()->first<=t) {
+            bin_t freebin = to_free.begin()->second;
+            to_free.erase(to_free.begin());
+            space.reset(freebin);
+#ifdef SHOWOUTPUT
+            printf("freed at %lli\n",
+                freebin.toUInt());
+#endif
+       }
+        // log: space taken, gaps, binmap cells, tree cells
+        int cells = space.cells_number();
+
+#ifdef SHOWOUTPUT
+        printf("time %i cells used %i blocks %i\n",
+                t,cells,(int)to_free.size());
+#endif
+        //space.dump("space");
+    }
+    for(ts_t::iterator i=to_free.begin(); i!=to_free.end(); i++)
+        space.reset(i->second);
+    EXPECT_TRUE(space.is_empty(top));
+}
+
+int main (int argc, char** argv) {
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff --git a/src/libswift_udp/tests/hashtest.cpp b/src/libswift_udp/tests/hashtest.cpp
new file mode 100644 (file)
index 0000000..be5865e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  hashtest.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/12/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <fcntl.h>
+#include "bin.h"
+#include <gtest/gtest.h>
+#include "hashtree.h"
+
+using namespace swift;
+
+char hash123[] = "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0";
+char rooth123[] = "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0";
+
+char hash456a[] = "4d38c7459a659d769bb956c2d758d266008199a4";
+char hash456b[] = "a923e4b60d2a2a2a5ede87479e0314b028e3ae60";
+char rooth456[] = "5b53677d3a695f29f1b4e18ab6d705312ef7f8c3";
+
+
+TEST(Sha1HashTest,Trivial) {
+       Sha1Hash hash("123\n");
+       EXPECT_STREQ(hash123,hash.hex().c_str());
+}
+
+
+TEST(Sha1HashTest,OfferDataTest) {
+       Sha1Hash roothash123(true,hash123);
+       //for(bin_t pos(0,0); !pos.is_all(); pos=pos.parent())
+       //      roothash123 = Sha1Hash(roothash123,Sha1Hash::ZERO);
+    unlink("123");
+    EXPECT_STREQ(rooth123,roothash123.hex().c_str());
+       HashTree tree("123",roothash123);
+    tree.OfferHash(bin_t(0,0),Sha1Hash(true,hash123));
+       ASSERT_EQ(1,tree.size_in_chunks());
+    ASSERT_TRUE(tree.OfferData(bin_t(0,0), "123\n", 4));
+    unlink("123");
+       ASSERT_EQ(4,tree.size());
+}
+
+
+TEST(Sha1HashTest,SubmitTest) {
+    FILE* f123 = fopen("123","wb+");
+    fprintf(f123, "123\n");
+    fclose(f123);
+    HashTree ht123("123");
+    EXPECT_STREQ(hash123,ht123.hash(bin_t(0,0)).hex().c_str());
+    EXPECT_STREQ(rooth123,ht123.root_hash().hex().c_str());
+    EXPECT_EQ(4,ht123.size());
+}
+
+
+TEST(Sha1HashTest,OfferDataTest2) {
+       char data456a[1024]; // 2 chunks with cs 1024, 3 nodes in tree
+       for (int i=0; i<1024; i++)
+               data456a[i] = '$';
+       char data456b[4];
+       for (int i=0; i<4; i++)
+               data456b[i] = '$';
+
+    FILE* f456 = fopen("456","wb");
+    fwrite(data456a,1,1024,f456);
+    fwrite(data456b,1,4,f456);
+    fclose(f456);
+
+       Sha1Hash roothash456(Sha1Hash(true,hash456a),Sha1Hash(true,hash456b));
+    unlink("456");
+    EXPECT_STREQ(rooth456,roothash456.hex().c_str());
+       HashTree tree("456",roothash456);
+       tree.OfferHash(bin_t(1,0),roothash456);
+    tree.OfferHash(bin_t(0,0),Sha1Hash(true,hash456a));
+    tree.OfferHash(bin_t(0,1),Sha1Hash(true,hash456b));
+       ASSERT_EQ(2,tree.size_in_chunks());
+    ASSERT_TRUE(tree.OfferData(bin_t(0,0), data456a, 1024));
+    ASSERT_TRUE(tree.OfferData(bin_t(0,1), data456b, 4));
+    unlink("456");
+       ASSERT_EQ(1028,tree.size());
+}
+
+
+/*TEST(Sha1HashTest,HashFileTest) {
+       uint8_t a [1024], b[1024], c[1024];
+       memset(a,'a',1024);
+       memset(b,'b',1024);
+       memset(c,'c',1024);
+       Sha1Hash aaahash(a,1024), bbbhash(b,1024), ccchash(c,1024);
+       Sha1Hash abhash(aaahash,bbbhash), c0hash(ccchash,Sha1Hash::ZERO);
+       Sha1Hash aabbccroot(abhash,c0hash);
+       for(bin pos=bin(7); pos<bin::ALL; pos=pos.parent())
+               aabbccroot = Sha1Hash(aabbccroot,Sha1Hash::ZERO);
+       int f = open("testfile",O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       write(f,a,1024);
+       write(f,b,1024);
+       write(f,c,1024);
+       HashTree filetree(f);
+       close(f);
+       ASSERT_TRUE(aabbccroot==filetree.root);
+       EXPECT_EQ(2,filetree.peaks.size());
+       EXPECT_TRUE(aaahash==filetree[1]);
+       HashTree bootstree(filetree.root);
+       EXPECT_EQ( HashTree::DUNNO, bootstree.offer(filetree.peaks[0].first,filetree.peaks[0].second) );
+       EXPECT_EQ( HashTree::PEAK_ACCEPT, bootstree.offer(filetree.peaks[1].first,filetree.peaks[1].second) );
+       EXPECT_EQ( 3, bootstree.length );
+       EXPECT_EQ( 4, bootstree.mass );
+       EXPECT_EQ( HashTree::DUNNO, bootstree.offer(1,aaahash) );
+       EXPECT_EQ( HashTree::ACCEPT, bootstree.offer(2,bbbhash) );
+       EXPECT_TRUE ( bootstree.bits[3]==abhash );
+       EXPECT_TRUE ( bootstree.bits[1]==aaahash );
+       EXPECT_TRUE ( bootstree.bits[2]==bbbhash );
+       EXPECT_FALSE ( bootstree.bits[2]==aaahash );
+}*/
+
+
+int main (int argc, char** argv) {
+       //bin::init();
+
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+
+}
diff --git a/src/libswift_udp/tests/ledbattest.cpp b/src/libswift_udp/tests/ledbattest.cpp
new file mode 100644 (file)
index 0000000..b18c217
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *  ledbattest.cpp
+ *
+ *  BROKEN: Arno: must be rewritten to libevent
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+#include <deque>
+#include "swift.h"
+#include <gtest/gtest.h>
+
+using namespace swift;
+using namespace std;
+
+/**
+  TODO
+  * losses
+  * smooth rate
+  * seq 12345 stop
+  * busy pipe => negative cwnd
+*/
+
+TEST(Datagram,LedbatTest) {
+
+    int MAX_REORDERING = 3;
+    tint TARGET = 25*TINT_MSEC;
+    float GAIN = 1.0/TARGET;
+    int seq_off = 0;
+    float cwnd = 1;
+    tint DELAY_BIN = TINT_SEC*30;
+    tint min_delay = TINT_NEVER;
+    tint rtt_avg = TINT_NEVER>>4, dev_avg = TINT_NEVER>>4;
+    tint last_bin_time = 0;
+    tint last_drop_time = 0;
+    int delay_bin = 0;
+    deque<tint> history, delay_history;
+    tint min_delay_bins[4] = {TINT_NEVER,TINT_NEVER,
+        TINT_NEVER,TINT_NEVER};
+    tint cur_delays[4] = {TINT_NEVER,TINT_NEVER,
+        TINT_NEVER,TINT_NEVER};
+    tint last_sec = 0;
+    int sec_ackd = 0;
+
+    evutil_socket_t send_sock = Datagram::Bind(10001); // bind sending socket
+    evutil_socket_t ack_sock = Datagram::Bind(10002);  // bind receiving socket
+    struct sockaddr_in send_to, ack_to;
+    send_to.sin_family = AF_INET;
+    send_to.sin_port = htons(10002);
+    send_to.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    ack_to.sin_family = AF_INET;
+    ack_to.sin_port = htons(10001);
+    ack_to.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    uint8_t* garbage = (uint8_t*) malloc(1024);
+    evutil_socket_t socks[2] = {send_sock,ack_sock};
+    evutil_socket_t sock2read;
+    tint wait_time = 100*TINT_MSEC;
+
+    while (sock2read = Datagram::Wait(2,socks,wait_time)) {
+        tint now = Datagram::Time();
+        if (sock2read==ack_sock) {
+            Datagram data(ack_sock); // send an acknowledgement
+            data.Recv();
+            int seq = data.Pull32();
+            Datagram ack(ack_sock,ack_to);
+            ack.Push32(seq);
+            ack.Push64(now);
+            if (4+8!=ack.Send())
+                fprintf(stderr,"short write\n");
+            fprintf(stderr,"%lli rcvd%i\n",now/TINT_SEC,seq);
+            //cc->OnDataRecv(bin64_t(0,seq));
+            // TODO: peer cwnd !!!
+            continue;
+        }
+        if (sock2read==send_sock) {        // process an acknowledgement
+            Datagram ack(send_sock);
+            ack.Recv();
+            int seq = ack.Pull32();
+            tint arrival_time = ack.Pull64();
+            seq -= seq_off;
+            if (seq<0)
+                continue;
+            if (seq>=history.size())
+                continue;
+            if (history[seq]==0)
+                continue;
+            tint send_time = history[seq];
+            history[seq] = 0;
+            if (seq>MAX_REORDERING*2) { //loss
+                if (last_drop_time<now-rtt_avg) {
+                    cwnd /= 2;
+                    last_drop_time = now;
+                }
+                fprintf(stderr,"got %i. LOSS, cwnd drop: %f\n",seq,cwnd);
+                for(int i=0; i<MAX_REORDERING*2 && history.size(); i++) {
+                    seq_off++;
+                    history.pop_front();
+                }
+                continue;
+            }
+            tint delay = arrival_time - send_time;
+            if (seq==0 && seq_off==0) { // FIXME
+                rtt_avg = now - send_time;
+                dev_avg = rtt_avg;
+            }
+            if (send_time/DELAY_BIN != last_bin_time) {
+                last_bin_time = send_time/DELAY_BIN;
+                delay_bin = (delay_bin+1) % 4;
+                min_delay_bins[delay_bin] = TINT_NEVER;
+                min_delay = TINT_NEVER;
+                for(int i=0;i<4;i++)
+                    if (min_delay_bins[i]<min_delay)
+                        min_delay = min_delay_bins[i];
+            }
+            if (min_delay_bins[delay_bin] > delay)
+                min_delay_bins[delay_bin] = delay;
+            if (delay < min_delay)
+                min_delay = delay;
+            cur_delays[(seq_off+seq)%4] = delay;
+            tint current_delay = TINT_NEVER;
+            for(int i=0; i<4; i++)
+                if (current_delay > cur_delays[i])
+                    current_delay = cur_delays[i];  // FIXME avg
+            tint queueing_delay = current_delay - min_delay;
+            // adjust cwnd
+            tint off_target = TARGET - queueing_delay;
+            //cerr<<"\t"<<cwnd<<"+="<<GAIN<<"*"<<off_target<<"/"<<cwnd<<endl;
+            cwnd += GAIN * off_target / cwnd;
+            fprintf(stderr,"ackd cwnd%f cur%lli min%lli seq%i off%i\n",
+                    cwnd,current_delay,min_delay,seq_off+seq,seq);
+
+            if (now/TINT_SEC!=last_sec/TINT_SEC) {
+                fprintf(stderr,"%i KB/sec\n",sec_ackd);
+                sec_ackd = 0;
+                last_sec = now; // FIXME
+            } else
+                sec_ackd++;
+
+        } // if
+        while (history[0]==0 && history.size()) {
+            history.pop_front();
+            seq_off++;
+        }
+        if (history.size() && history[0]<now-rtt_avg-5*dev_avg) {
+            if (last_drop_time<now-rtt_avg) {
+                cwnd /= 2;
+                last_drop_time = now;
+            }
+            fprintf(stderr,"TIMEOUT LOSS, cwnd drop: %f\n",cwnd);
+            seq_off++;
+            history.pop_front();
+        }
+        // fill cwnd
+        if (history.size()<cwnd) {
+            int sendseq = history.size() + seq_off;
+            Datagram send(send_sock,send_to);
+            send.Push32(sendseq);
+            send.Push(garbage,1024);
+            history.push_back(now);
+            fprintf(stderr,"sent%i\n",sendseq);
+            if (4+1024!=send.Send())
+                fprintf(stderr,"short data write\n");
+        }
+        if (cwnd<1)
+            cwnd = 1;
+        if (history.size()<cwnd)
+            wait_time = rtt_avg/cwnd;
+        else
+            wait_time = 100*TINT_MSEC;
+    } // while
+}
+
+int main (int argc, char** argv) {
+    printf("Warning: use the script to set up dummynet!\n");
+
+    swift::LibraryInit();
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+
+}
diff --git a/src/libswift_udp/tests/ledbattest2.cpp b/src/libswift_udp/tests/ledbattest2.cpp
new file mode 100644 (file)
index 0000000..6f5ef3b
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *  ledbattest2.cpp
+ *
+ *  BROKEN: Arno: must be rewritten to libevent
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+       #include "compat/stdint.h"
+    #include <winsock2.h>
+#else
+   #include <sys/socket.h>
+   #include <netinet/in.h>
+   #include <arpa/inet.h>
+#endif
+#include <vector>
+#include <deque>
+#include "datagram.h"
+#include "swift.h"
+#include <gtest/gtest.h>
+
+using namespace swift;
+using namespace std;
+
+/**
+  TODO
+  * losses
+  * smooth rate
+  * seq 12345 stop
+  * busy pipe => negative cwnd
+*/
+
+unsigned long dest_addr;
+int send_port = 10001;
+int ack_port = 10002;
+
+TEST(Datagram,LedbatTest) {
+
+    int MAX_REORDERING = 3;
+    tint TARGET = 25*TINT_MSEC;
+    float GAIN = 1.0/TARGET;
+    int seq_off = 0;
+    float cwnd = 1;
+    tint DELAY_BIN = TINT_SEC*30;
+    tint min_delay = TINT_NEVER;
+    tint rtt_avg = TINT_NEVER>>4, dev_avg = TINT_NEVER>>4;
+    tint last_bin_time = 0;
+    tint last_drop_time = 0;
+    int delay_bin = 0;
+    deque<tint> history, delay_history;
+    tint min_delay_bins[4] = {TINT_NEVER,TINT_NEVER,
+        TINT_NEVER,TINT_NEVER};
+    tint cur_delays[4] = {TINT_NEVER,TINT_NEVER,
+        TINT_NEVER,TINT_NEVER};
+    tint last_sec = 0;
+    int sec_ackd = 0;
+
+    // bind sending socket
+    evutil_socket_t send_sock = Datagram::Bind(Address(INADDR_ANY,send_port));
+    // bind receiving socket
+    evutil_socket_t ack_sock = Datagram::Bind(Address(INADDR_ANY,ack_port));
+    struct sockaddr_in send_to, ack_to;
+    memset(&send_to, 0, sizeof(struct sockaddr_in));
+    memset(&ack_to, 0, sizeof(struct sockaddr_in));
+    send_to.sin_family = AF_INET;
+    send_to.sin_port = htons(ack_port);
+    send_to.sin_addr.s_addr = dest_addr;
+    ack_to.sin_family = AF_INET;
+    ack_to.sin_port = htons(send_port);
+    ack_to.sin_addr.s_addr = dest_addr;
+    uint8_t* garbage = (uint8_t*) malloc(1024);
+    evutil_socket_t socks[2] = {send_sock,ack_sock};
+    evutil_socket_t sock2read;
+    tint wait_time = 100*TINT_MSEC;
+
+    while (sock2read = Datagram::Wait(2,socks,wait_time)) {
+        tint now = Datagram::Time();
+        if (sock2read==ack_sock) {
+            Datagram data(ack_sock); // send an acknowledgement
+            data.Recv();
+            int seq = data.Pull32();
+            Datagram ack(ack_sock,ack_to);
+            ack.Push32(seq);
+            ack.Push64(now);
+            if (4+8!=ack.Send())
+                fprintf(stderr,"short write\n");
+            fprintf(stderr,"%lli rcvd%i\n",now/TINT_SEC,seq);
+            // TODO: peer cwnd !!!
+            continue;
+        }
+        if (sock2read==send_sock) {        // process an acknowledgement
+            Datagram ack(send_sock);
+            ack.Recv();
+            int seq = ack.Pull32();
+            tint arrival_time = ack.Pull64();
+            seq -= seq_off;
+            if (seq<0)
+                continue;
+            if (seq>=history.size())
+                continue;
+            if (history[seq]==0)
+                continue;
+            tint send_time = history[seq];
+            history[seq] = 0;
+            if (seq>MAX_REORDERING*2) { //loss
+                if (last_drop_time<now-rtt_avg) {
+                    cwnd /= 2;
+                    last_drop_time = now;
+                }
+                fprintf(stderr,"got %i. LOSS, cwnd drop: %f\n",seq,cwnd);
+                for(int i=0; i<MAX_REORDERING*2 && history.size(); i++) {
+                    seq_off++;
+                    history.pop_front();
+                }
+                continue;
+            }
+            tint delay = arrival_time - send_time;
+            if (seq==0 && seq_off==0) { // FIXME
+                rtt_avg = now - send_time;
+                dev_avg = rtt_avg;
+            }
+            if (send_time/DELAY_BIN != last_bin_time) {
+                last_bin_time = send_time/DELAY_BIN;
+                delay_bin = (delay_bin+1) % 4;
+                min_delay_bins[delay_bin] = TINT_NEVER;
+                min_delay = TINT_NEVER;
+                for(int i=0;i<4;i++)
+                    if (min_delay_bins[i]<min_delay)
+                        min_delay = min_delay_bins[i];
+            }
+            if (min_delay_bins[delay_bin] > delay)
+                min_delay_bins[delay_bin] = delay;
+            if (delay < min_delay)
+                min_delay = delay;
+            cur_delays[(seq_off+seq)%4] = delay;
+            tint current_delay = TINT_NEVER;
+            for(int i=0; i<4; i++)
+                if (current_delay > cur_delays[i])
+                    current_delay = cur_delays[i];  // FIXME avg
+            tint queueing_delay = current_delay - min_delay;
+            // adjust cwnd
+            tint off_target = TARGET - queueing_delay;
+            //cerr<<"\t"<<cwnd<<"+="<<GAIN<<"*"<<off_target<<"/"<<cwnd<<endl;
+            cwnd += GAIN * off_target / cwnd;
+            fprintf(stderr,"ackd cwnd%f cur%lli min%lli seq%i off%i\n",
+                    cwnd,current_delay,min_delay,seq_off+seq,seq);
+
+            if (now/TINT_SEC!=last_sec/TINT_SEC) {
+                fprintf(stderr,"%i KB/sec\n",sec_ackd);
+                sec_ackd = 0;
+                last_sec = now; // FIXME
+            } else
+                sec_ackd++;
+
+        } // if
+        while (history[0]==0 && history.size()) {
+            history.pop_front();
+            seq_off++;
+        }
+        if (history.size() && history[0]<now-rtt_avg-5*dev_avg) {
+            if (last_drop_time<now-rtt_avg) {
+                cwnd /= 2;
+                last_drop_time = now;
+            }
+            fprintf(stderr,"TIMEOUT LOSS, cwnd drop: %f\n",cwnd);
+            seq_off++;
+            history.pop_front();
+        }
+        // fill cwnd
+        if (history.size()<cwnd) {
+            int sendseq = history.size() + seq_off;
+            Datagram send(send_sock,send_to);
+            send.Push32(sendseq);
+            send.Push(garbage,1024);
+            history.push_back(now);
+            fprintf(stderr,"sent%i\n",sendseq);
+            if (4+1024!=send.Send())
+                fprintf(stderr,"short data write\n");
+        }
+        if (cwnd<1)
+            cwnd = 1;
+        if (history.size()<cwnd)
+            wait_time = rtt_avg/cwnd;
+        else
+            wait_time = 100*TINT_MSEC;
+    } // while
+}
+
+int main (int argc, char** argv) {
+
+    int opt;
+    swift::LibraryInit();
+    printf("Warning: use the script to set up dummynet!\n");
+    testing::InitGoogleTest(&argc, argv);
+    dest_addr = htonl(INADDR_LOOPBACK);
+    while ((opt = getopt(argc, argv, "a:d:s:h")) != -1)
+        switch (opt) {
+        case 'd':
+            if (!inet_aton(optarg, (struct in_addr *)&dest_addr)) {
+                fprintf(stderr, "inet_aton failed for addr: %s\n", optarg);
+                return -1;
+            }
+            break;
+        case 'a':
+            ack_port = (int)strtol(optarg, NULL, 10);
+            break;
+        case 's':
+            send_port = (int)strtol(optarg, NULL, 10);
+            break;
+        case 'h':
+        default:
+            printf("\nledbattest2 usage:\n"
+                   "  -d Destination IP-address (default: 127.0.0.1)\n"
+                   "  -s Send port (default: 10001)\n"
+                   "  -a Ack port (default: 10002)\n");
+            break;
+        }
+    return RUN_ALL_TESTS();
+}
diff --git a/src/libswift_udp/tests/pex_test.sh b/src/libswift_udp/tests/pex_test.sh
new file mode 100755 (executable)
index 0000000..ff65406
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+EXEC=build/Debug/
+PEERCOUNT=2
+STORE=_pextest 
+TOKILL=
+rm -rf $STORE
+mkdir $STORE
+
+#$EXEC/seeder doc/sofi.jpg 7001
+
+for i in `seq 1 $PEERCOUNT`; do
+    ( $EXEC/leecher 282a863d5567695161721686a59f0c667250a35d \
+        $STORE/sofi$i.jpg 7001 710$i > $STORE/leecher$i.log ) &
+    TOKILL="$TOKILL $!"
+    sleep 4;
+done
+
+sleep 10
+
+for p in $TOKILL; do
+    kill -9 $p
+done
+
+for i in `seq 1 $PEERCOUNT`; do
+    cat $STORE/leecher$i.log | grep sent | awk '{print $5}' | \
+        sort | uniq -c > $STORE/peers$i.txt
+    peers=`wc -l < $STORE/peers$i.txt`
+    if [ $peers -ne $PEERCOUNT ]; then
+        echo Peer $i has $peers peers
+    fi
+done
diff --git a/src/libswift_udp/tests/test.bat b/src/libswift_udp/tests/test.bat
new file mode 100644 (file)
index 0000000..16b1de3
--- /dev/null
@@ -0,0 +1,7 @@
+bin64test.exe\r
+binstest2.exe\r
+connecttest.exe\r
+dgramtest.exe\r
+freemap.exe\r
+hashtest.exe\r
+transfertest.exe\r
diff --git a/src/libswift_udp/tests/test_file0.dat b/src/libswift_udp/tests/test_file0.dat
new file mode 100644 (file)
index 0000000..f7843d1
Binary files /dev/null and b/src/libswift_udp/tests/test_file0.dat differ
diff --git a/src/libswift_udp/tests/transfertest.cpp b/src/libswift_udp/tests/transfertest.cpp
new file mode 100644 (file)
index 0000000..9938bc3
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *  transfertest.cpp
+ *  swift
+ *
+ *  Created by Victor Grishchenko on 10/7/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+//#include <gtest/gtest.h>
+//#include <glog/logging.h>
+#include "swift.h"
+#include "compat.h"
+#include <gtest/gtest.h>
+
+using namespace swift;
+
+const char* BTF = "test_file";
+
+Sha1Hash A,B,C,D,E,AB,CD,ABCD,E0,E000,ABCDE000,ROOT;
+
+
+TEST(TransferTest,TBHeap) {
+    tbheap tbh;
+    ASSERT_TRUE(tbh.is_empty());
+    tbh.push(tintbin(3,bin_t::NONE));
+    tbh.push(tintbin(1,bin_t::NONE));
+    ASSERT_EQ(2,tbh.size());
+    tbh.push(tintbin(2,bin_t::ALL));
+    ASSERT_EQ(1,tbh.pop().time);
+    ASSERT_EQ(bin_t::ALL,tbh.peek().bin);
+    ASSERT_EQ(2,tbh.pop().time);
+    ASSERT_EQ(3,tbh.pop().time);
+}
+
+
+TEST(TransferTest,TransferFile) {
+
+    AB = Sha1Hash(A,B);
+    CD = Sha1Hash(C,D);
+    ABCD = Sha1Hash(AB,CD);
+    E0 = Sha1Hash(E,Sha1Hash::ZERO);
+    E000 = Sha1Hash(E0,Sha1Hash::ZERO);
+    ABCDE000 = Sha1Hash(ABCD,E000);
+    ROOT = ABCDE000;
+    //for (bin_t pos(3,0); !pos.is_all(); pos=pos.parent()) {
+    //    ROOT = Sha1Hash(ROOT,Sha1Hash::ZERO);
+        //printf("m %lli %s\n",(uint64_t)pos.parent(),ROOT.hex().c_str());
+    //}
+    
+    // now, submit a new file
+    
+    FileTransfer* seed_transfer = new FileTransfer(BTF);
+    HashTree* seed = & seed_transfer->file();
+    EXPECT_TRUE(A==seed->hash(bin_t(0,0)));
+    EXPECT_TRUE(E==seed->hash(bin_t(0,4)));
+    EXPECT_TRUE(ABCD==seed->hash(bin_t(2,0)));
+    EXPECT_TRUE(ROOT==seed->root_hash());
+    EXPECT_TRUE(ABCD==seed->peak_hash(0));
+    EXPECT_TRUE(E==seed->peak_hash(1));
+    EXPECT_TRUE(ROOT==seed->root_hash());
+    EXPECT_EQ(4100,seed->size());
+    EXPECT_EQ(5,seed->size_in_chunks());
+    EXPECT_EQ(4100,seed->complete());
+    EXPECT_EQ(4100,seed->seq_complete());
+    EXPECT_EQ(bin_t(2,0),seed->peak(0));
+
+    // retrieve it
+    unlink("copy");
+    FileTransfer* leech_transfer = new FileTransfer("copy",seed->root_hash());
+    HashTree* leech = & leech_transfer->file();
+    leech_transfer->picker().Randomize(0);
+    // transfer peak hashes
+    for(int i=0; i<seed->peak_count(); i++)
+        leech->OfferHash(seed->peak(i),seed->peak_hash(i));
+    ASSERT_EQ(5<<10,leech->size());
+    ASSERT_EQ(5,leech->size_in_chunks());
+    ASSERT_EQ(0,leech->complete());
+    EXPECT_EQ(bin_t(2,0),leech->peak(0));
+    // transfer data and hashes
+    //           ABCD            E000
+    //     AB         CD       E0    0
+    //  AAAA BBBB  CCCC DDDD  E  0  0  0
+    // calculated leech->OfferHash(bin64_t(1,0), seed->hashes[bin64_t(1,0)]);
+    leech->OfferHash(bin_t(1,1), seed->hash(bin_t(1,1)));
+    for (int i=0; i<5; i++) {
+        if (i==2) { // now: stop, save, start
+            delete leech_transfer;
+            leech_transfer = new FileTransfer("copy",seed->root_hash(),false);
+            leech = & leech_transfer->file();
+            leech_transfer->picker().Randomize(0);
+            EXPECT_EQ(2,leech->chunks_complete());
+            EXPECT_EQ(bin_t(2,0),leech->peak(0));
+        }
+        bin_t next = leech_transfer->picker().Pick(seed->ack_out(),1,TINT_NEVER);
+        ASSERT_NE(bin_t::NONE,next);
+        ASSERT_TRUE(next.base_offset()<5);
+        uint8_t buf[1024];         //size_t len = seed->storer->ReadData(next,&buf);
+        size_t len = pread(seed->file_descriptor(),buf,1024,next.base_offset()<<10);
+        bin_t sibling = next.sibling();
+        if (sibling.base_offset()<seed->size_in_chunks())
+            leech->OfferHash(sibling, seed->hash(sibling));
+        uint8_t memo = *buf;
+        *buf = 'z';
+        EXPECT_FALSE(leech->OfferData(next, (char*)buf, len));
+        fprintf(stderr,"offer of bad data was refused, OK\n");
+        *buf = memo;
+        EXPECT_TRUE(leech->OfferData(next, (char*)buf, len));
+    }
+    EXPECT_EQ(4100,leech->size());
+    EXPECT_EQ(5,leech->size_in_chunks());
+    EXPECT_EQ(4100,leech->complete());
+    EXPECT_EQ(4100,leech->seq_complete());
+
+}
+/*
+ FIXME
+ - always rehashes (even fresh files)
+ */
+
+int main (int argc, char** argv) {
+
+    unlink("test_file");
+    unlink("copy");
+    unlink("test_file.mhash");
+    unlink("copy.mhash");
+
+       int f = open(BTF,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       if (f < 0)
+       {
+               eprintf("Error opening %s\n",BTF);
+               return -1;
+       }
+    uint8_t buf[1024];
+    memset(buf,'A',1024);
+    A = Sha1Hash(buf,1024);
+    write(f,buf,1024);
+    memset(buf,'B',1024);
+    B = Sha1Hash(buf,1024);
+    write(f,buf,1024);
+    memset(buf,'C',1024);
+    C = Sha1Hash(buf,1024);
+    write(f,buf,1024);
+    memset(buf,'D',1024);
+    D = Sha1Hash(buf,1024);
+    write(f,buf,1024);
+    memset(buf,'E',4);
+    E = Sha1Hash(buf,4);
+    write(f,buf,4);
+       close(f);
+
+       testing::InitGoogleTest(&argc, argv);
+       int ret = RUN_ALL_TESTS();
+
+    return ret;
+}
diff --git a/src/libswift_udp/transfer.cpp b/src/libswift_udp/transfer.cpp
new file mode 100644 (file)
index 0000000..ffc8281
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ *  transfer.cpp
+ *  some transfer-scope code
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
+ *
+ */
+#include <errno.h>
+#include <string>
+#include <sstream>
+#include <cfloat>
+#include "swift.h"
+
+#include "ext/seq_picker.cpp" // FIXME FIXME FIXME FIXME
+#include "ext/vod_picker.cpp"
+
+using namespace swift;
+
+std::vector<FileTransfer*> FileTransfer::files(20);
+
+#define BINHASHSIZE (sizeof(bin64_t)+sizeof(Sha1Hash))
+
+
+#define TRACKER_RETRY_INTERVAL_START   (5*TINT_SEC)
+#define TRACKER_RETRY_INTERVAL_EXP             1.1                             // exponent used to increase INTERVAL_START
+#define TRACKER_RETRY_INTERVAL_MAX             (1800*TINT_SEC) // 30 minutes
+
+// FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
+
+FileTransfer::FileTransfer (const char* filename, const Sha1Hash& _root_hash, bool force_check_diskvshash, bool check_netwvshash, uint32_t chunk_size) :
+    file_(filename,_root_hash,chunk_size,NULL,force_check_diskvshash,check_netwvshash), cb_installed(0), mychannels_(),
+    speedzerocount_(0), tracker_(), tracker_retry_interval_(TRACKER_RETRY_INTERVAL_START), tracker_retry_time_(NOW)
+{
+    if (files.size()<fd()+1)
+        files.resize(fd()+1);
+    files[fd()] = this;
+    if (ENABLE_VOD_PIECEPICKER) {
+       // Ric: init availability
+       availability_ = new Availability();
+       // Ric: TODO assign picker based on input params...
+       picker_ = new VodPiecePicker(this);
+    }
+    else
+       picker_ = new SeqPiecePicker(this);
+    picker_->Randomize(rand()&63);
+    init_time_ = Channel::Time();
+    cur_speed_[DDIR_UPLOAD] = MovingAverageSpeed();
+    cur_speed_[DDIR_DOWNLOAD] = MovingAverageSpeed();
+    max_speed_[DDIR_UPLOAD] = DBL_MAX;
+    max_speed_[DDIR_DOWNLOAD] = DBL_MAX;
+
+    // SAFECLOSE
+    evtimer_assign(&evclean_,Channel::evbase,&FileTransfer::LibeventCleanCallback,this);
+    evtimer_add(&evclean_,tint2tv(5*TINT_SEC));
+
+}
+
+
+// SAFECLOSE
+void FileTransfer::LibeventCleanCallback(int fd, short event, void *arg)
+{
+       // Arno, 2012-02-24: Why-oh-why, update NOW
+       Channel::Time();
+
+       FileTransfer *ft = (FileTransfer *)arg;
+       if (ft == NULL)
+               return;
+
+       // STL and MS and conditional delete from set not a happy place :-(
+       std::set<Channel *>     delset;
+       std::set<Channel *>::iterator iter;
+       bool hasestablishedpeers=false;
+       for (iter=ft->mychannels_.begin(); iter!=ft->mychannels_.end(); iter++)
+       {
+               Channel *c = *iter;
+               if (c != NULL) {
+                       if (c->IsScheduled4Close())
+                               delset.insert(c);
+
+                       if (c->is_established ()) {
+                               hasestablishedpeers = true;
+                               //fprintf(stderr,"%s peer %s\n", ft->file().root_hash().hex().c_str(), c->peer().str() );
+                       }
+               }
+       }
+       for (iter=delset.begin(); iter!=delset.end(); iter++)
+       {
+               Channel *c = *iter;
+               dprintf("%s #%u clean cb close\n",tintstr(),c->id());
+               c->Close();
+               ft->mychannels_.erase(c);
+               delete c;
+    }
+
+       // Arno, 2012-02-24: Check for liveliness.
+       ft->ReConnectToTrackerIfAllowed(hasestablishedpeers);
+
+       // Reschedule cleanup
+    evtimer_add(&(ft->evclean_),tint2tv(5*TINT_SEC));
+}
+
+
+void FileTransfer::ReConnectToTrackerIfAllowed(bool hasestablishedpeers)
+{
+       // If I'm not connected to any
+       // peers, try to contact the tracker again.
+       if (!hasestablishedpeers)
+       {
+               if (NOW > tracker_retry_time_)
+               {
+                       ConnectToTracker();
+
+                       tracker_retry_interval_ *= TRACKER_RETRY_INTERVAL_EXP;
+                       if (tracker_retry_interval_ > TRACKER_RETRY_INTERVAL_MAX)
+                               tracker_retry_interval_ = TRACKER_RETRY_INTERVAL_MAX;
+                       tracker_retry_time_ = NOW + tracker_retry_interval_;
+               }
+       }
+       else
+       {
+               tracker_retry_interval_ = TRACKER_RETRY_INTERVAL_START;
+               tracker_retry_time_ = NOW + tracker_retry_interval_;
+       }
+}
+
+
+void FileTransfer::ConnectToTracker()
+{
+       Channel *c = NULL;
+    if (tracker_ != Address())
+       c = new Channel(this,INVALID_SOCKET,tracker_);
+    else if (Channel::tracker!=Address())
+       c = new Channel(this);
+}
+
+
+Channel * FileTransfer::FindChannel(const Address &addr, Channel *notc)
+{
+       std::set<Channel *>::iterator iter;
+       for (iter=mychannels_.begin(); iter!=mychannels_.end(); iter++)
+       {
+               Channel *c = *iter;
+               if (c != NULL) {
+                       if (c != notc && (c->peer() == addr || c->recv_peer() == addr)) {
+                               return c;
+                       }
+               }
+       }
+       return NULL;
+}
+
+
+
+
+void    Channel::CloseTransfer (FileTransfer* trans) {
+    for(int i=0; i<Channel::channels.size(); i++)
+        if (Channel::channels[i] && Channel::channels[i]->transfer_==trans)
+        {
+               //fprintf(stderr,"Channel::CloseTransfer: delete #%i\n", Channel::channels[i]->id());
+               Channel::channels[i]->Close(); // ARNO
+            delete Channel::channels[i];
+        }
+}
+
+
+void swift::AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg) {
+
+       //fprintf(stderr,"swift::AddProgressCallback: transfer %i\n", transfer );
+
+    FileTransfer* trans = FileTransfer::file(transfer);
+    if (!trans)
+        return;
+
+    //fprintf(stderr,"swift::AddProgressCallback: ft obj %p %p\n", trans, cb );
+
+    trans->cb_agg[trans->cb_installed] = agg;
+    trans->callbacks[trans->cb_installed] = cb;
+    trans->cb_installed++;
+}
+
+
+void swift::ExternallyRetrieved (int transfer,bin_t piece) {
+    FileTransfer* trans = FileTransfer::file(transfer);
+    if (!trans)
+        return;
+    trans->ack_out().set(piece); // that easy
+}
+
+
+void swift::RemoveProgressCallback (int transfer, ProgressCallback cb) {
+
+       //fprintf(stderr,"swift::RemoveProgressCallback: transfer %i\n", transfer );
+
+    FileTransfer* trans = FileTransfer::file(transfer);
+    if (!trans)
+        return;
+
+    //fprintf(stderr,"swift::RemoveProgressCallback: transfer %i ft obj %p %p\n", transfer, trans, cb );
+
+    for(int i=0; i<trans->cb_installed; i++)
+        if (trans->callbacks[i]==cb)
+            trans->callbacks[i]=trans->callbacks[--trans->cb_installed];
+
+    for(int i=0; i<trans->cb_installed; i++)
+    {
+       fprintf(stderr,"swift::RemoveProgressCallback: transfer %i remain %p\n", transfer, trans->callbacks[i] );
+    }
+}
+
+
+FileTransfer::~FileTransfer ()
+{
+    Channel::CloseTransfer(this);
+    files[fd()] = NULL;
+    delete picker_;
+    delete availability_;
+  
+    // Arno, 2012-02-06: Cancel cleanup timer, otherwise chaos!
+    evtimer_del(&evclean_);
+}
+
+
+FileTransfer* FileTransfer::Find (const Sha1Hash& root_hash) {
+    for(int i=0; i<files.size(); i++)
+        if (files[i] && files[i]->root_hash()==root_hash)
+            return files[i];
+    return NULL;
+}
+
+
+int swift:: Find (Sha1Hash hash) {
+    FileTransfer* t = FileTransfer::Find(hash);
+    if (t)
+        return t->fd();
+    return -1;
+}
+
+
+
+bool FileTransfer::OnPexIn (const Address& addr) {
+
+       //fprintf(stderr,"FileTransfer::OnPexIn: %s\n", addr.str() );
+       // Arno: this brings safety, but prevents private swift installations.
+       // TODO: detect public internet.
+       //if (addr.is_private())
+       //      return false;
+    // Gertjan fix: PEX redo
+    if (hs_in_.size()<SWIFT_MAX_CONNECTIONS)
+    {
+       // Arno, 2012-02-27: Check if already connected to this peer.
+               Channel *c = FindChannel(addr,NULL);
+               if (c == NULL)
+                       new Channel(this,Channel::default_socket(),addr);
+               else
+                       return false;
+    }
+    return true;
+}
+
+//Gertjan
+int FileTransfer::RandomChannel (int own_id) {
+    binqueue choose_from;
+    int i;
+
+    for (i = 0; i < (int) hs_in_.size(); i++) {
+        if (hs_in_[i].toUInt() == own_id)
+            continue;
+        Channel *c = Channel::channel(hs_in_[i].toUInt());
+        if (c == NULL || c->transfer().fd() != this->fd()) {
+            /* Channel was closed or is not associated with this FileTransfer (anymore). */
+            hs_in_[i] = hs_in_[0];
+            hs_in_.pop_front();
+            i--;
+            continue;
+        }
+        if (!c->is_established())
+            continue;
+        choose_from.push_back(hs_in_[i]);
+    }
+    if (choose_from.size() == 0)
+        return -1;
+
+    return choose_from[rand() % choose_from.size()].toUInt();
+}
+
+void           FileTransfer::OnRecvData(int n)
+{
+       // Got n ~ 32K
+       cur_speed_[DDIR_DOWNLOAD].AddPoint((uint64_t)n);
+}
+
+void           FileTransfer::OnSendData(int n)
+{
+       // Sent n ~ 1K
+       cur_speed_[DDIR_UPLOAD].AddPoint((uint64_t)n);
+}
+
+
+void           FileTransfer::OnSendNoData()
+{
+       // AddPoint(0) everytime we don't AddData gives bad speed measurement
+       // batch 32 such events into 1.
+       speedzerocount_++;
+       if (speedzerocount_ >= 32)
+       {
+               cur_speed_[DDIR_UPLOAD].AddPoint((uint64_t)0);
+               speedzerocount_ = 0;
+       }
+}
+
+
+double         FileTransfer::GetCurrentSpeed(data_direction_t ddir)
+{
+       return cur_speed_[ddir].GetSpeedNeutral();
+}
+
+
+void           FileTransfer::SetMaxSpeed(data_direction_t ddir, double m)
+{
+       max_speed_[ddir] = m;
+       // Arno, 2012-01-04: Be optimistic, forget history.
+       cur_speed_[ddir].Reset();
+}
+
+
+double         FileTransfer::GetMaxSpeed(data_direction_t ddir)
+{
+       return max_speed_[ddir];
+}
+
+
+uint32_t       FileTransfer::GetNumLeechers()
+{
+       uint32_t count = 0;
+       std::set<Channel *>::iterator iter;
+    for (iter=mychannels_.begin(); iter!=mychannels_.end(); iter++)
+    {
+           Channel *c = *iter;
+           if (c != NULL)
+                   if (!c->IsComplete()) // incomplete?
+                           count++;
+    }
+    return count;
+}
+
+
+uint32_t       FileTransfer::GetNumSeeders()
+{
+       uint32_t count = 0;
+       std::set<Channel *>::iterator iter;
+    for (iter=mychannels_.begin(); iter!=mychannels_.end(); iter++)
+    {
+           Channel *c = *iter;
+           if (c != NULL)
+                   if (c->IsComplete()) // complete?
+                           count++;
+    }
+    return count;
+}