From 46f3ca346fe499ba3e8f595fc5c2aa2b3e3c4d5f Mon Sep 17 00:00:00 2001 From: Adrian Bondrescu Date: Sun, 19 Aug 2012 21:40:53 +0300 Subject: [PATCH] Add files for swift over UDP. --- src/libswift_udp/BUGS | 65 + src/libswift_udp/ChangeLog | 7 + src/libswift_udp/LICENSE | 694 +++ src/libswift_udp/Makefile | 15 + src/libswift_udp/README | 35 + src/libswift_udp/SConstruct | 140 + src/libswift_udp/TODO | 76 + src/libswift_udp/availability.cpp | 242 + src/libswift_udp/availability.h | 97 + src/libswift_udp/avgspeed.cpp | 64 + src/libswift_udp/avgspeed.h | 36 + src/libswift_udp/bin.cpp | 169 + src/libswift_udp/bin.h | 782 +++ src/libswift_udp/bin_utils.h | 88 + src/libswift_udp/bingrep.cpp | 20 + src/libswift_udp/binheap.cpp | 71 + src/libswift_udp/binheap.h | 28 + src/libswift_udp/binmap.cpp | 2126 ++++++++ src/libswift_udp/binmap.h | 251 + src/libswift_udp/channel.cpp | 615 +++ src/libswift_udp/cmdgw.cpp | 758 +++ src/libswift_udp/compat.cpp | 233 + src/libswift_udp/compat.h | 167 + src/libswift_udp/do_tests.sh | 9 + src/libswift_udp/doc/apusapus.png | Bin 0 -> 4575 bytes src/libswift_udp/doc/binmaps-alenex.pdf | Bin 0 -> 223875 bytes src/libswift_udp/doc/cc-states.png | Bin 0 -> 124774 bytes .../draft-ietf-ppsp-peer-protocol-00.nroff | 2087 ++++++++ .../doc/draft-ietf-ppsp-peer-protocol-00.txt | 2240 ++++++++ src/libswift_udp/doc/index.html | 164 + .../doc/mfold-article/IEEEtran.bst | 2425 +++++++++ .../doc/mfold-article/IEEEtran.cls | 4702 +++++++++++++++++ src/libswift_udp/doc/mfold-article/swarm.png | Bin 0 -> 159796 bytes .../doc/mfold-article/swift-sigicn-jpp.bib | 30 + .../doc/mfold-article/swift-sigicn-jpp.pdf | Bin 0 -> 650579 bytes .../doc/mfold-article/swift-sigicn-jpp.tex | 255 + src/libswift_udp/doc/p2tp-lancaster.pdf | Bin 0 -> 1641056 bytes src/libswift_udp/doc/p2tp-uml-er.pdf | Bin 0 -> 85271 bytes src/libswift_udp/doc/state-diagram.pdf | Bin 0 -> 56302 bytes src/libswift_udp/doc/style.css | 68 + src/libswift_udp/doc/swift.css | 121 + .../doc/wireshark-dissector/AUTHORS | 2 + .../doc/wireshark-dissector/CMakeLists.txt | 66 + .../doc/wireshark-dissector/COPYING | 340 ++ .../doc/wireshark-dissector/ChangeLog | 0 .../doc/wireshark-dissector/Makefile | 828 +++ .../doc/wireshark-dissector/Makefile.am | 131 + .../doc/wireshark-dissector/Makefile.common | 36 + .../doc/wireshark-dissector/Makefile.in | 828 +++ .../doc/wireshark-dissector/Makefile.nmake | 103 + .../doc/wireshark-dissector/README | 10 + .../doc/wireshark-dissector/moduleinfo.h | 17 + .../doc/wireshark-dissector/moduleinfo.nmake | 28 + .../doc/wireshark-dissector/packet-swift.c | 383 ++ .../doc/wireshark-dissector/plugin.c | 31 + .../doc/wireshark-dissector/plugin.rc.in | 34 + src/libswift_udp/ext/seq_picker.cpp | 82 + src/libswift_udp/ext/simple_selector.cpp | 38 + src/libswift_udp/ext/vod_picker.cpp | 302 ++ src/libswift_udp/getopt.c | 122 + src/libswift_udp/getopt_long.c | 548 ++ src/libswift_udp/getopt_win.h | 110 + src/libswift_udp/hashtree.cpp | 589 +++ src/libswift_udp/hashtree.h | 165 + src/libswift_udp/httpgw.cpp | 509 ++ src/libswift_udp/mfold/bash_profile | 4 + src/libswift_udp/mfold/build.default.sh | 82 + src/libswift_udp/mfold/clean.default.sh | 10 + src/libswift_udp/mfold/cleanup.default.sh | 3 + .../cleanup.node300.das2.ewi.tudelft.nl.sh | 1 + src/libswift_udp/mfold/compile.default.sh | 20 + src/libswift_udp/mfold/das2.txt | 27 + src/libswift_udp/mfold/do-harvest.sh | 25 + src/libswift_udp/mfold/doall | 34 + src/libswift_udp/mfold/docmd | 39 + src/libswift_udp/mfold/dohrv | 117 + src/libswift_udp/mfold/dotop | 9 + src/libswift_udp/mfold/env.1mbit.sh | 4 + src/libswift_udp/mfold/env.default.sh | 19 + src/libswift_udp/mfold/env.lossy.sh | 4 + src/libswift_udp/mfold/env.messy.sh | 5 + src/libswift_udp/mfold/hosts.txt | 27 + src/libswift_udp/mfold/install.default.sh | 1 + src/libswift_udp/mfold/loggraphs | 99 + src/libswift_udp/mfold/logparse | 185 + src/libswift_udp/mfold/logreport | 35 + src/libswift_udp/mfold/net.aussie.sh | 3 + src/libswift_udp/mfold/net.lossy.sh | 3 + src/libswift_udp/mfold/net.messy.sh | 3 + src/libswift_udp/mfold/netclean.default.sh | 28 + .../mfold/netcleanroot.default.sh | 16 + src/libswift_udp/mfold/netem.default.sh | 103 + src/libswift_udp/mfold/netroot.default.sh | 36 + src/libswift_udp/mfold/ps.default.sh | 4 + src/libswift_udp/mfold/report.css | 35 + src/libswift_udp/mfold/run.default.sh | 13 + .../mfold/run.node300.das2.ewi.tudelft.nl.sh | 7 + src/libswift_udp/mfold/run.seeder.sh | 13 + src/libswift_udp/mfold/servers.txt | 43 + src/libswift_udp/mfold/ssh-config | 129 + src/libswift_udp/mfold/status.default.sh | 1 + src/libswift_udp/mfold/tcinfo.default.sh | 21 + src/libswift_udp/mfold/test.default.sh | 3 + src/libswift_udp/mfold/vtt.txt | 15 + src/libswift_udp/nat_test.cpp | 159 + src/libswift_udp/nat_test_server.c | 156 + src/libswift_udp/send_control.cpp | 231 + src/libswift_udp/sendrecv.cpp | 1089 ++++ src/libswift_udp/serialize.h | 23 + src/libswift_udp/sha1.cpp | 289 + src/libswift_udp/sha1.h | 23 + src/libswift_udp/statsgw.cpp | 284 + src/libswift_udp/swift.cpp | 598 +++ src/libswift_udp/swift.h | 781 +++ src/libswift_udp/tests/SConscript | 95 + src/libswift_udp/tests/bin64test.cpp | 81 + src/libswift_udp/tests/binstest2.cpp | 544 ++ src/libswift_udp/tests/binstest3.cpp | 696 +++ src/libswift_udp/tests/congctrltest.cpp | 111 + src/libswift_udp/tests/connecttest.cpp | 81 + src/libswift_udp/tests/dgramtest.cpp | 115 + src/libswift_udp/tests/freemap.cpp | 87 + src/libswift_udp/tests/hashtest.cpp | 123 + src/libswift_udp/tests/ledbattest.cpp | 184 + src/libswift_udp/tests/ledbattest2.cpp | 223 + src/libswift_udp/tests/pex_test.sh | 32 + src/libswift_udp/tests/test.bat | 7 + src/libswift_udp/tests/test_file0.dat | Bin 0 -> 65449 bytes src/libswift_udp/tests/transfertest.cpp | 155 + src/libswift_udp/transfer.cpp | 360 ++ 130 files changed, 31935 insertions(+) create mode 100644 src/libswift_udp/BUGS create mode 100644 src/libswift_udp/ChangeLog create mode 100644 src/libswift_udp/LICENSE create mode 100644 src/libswift_udp/Makefile create mode 100644 src/libswift_udp/README create mode 100644 src/libswift_udp/SConstruct create mode 100644 src/libswift_udp/TODO create mode 100644 src/libswift_udp/availability.cpp create mode 100644 src/libswift_udp/availability.h create mode 100644 src/libswift_udp/avgspeed.cpp create mode 100644 src/libswift_udp/avgspeed.h create mode 100644 src/libswift_udp/bin.cpp create mode 100644 src/libswift_udp/bin.h create mode 100644 src/libswift_udp/bin_utils.h create mode 100644 src/libswift_udp/bingrep.cpp create mode 100644 src/libswift_udp/binheap.cpp create mode 100644 src/libswift_udp/binheap.h create mode 100644 src/libswift_udp/binmap.cpp create mode 100644 src/libswift_udp/binmap.h create mode 100644 src/libswift_udp/channel.cpp create mode 100644 src/libswift_udp/cmdgw.cpp create mode 100644 src/libswift_udp/compat.cpp create mode 100644 src/libswift_udp/compat.h create mode 100755 src/libswift_udp/do_tests.sh create mode 100644 src/libswift_udp/doc/apusapus.png create mode 100644 src/libswift_udp/doc/binmaps-alenex.pdf create mode 100644 src/libswift_udp/doc/cc-states.png create mode 100644 src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.nroff create mode 100644 src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.txt create mode 100644 src/libswift_udp/doc/index.html create mode 100644 src/libswift_udp/doc/mfold-article/IEEEtran.bst create mode 100644 src/libswift_udp/doc/mfold-article/IEEEtran.cls create mode 100644 src/libswift_udp/doc/mfold-article/swarm.png create mode 100644 src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.bib create mode 100644 src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.pdf create mode 100644 src/libswift_udp/doc/mfold-article/swift-sigicn-jpp.tex create mode 100755 src/libswift_udp/doc/p2tp-lancaster.pdf create mode 100644 src/libswift_udp/doc/p2tp-uml-er.pdf create mode 100644 src/libswift_udp/doc/state-diagram.pdf create mode 100644 src/libswift_udp/doc/style.css create mode 100644 src/libswift_udp/doc/swift.css create mode 100644 src/libswift_udp/doc/wireshark-dissector/AUTHORS create mode 100644 src/libswift_udp/doc/wireshark-dissector/CMakeLists.txt create mode 100644 src/libswift_udp/doc/wireshark-dissector/COPYING create mode 100644 src/libswift_udp/doc/wireshark-dissector/ChangeLog create mode 100644 src/libswift_udp/doc/wireshark-dissector/Makefile create mode 100644 src/libswift_udp/doc/wireshark-dissector/Makefile.am create mode 100644 src/libswift_udp/doc/wireshark-dissector/Makefile.common create mode 100644 src/libswift_udp/doc/wireshark-dissector/Makefile.in create mode 100644 src/libswift_udp/doc/wireshark-dissector/Makefile.nmake create mode 100644 src/libswift_udp/doc/wireshark-dissector/README create mode 100644 src/libswift_udp/doc/wireshark-dissector/moduleinfo.h create mode 100644 src/libswift_udp/doc/wireshark-dissector/moduleinfo.nmake create mode 100644 src/libswift_udp/doc/wireshark-dissector/packet-swift.c create mode 100644 src/libswift_udp/doc/wireshark-dissector/plugin.c create mode 100644 src/libswift_udp/doc/wireshark-dissector/plugin.rc.in create mode 100644 src/libswift_udp/ext/seq_picker.cpp create mode 100644 src/libswift_udp/ext/simple_selector.cpp create mode 100644 src/libswift_udp/ext/vod_picker.cpp create mode 100644 src/libswift_udp/getopt.c create mode 100644 src/libswift_udp/getopt_long.c create mode 100644 src/libswift_udp/getopt_win.h create mode 100644 src/libswift_udp/hashtree.cpp create mode 100644 src/libswift_udp/hashtree.h create mode 100644 src/libswift_udp/httpgw.cpp create mode 100644 src/libswift_udp/mfold/bash_profile create mode 100644 src/libswift_udp/mfold/build.default.sh create mode 100644 src/libswift_udp/mfold/clean.default.sh create mode 100644 src/libswift_udp/mfold/cleanup.default.sh create mode 100644 src/libswift_udp/mfold/cleanup.node300.das2.ewi.tudelft.nl.sh create mode 100644 src/libswift_udp/mfold/compile.default.sh create mode 100644 src/libswift_udp/mfold/das2.txt create mode 100644 src/libswift_udp/mfold/do-harvest.sh create mode 100755 src/libswift_udp/mfold/doall create mode 100755 src/libswift_udp/mfold/docmd create mode 100755 src/libswift_udp/mfold/dohrv create mode 100755 src/libswift_udp/mfold/dotop create mode 100644 src/libswift_udp/mfold/env.1mbit.sh create mode 100644 src/libswift_udp/mfold/env.default.sh create mode 100644 src/libswift_udp/mfold/env.lossy.sh create mode 100644 src/libswift_udp/mfold/env.messy.sh create mode 100644 src/libswift_udp/mfold/hosts.txt create mode 100644 src/libswift_udp/mfold/install.default.sh create mode 100755 src/libswift_udp/mfold/loggraphs create mode 100755 src/libswift_udp/mfold/logparse create mode 100755 src/libswift_udp/mfold/logreport create mode 100644 src/libswift_udp/mfold/net.aussie.sh create mode 100644 src/libswift_udp/mfold/net.lossy.sh create mode 100644 src/libswift_udp/mfold/net.messy.sh create mode 100644 src/libswift_udp/mfold/netclean.default.sh create mode 100644 src/libswift_udp/mfold/netcleanroot.default.sh create mode 100755 src/libswift_udp/mfold/netem.default.sh create mode 100644 src/libswift_udp/mfold/netroot.default.sh create mode 100644 src/libswift_udp/mfold/ps.default.sh create mode 100644 src/libswift_udp/mfold/report.css create mode 100644 src/libswift_udp/mfold/run.default.sh create mode 100644 src/libswift_udp/mfold/run.node300.das2.ewi.tudelft.nl.sh create mode 100644 src/libswift_udp/mfold/run.seeder.sh create mode 100644 src/libswift_udp/mfold/servers.txt create mode 100644 src/libswift_udp/mfold/ssh-config create mode 100644 src/libswift_udp/mfold/status.default.sh create mode 100644 src/libswift_udp/mfold/tcinfo.default.sh create mode 100644 src/libswift_udp/mfold/test.default.sh create mode 100644 src/libswift_udp/mfold/vtt.txt create mode 100644 src/libswift_udp/nat_test.cpp create mode 100644 src/libswift_udp/nat_test_server.c create mode 100644 src/libswift_udp/send_control.cpp create mode 100644 src/libswift_udp/sendrecv.cpp create mode 100644 src/libswift_udp/serialize.h create mode 100644 src/libswift_udp/sha1.cpp create mode 100644 src/libswift_udp/sha1.h create mode 100644 src/libswift_udp/statsgw.cpp create mode 100644 src/libswift_udp/swift.cpp create mode 100644 src/libswift_udp/swift.h create mode 100644 src/libswift_udp/tests/SConscript create mode 100644 src/libswift_udp/tests/bin64test.cpp create mode 100755 src/libswift_udp/tests/binstest2.cpp create mode 100644 src/libswift_udp/tests/binstest3.cpp create mode 100644 src/libswift_udp/tests/congctrltest.cpp create mode 100644 src/libswift_udp/tests/connecttest.cpp create mode 100644 src/libswift_udp/tests/dgramtest.cpp create mode 100755 src/libswift_udp/tests/freemap.cpp create mode 100644 src/libswift_udp/tests/hashtest.cpp create mode 100644 src/libswift_udp/tests/ledbattest.cpp create mode 100644 src/libswift_udp/tests/ledbattest2.cpp create mode 100755 src/libswift_udp/tests/pex_test.sh create mode 100644 src/libswift_udp/tests/test.bat create mode 100644 src/libswift_udp/tests/test_file0.dat create mode 100644 src/libswift_udp/tests/transfertest.cpp create mode 100644 src/libswift_udp/transfer.cpp diff --git a/src/libswift_udp/BUGS b/src/libswift_udp/BUGS new file mode 100644 index 0000000..887e828 --- /dev/null +++ b/src/libswift_udp/BUGS @@ -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 index 0000000..ad12850 --- /dev/null +++ b/src/libswift_udp/ChangeLog @@ -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 index 0000000..3b9c716 --- /dev/null +++ b/src/libswift_udp/LICENSE @@ -0,0 +1,694 @@ +------------------------------------------------------------------------------ + + swift content-delivery library. + + The research leading to this library has received funding from the European + Community's Seventh Framework Programme in the P2P-Next project under grant + agreement no 216217. + + All library modules are free software, unless stated otherwise; you can + redistribute them and/or modify them under the terms of the GNU Lesser + General Public License as published by the Free Software Foundation; in + particular, version 2.1 of the License. + + This library 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 + Lesser General Public License for more details. + + The following library modules are Copyright (c) 2008-2012, VTT Technical Research Centre of Finland; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Norut AS; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, DACC Systems AB; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Lancaster University; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Jožef Stefan Institute; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, First Oversi Ltd.; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, TECHNISCHE UNIVERSITEIT DELFT; All rights reserved: + All files and directories found in the directory containing this LICENSE file. + + The following library modules are Copyright (c) 2008-2012, STMicroelectronics S.r.l.; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Kungliga Tekniska Högskolan (The Royal Institute of Technology); All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Markenfilm GmbH & Co. KG; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Radiotelevizija Slovenija Javni Zavvod Ljubljana; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Kendra Foundation; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Universitaet Klagenfurt; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, AG Projects; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, The British Broadcasting Corporation; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Pioneer Digital Design Centre Limited; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, INSTITUT FUER RUNDFUNKTECHNIK GMBH; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Fabchannel BV; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, University Politehnica Bucharest; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, EBU-UER; All rights reserved: + + The following library modules are Copyright (c) 2008-2012, Università di Roma Sapienza; All rights reserved: + + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + VTT Technical Research Centre of Finland, + Tekniikankatu 1, + FIN-33710 Tampere, + Finland + + Norut AS, + Postboks 6434 + Forskningsparken, + 9294 Tromsø, + Norway + + DACC Systems AB + Glimmervägen 4, + SE18734, Täby, + Sweden + + Lancaster University, + University House, + Bailrigg, Lancaster, LA1 4YW + United Kingdom + + Jožef Stefan Institute, + Jamova cesta 39, + 1000 Ljubljana, + Slovenia + + First Oversi Ltd., + Rishon Lezion 1, + Petah Tikva 49723, + Israel + + TECHNISCHE UNIVERSITEIT DELFT, + Faculty of Electrical Engineering, Mathematics and Computer Science, + Mekelweg 4, + 2628 CD Delft, + The Netherlands + + STMicroelectronics S.r.l., + via C.Olivetti 2, + I-20041 Agrate Brianza, + Italy + + Kungliga Tekniska Högskolan (The Royal Institute of Technology), + KTH/ICT/ECS/TSLab + Electrum 229 + 164 40 Kista + Sweden + + Markenfilm GmbH & Co. KG, + Schulauer Moorweg 25, + 22880 Wedel, + Germany + + Radiotelevizija Slovenija Javni Zavvod Ljubljana, + Kolodvorska 2, + SI-1000 Ljubljana, + Slovenia + + + Kendra Foundation, + Meadow Barn, Holne, + Newton Abbot, Devon, TQ13 7SP, + United Kingdom + + + Universitaet Klagenfurt, + Universitaetstrasse 65-67, + 9020 Klagenfurt, + Austria + + AG Projects, + Dr. Leijdsstraat 92, + 2021RK Haarlem, + The Netherlands + + The British Broadcasting Corporation, + Broadcasting House, Portland Place, + London, W1A 1AA + United Kingdom + + Pioneer Digital Design Centre Limited, + Pioneer House, Hollybush Hill, Stoke Poges, + Slough, SL2 4QP + United Kingdom + + INSTITUT FUER RUNDFUNKTECHNIK GMBH + Floriansmuehlstrasse 60, + 80939 München, + Germany + + Fabchannel BV, + Kleine-Gartmanplantsoen 21, + 1017 RP Amsterdam, + The Netherlands + + University Politehnica Bucharest, + 313 Splaiul Independentei, + District 6, cod 060042, Bucharest, + Romania + + EBU-UER, + L'Ancienne Route 17A, 1218 + Grand Saconnex - Geneva, + Switzerland + + Università di Roma Sapienza + Dipartimento di Informatica e Sistemistica (DIS), + Via Ariosto 25, + 00185 Rome, + Italy + + +------------------------------------------------------------------------------ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + 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 + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +------------------------------------------------------------------------------- + diff --git a/src/libswift_udp/Makefile b/src/libswift_udp/Makefile new file mode 100644 index 0000000..2ac2044 --- /dev/null +++ b/src/libswift_udp/Makefile @@ -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 index 0000000..ddfd593 --- /dev/null +++ b/src/libswift_udp/README @@ -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 index 0000000..ceca61f --- /dev/null +++ b/src/libswift_udp/SConstruct @@ -0,0 +1,140 @@ +# Written by Victor Grishchenko, Arno Bakker +# see LICENSE.txt for license information +# +# Requirements: +# - scons: Cross-platform build system http://www.scons.org/ +# - libevent2: Event driven network I/O http://www.libevent.org/ +# * Install in \build\libevent-2.0.14-stable +# For debugging: +# - googletest: Google C++ Test Framework http://code.google.com/p/googletest/ +# * Install in \build\gtest-1.4.0 +# + + +import os +import re +import sys + +DEBUG = True + +TestDir='tests' + +target = 'swift' +source = [ 'bin.cpp', 'binmap.cpp','binheap.cpp', 'sha1.cpp','hashtree.cpp', + 'transfer.cpp', 'channel.cpp', 'sendrecv.cpp', 'send_control.cpp', + 'compat.cpp','avgspeed.cpp', 'availability.cpp'] + + +env = Environment() +if sys.platform == "win32": + #libevent2path = '\\build\\libevent-2.0.14-stable' + libevent2path = '\\build\\ttuki\\libevent-2.0.15-arno-http' + + # "MSVC works out of the box". Sure. + # Make sure scons finds cl.exe, etc. + env.Append ( ENV = { 'PATH' : os.environ['PATH'] } ) + + # Make sure scons finds std MSVC include files + if not 'INCLUDE' in os.environ: + print "swift: Please run scons in a Visual Studio Command Prompt" + sys.exit(-1) + + include = os.environ['INCLUDE'] + include += libevent2path+'\\include;' + include += libevent2path+'\\WIN32-Code;' + if DEBUG: + include += '\\build\\gtest-1.4.0\\include;' + + env.Append ( ENV = { 'INCLUDE' : include } ) + + if 'CXXPATH' in os.environ: + cxxpath = os.environ['CXXPATH'] + else: + cxxpath = "" + cxxpath += include + if DEBUG: + env.Append(CXXFLAGS="/Zi /MTd") + env.Append(LINKFLAGS="/DEBUG") + env.Append(CXXPATH=cxxpath) + env.Append(CPPPATH=cxxpath) + + # getopt for win32 + source += ['getopt.c','getopt_long.c'] + + # Set libs to link to + # Advapi32.lib for CryptGenRandom in evutil_rand.obj + libs = ['ws2_32','libevent','Advapi32'] + if DEBUG: + libs += ['gtestd'] + + # Update lib search path + libpath = os.environ['LIBPATH'] + libpath += libevent2path+';' + if DEBUG: + libpath += '\\build\\gtest-1.4.0\\msvc\\gtest\\Debug;' + + # Somehow linker can't find uuid.lib + libpath += 'C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib;' + + # TODO: Make the swift.exe a Windows program not a Console program + if not DEBUG: + env.Append(LINKFLAGS="/SUBSYSTEM:WINDOWS") + + APPSOURCE=['swift.cpp','httpgw.cpp','statsgw.cpp','cmdgw.cpp','getopt.c','getopt_long.c'] + +else: + libevent2path = '/arno/pkgs/libevent-2.0.15-arno-http' + + # Enable the user defining external includes + if 'CPPPATH' in os.environ: + cpppath = os.environ['CPPPATH'] + else: + cpppath = "" + print "To use external libs, set CPPPATH environment variable to list of colon-separated include dirs" + cpppath += libevent2path+'/include:' + env.Append(CPPPATH=".:"+cpppath) + #env.Append(LINKFLAGS="--static") + + #if DEBUG: + # env.Append(CXXFLAGS="-g") + + # Large-file support always + env.Append(CXXFLAGS="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE") + + # Set libs to link to + libs = ['stdc++','libevent','pthread'] + if 'LIBPATH' in os.environ: + libpath = os.environ['LIBPATH'] + else: + libpath = "" + print "To use external libs, set LIBPATH environment variable to list of colon-separated lib dirs" + libpath += libevent2path+'/lib:' + + linkflags = '-Wl,-rpath,'+libevent2path+'/lib' + env.Append(LINKFLAGS=linkflags); + + + APPSOURCE=['swift.cpp','httpgw.cpp','statsgw.cpp','cmdgw.cpp'] + +if DEBUG: + env.Append(CXXFLAGS="-DDEBUG") + +env.StaticLibrary ( + target='libswift', + source = source, + LIBS=libs, + LIBPATH=libpath ) + +env.Program( + target='swift', + source=APPSOURCE, + #CPPPATH=cpppath, + LIBS=[libs,'libswift'], + LIBPATH=libpath+':.') + +Export("env") +Export("libs") +Export("libpath") +Export("DEBUG") +# Arno: uncomment to build tests +SConscript('tests/SConscript') diff --git a/src/libswift_udp/TODO b/src/libswift_udp/TODO new file mode 100644 index 0000000..e1d8541 --- /dev/null +++ b/src/libswift_udp/TODO @@ -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 index 0000000..a17c208 --- /dev/null +++ b/src/libswift_udp/availability.cpp @@ -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; iis_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 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()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 index 0000000..2c7b31c --- /dev/null +++ b/src/libswift_udp/availability.h @@ -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 + +#ifndef AVAILABILITY_H +#define AVAILABILITY_H + +namespace swift { + +typedef std::vector< std::pair > 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 index 0000000..2bc8607 --- /dev/null +++ b/src/libswift_udp/avgspeed.cpp @@ -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 index 0000000..b7d6f37 --- /dev/null +++ b/src/libswift_udp/avgspeed.h @@ -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 index 0000000..1878f3e --- /dev/null +++ b/src/libswift_udp/bin.cpp @@ -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 + + +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 index 0000000..70a05c3 --- /dev/null +++ b/src/libswift_udp/bin.h @@ -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 + + +/** + * 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(layer) < 8 * sizeof(uint_t)) { + v_ = ((2 * offset + 1) << layer) - 1; + } else { + v_ = static_cast(-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(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(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 index 0000000..b6de28e --- /dev/null +++ b/src/libswift_udp/bin_utils.h @@ -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(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(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(v)); +} + + +/** + * Restore the bin from an uint64_t value + */ +inline bin_t bin_fromUInt64(uint64_t v) { + return bin_t(static_cast(v)); +} + + +#endif /*_bin_utils_h__*/ diff --git a/src/libswift_udp/bingrep.cpp b/src/libswift_udp/bingrep.cpp new file mode 100644 index 0000000..f5cd51c --- /dev/null +++ b/src/libswift_udp/bingrep.cpp @@ -0,0 +1,20 @@ +#include +#include +#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 index 0000000..58054d4 --- /dev/null +++ b/src/libswift_udp/binheap.cpp @@ -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 +#include + +#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; isize_) { + 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 index 0000000..eaba92a --- /dev/null +++ b/src/libswift_udp/binheap.h @@ -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 index 0000000..feee239 --- /dev/null +++ b/src/libswift_udp/binmap.cpp @@ -0,0 +1,2126 @@ +#include +#include +#include +#include +#include + +#include + + +#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(0); +const bitmap_t BITMAP_FILLED = static_cast(-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(0x00000001), static_cast(0x00000003), + static_cast(0x00000002), static_cast(0x0000000f), + static_cast(0x00000004), static_cast(0x0000000c), + static_cast(0x00000008), static_cast(0x000000ff), + static_cast(0x00000010), static_cast(0x00000030), + static_cast(0x00000020), static_cast(0x000000f0), + static_cast(0x00000040), static_cast(0x000000c0), + static_cast(0x00000080), static_cast(0x0000ffff), + static_cast(0x00000100), static_cast(0x00000300), + static_cast(0x00000200), static_cast(0x00000f00), + static_cast(0x00000400), static_cast(0x00000c00), + static_cast(0x00000800), static_cast(0x0000ff00), + static_cast(0x00001000), static_cast(0x00003000), + static_cast(0x00002000), static_cast(0x0000f000), + static_cast(0x00004000), static_cast(0x0000c000), + static_cast(0x00008000), static_cast(0xffffffff), + static_cast(0x00010000), static_cast(0x00030000), + static_cast(0x00020000), static_cast(0x000f0000), + static_cast(0x00040000), static_cast(0x000c0000), + static_cast(0x00080000), static_cast(0x00ff0000), + static_cast(0x00100000), static_cast(0x00300000), + static_cast(0x00200000), static_cast(0x00f00000), + static_cast(0x00400000), static_cast(0x00c00000), + static_cast(0x00800000), static_cast(0xffff0000), + static_cast(0x01000000), static_cast(0x03000000), + static_cast(0x02000000), static_cast(0x0f000000), + static_cast(0x04000000), static_cast(0x0c000000), + static_cast(0x08000000), static_cast(0xff000000), + static_cast(0x10000000), static_cast(0x30000000), + static_cast(0x20000000), static_cast(0xf0000000), + static_cast(0x40000000), static_cast(0xc0000000), + static_cast(0x80000000), /* special */ static_cast(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(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(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(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(-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(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(idx + 1); + } + + free_top_ = static_cast(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(total_size())); + printf("cells number: %u (of %u)\n", static_cast(allocated_cells_number_), static_cast(cells_number_)); + printf("root bin: %llu\n", static_cast(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 +#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 index 0000000..35f4435 --- /dev/null +++ b/src/libswift_udp/channel.cpp @@ -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 +#include "compat.h" +//#include +#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::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 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 "<= 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; ih_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()SetTracker(tracker); + ft->ConnectToTracker(); + + return ft->file().file_descriptor(); + } else { + if (ft) + delete ft; + return -1; + } +} + + +void swift::Close (int fd) { + if (fdAddPeer(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 index 0000000..841bd25 --- /dev/null +++ b/src/libswift_udp/cmdgw.cpp @@ -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 +#include +#include + +#include "swift.h" +#include "compat.h" +#include +#include +#include + + +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; icmdsock==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; itransfer); + 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::iterator iter; + std::set 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; iid = ++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 index 0000000..04e3f6f --- /dev/null +++ b/src/libswift_udp/compat.cpp @@ -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 +#include +#include +#ifdef _WIN32 +#include +#include +#include +#include +#include +#else +#include +#include +#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 tempPath(result + 1); + result = ::GetTempPath(static_cast(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(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 index 0000000..0604bb4 --- /dev/null +++ b/src/libswift_udp/compat.h @@ -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 +#endif + +#ifdef _WIN32 +#include +#include +#include +#include // for std::min/max +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include "getopt_win.h" +#else +#include +#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 + +#ifndef _WIN32 +#define INVALID_SOCKET -1 +#endif + +#ifndef LONG_MAX +#include +#define LONG_MAX numeric_limits::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 index 0000000..f02a6cc --- /dev/null +++ b/src/libswift_udp/do_tests.sh @@ -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 index 0000000000000000000000000000000000000000..77a6a26897a38112d7107244e9e6309583bc58f3 GIT binary patch literal 4575 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 Jvv#uK{2voAPaOaN literal 0 HcmV?d00001 diff --git a/src/libswift_udp/doc/cc-states.png b/src/libswift_udp/doc/cc-states.png new file mode 100644 index 0000000000000000000000000000000000000000..57b5d499cbe08ec87c34f77f588e91f1f96c634f GIT binary patch literal 124774 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

&&WbY_hYfUV_S2jZj|(}7M-|_ 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 z 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

qYZHW%!uu~SD;^i!#4gS?kzGNz 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%0A=OTav)nZwp(}8+W`o2cF~^>Dv&4 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 OFqaBMQ?XJW7WO|`hje!U literal 0 HcmV?d00001 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 index 0000000..8f6dd7d --- /dev/null +++ b/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.nroff @@ -0,0 +1,2087 @@ +.\" Auto generated Nroff by NroffEdit on April 12, 2010 +.pl 10.0i +.po 0 +.ll 7.2i +.lt 7.2i +.nr LL 7.2i +.nr LT 7.2i +.ds LF Grishchenko and Bakker +.ds RF FORMFEED[Page %] +.ds LH Internet-Draft +.ds RH December 19, 2011 +.ds CH swift +.ds CF Expires June 21, 2012 +.hy 0 +.nh +.ad l +.in 0 +.nf +.tl 'PPSP' 'A. Bakker' +.tl 'Internet-Draft' 'TU Delft' +.tl 'Intended status: Informational' +.tl 'Expires: June 21, 2012' 'December 19, 2011' + +.fi +.in 3 +.in 12 +.ti 8 +Peer-to-Peer Streaming Protocol (PPSP) \% + +.ti 0 +Abstract + +.in 3 +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. + + +.ti 0 +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 +http://www.ietf.org/shadow.html. + + +.nf +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. + +.\" \# TD4 -- Set TOC depth by altering this value (TD5 = depth 5) +.\" \# TOC -- Beginning of auto updated Table of Contents +.in 0 +Table of Contents + +.nf + 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 + 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 +.fi +.in 3 + +.\" \# ETC -- End of auto updated Table of Contents + + + +.ti 0 +1. Introduction + +.ti 0 +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 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]. + + +.ti 0 +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]. + +.ti 0 +1.3. Terminology + +.in 3 +message +.br +.in 8 +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. + +.in 3 +datagram +.br +.in 8 +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). + +.in 3 +content +.br +.in 8 +Either a live transmission, a pre-recorded multimedia asset, or a file. + +.in 3 +bin +.br +.in 8 +A number denoting a specific binary interval of the content (i.e., one +or more consecutive chunks). + +.in 3 +chunk +.br +.in 8 +The basic unit in which the content is divided. E.g. a block of N +kilobyte. + +.in 3 +hash +.br +.in 8 +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. + +.in 3 +root hash +.br +.in 8 +The root in a Merkle hash tree calculated recursively from the content. + +.in 3 +swarm +.br +.in 8 +A group of peers participating in the distribution of the same content. + +.in 3 +swarm ID +.br +.in 8 +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). + +.in 3 +tracker +.br +.in 8 +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. + +.in 3 +choking +.br +.in 8 +When a peer A is choking peer B it means that A is currently not +willing to accept requests for content from B. +.in 3 + + +.ti 0 +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). + + +.ti 0 +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. + +.ti 0 +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 +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. + + +.ti 0 +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. + + +.ti 0 +3. Messages + +.fi +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. + +.ti 0 +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). + + +.ti 0 +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". + +.ti 0 +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 + +.br +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: + + + + + 7 +.br + / \\ +.br + / \\ +.br + / \\ +.br + / \\ +.br + 3 11 +.br + / \\ / \\ +.br + / \\ / \\ +.br + / \\ / \\ +.br + 1 5 9 13 +.br + / \\ / \\ / \\ / \\ +.br + 0 2 4 6 8 10 12 14 +.br + +.fi +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". + + +.ti 0 +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]. + + +.ti 0 +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 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. + + +.ti 0 +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. + +.ti 0 +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. + + + + + 7 = root hash +.br + / \\ +.br + / \\ +.br + / \\ +.br + / \\ +.br + 3* 11 +.br + / \\ / \\ +.br + / \\ / \\ +.br + / \\ / \\ +.br + 1 5 9 13* = uncle hash +.br + / \\ / \\ / \\ / \\ +.br + 0 2 4 6 8 10* 12 14 +.br + + C0 C1 C2 C3 C4 C5 C6 E +.br + =chunk index ^^ = empty hash +.br + + +.ti 0 +3.5.2. Content Integrity Verification + +.fi +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. + +.ti 0 +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, 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. + +.ti 0 +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. + + +.ti 0 +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. + + + +.ti 0 +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. + +.fi +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. + + +.ti 0 +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. + + +.ti 0 +3.9. VERSION +.fi +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. + + +.ti 0 +3.10. Conveying Peer Capabilities +.fi +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. + + +.ti 0 +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: + +.nf +\&. +\&.. +1234567890ABCDEF1234567890ABCDEF12345678 readme.txt +01234567890ABCDEF1234567890ABCDEF1234567 big_file.dat + + + +.ti 0 +4. Automatic Detection of Content Size + +.fi +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. + +.ti 0 +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 +.br + / \\ +.br + / \\ +.br + / \\ +.br + / \\ +.br + 3* 11 +.br + / \\ / \\ +.br + / \\ / \\ +.br + / \\ / \\ +.br + 1 5 9* 13 +.br + / \\ / \\ / \\ / \\ +.br + 0 2 4 6 8 10 12* 14 +.br + + C0 C1 C2 C3 C4 C5 C6 E +.br + = 1018 bytes +.br + +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 for a file is therefore also at most logarithmic with its +size. + +.fi +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. + +.fi +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 + + +.ti 0 +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 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. + + + + + +.ti 0 +5. Live streaming + +.fi +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. + + + + + +.ti 0 +6. Transport Protocols and Encapsulation + +.ti 0 +6.1. UDP + +.ti 0 +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 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. + + +.ti 0 +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 +.br + DATA = 0x01 +.br + ACK = 0x02 +.br + HAVE = 0x03 +.br + HASH = 0x04 +.br + PEX_ADD = 0x05 +.br + PEX_REQ = 0x06 +.br + SIGNED_HASH = 0x07 +.br + HINT = 0x08 +.br + MSGTYPE_RCVD = 0x09 +.br + VERSION = 0x10 +.br + + +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. + +.ti 0 +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. + + +.ti 0 +6.1.4. HANDSHAKE and VERSION + +A channel is established with a handshake. To start a handshake, the initiating +peer needs to know: + +.nf +(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). +.fi + +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: +.nf + 00000000 10 01 + 04 7FFFFFFF 1234123412341234123412341234123412341234 + 00 00000011 +.fi +(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: +.nf + 00000011 10 01 + 00 00000022 03 00000003 +.fi +(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) + +.fi +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. + +On the wire: +.nf + 00 00000000 + + +.ti 0 +6.1.5. HAVE + +A HAVE message (type 0x03) states that the sending peer has the +complete specified bin and successfully checked its integrity: +.nf + 03 00000003 +(got/checked first four kilobytes of a file/stream) + + + +.ti 0 +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. +.nf + 02 00000002 12345678 +(got the second kilobyte of the file from you; my microsecond +timer was showing 0x12345678 at that moment) + + +.ti 0 +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) +.nf + 04 7FFFFFFF 1234123412341234123412341234123412341234 + + +.ti 0 +6.1.8. DATA + +.fi +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: +.nf + 01 00000000 48656c6c6f20776f726c6421 +(This message accommodates an entire file: "Hello world!") + + +.ti 0 +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: +.nf + 00000022 + +.ti 0 +6.1.10. Flow and Congestion Control + +.fi +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]. + + +.ti 0 +6.2. TCP + +.fi +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. + + +.ti 0 +6.3. RTP Profile for PPSP + +.fi +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 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. + + +.ti 0 +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. + +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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.in 0 + 0 1 2 3 +.br + 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 +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+ +.br + |V=2|P|X| CC |M| PT | sequence number | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | timestamp | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | synchronization source (SSRC) identifier | | +.br + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | +.br + | contributing source (CSRC) identifiers | | +.br + | .... | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | RTP extension (OPTIONAL) | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | payload ... | | +.br + | +-------------------------------+ | +.br + | | RTP padding | RTP pad count | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<+ +.br + ~ swift non-DATA messages (REQUIRED) ~ | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | length of swift messages (REQUIRED) | | +.br + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +.br + | +.br + Authenticated Portion ---+ +.br + + Figure: The format of an RTP-Swift packet. +.in 3 + +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. + + +.ti 0 +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 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. + +- 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. + + +- 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. + + +.ti 0 +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. + +.ti 0 +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 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 /. 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 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 +.br + HAVE = Accept-Ranges +.br + HASH = Content-Merkle +.br + PEX = e.g. extended Content-Location +.br + +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. + + +.ti 0 +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, +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. //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. + + + + +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 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. + + +.ti 0 +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. + + +.ti 0 +8. Extensibility + +.ti 0 +8.1. 32 bit vs 64 bit + +.nf +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: +.nf + 44 000000000000000E 01234567890ABCDEF1234567890ABCDEF1234567 + +.ti 0 +8.2. IPv6 + +.fi +IPv6 versions of PEX messages use the same 64+t shift as just mentioned. + + +.ti 0 +8.3. Congestion Control Algorithms + +.fi +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, 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. + + +.ti 0 +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. + + +.ti 0 +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). + + +.ti 0 +8.6. Different crypto/hashing schemes + +.fi +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. + + +.ti 0 +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 project is to create a generic content-centric +multiparty transport protocol to allow seamless, effortless data +dissemination on the Net. + + TABLE 1. Use cases. + +.br + | mirror-based peer-assisted peer-to-peer +.br +------+---------------------------------------------------- +.br +data | SunSITE CacheLogic VelociX BitTorrent +.br +VoD | YouTube Azureus(+seedboxes) SwarmPlayer +.br +live | Akamai Str. Octoshape, Joost PPlive +.br + +.fi +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. + + +.ti 0 +9.1. Design Goals + +.fi +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: + +.nf +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). + +.fi +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. + +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. + +.fi +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. + + +.ti 0 +9.2. Not TCP + +.fi +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, +.nf + 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 +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. + +.fi +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. + +.fi +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. + + + +.ti 0 +9.3. Generic Acknowledgments + +.nf +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. +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. + + + +.ti 0 +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. + +.fi +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. + + +.ti 0 +References + +.nf +.in 0 +[RFC2119] Key words for use in RFCs to Indicate Requirement Levels +[HTTP1MLN] Richard Jones. "A Million-user Comet Application with + 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. +[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 + + +.ti 0 +Authors' addresses + +.in 3 +A. Bakker +Technische Universiteit Delft +Department EWI/ST/PDS +Room HB 9.160 +Mekelweg 4 +2628CD Delft +The Netherlands + +Email: arno@cs.vu.nl + +.ce 0 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 index 0000000..22a3633 --- /dev/null +++ b/src/libswift_udp/doc/draft-ietf-ppsp-peer-protocol-00.txt @@ -0,0 +1,2240 @@ + + + + +PPSP A. Bakker +Internet-Draft TU Delft + +Expires: June 21, 2012 December 19, 2011 + + Peer-to-Peer Streaming Protocol (PPSP) + + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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 /. 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] + +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] + +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. //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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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] + +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 index 0000000..6207e1d --- /dev/null +++ b/src/libswift_udp/doc/index.html @@ -0,0 +1,164 @@ + + + + + + + swift: the multiparty transport protocol + + + + + + + +

+ + + + + + + +
+ +

Turn the Net into a single data cloud

+

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.

+

swift is a multiparty transport protocol. Its mission is to disseminate content among a swarm of peers. It might be understood as BitTorrent at the transport layer.

+

The TCP+BitTorrent stack consists of 60+90K lines of code (according to SLOCCount). With novel datastructures and refactoring we managed to implement swift in a mere 4,000 lines of cross-platform C++ code. The libswift library is licensed under LGPL; it runs on Mac OS X, Windows and a variety of Unices; it uses UDP with LEDBAT congestion control. Currently maximum throughput is 400Mbps, but we are working on that: our next target is 1 Gbps. The library is delivered as a part of P2P-Next, funded by the European Union Seventh Framework Programme.

+ +
+ + + +
+ +

Ideas

+ +

As wise people say, 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 conversation. Currently, however, the Internet is mostly used for disseminating content – and this mismatch definitely creates some problems.

+

The swift protocol is a content-centric multiparty transport protocol. Basically, it answers one and only one question: 'Here is a hash! Give me data for it!'. Ultimately swift aims at the abstraction of the Internet as a single big data cloud. 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 Merkle hash trees.

+

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:

+
    +
  • free bandwidth is wasted bandwidth
  • +
  • free storage is wasted storage.
  • +
+

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, non-intrusive congestion control and automatic disk space management.

+

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.

+ + + +

Design of the protocol

+ +

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 swift 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.

+ +

Atomic datagrams, not data stream

+

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.

+ +

Scale-independent unit system

+

To avoid a multilayered request/acknowledgement system, where every layer basically does the same but for bigger chunks of data – as is the case with BitTorrent+TCP packet-block-piece-file-torrent stacking – swift 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.

+ +

Datagram-level integrity checks

+

swift 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.

+ +

NAT traversal by design

+

The only method of peer discovery in swift is PEX: 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.

+ +

Subsetting of the protocol

+

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.

+ +

Push AND pull

+

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.

+ +

No transmission metadata

+

Unlike BitTorrent swift 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.

+ + + +

Specifications and documentation

+ + + + + +

Downloads

+ + + + + +

Frequently asked questions

+ +

Well, why swift?

+

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 swift to keep the name formally unique (for some definition of unique).

+ +

How is it different from...

+ +

...TCP?

+

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 swift is the congestion control, but... we need our own custom congestion control! Thus, we did not use TCP.

+

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.

+ +

...UDP?

+

UDP, which is the thinnest wrapper around IP, 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.

+ +

...BitTorrent?

+

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.

+ +

...µTorrent's µTP?

+

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.)

+

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 LEDBAT 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 µTP. The swift 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.

+ +

...Van Jacobson's CCN?

+

Van Jacobson's team in PARC is doing exploratory research on content-centric networking. 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 (<10 years) while CCNx is a much more ambitious rework.

+ +

...DCCP?

+

This question arises quite frequently as DCCP is a congestion-controlled datagram transport. The option of implementing swift over DCCP 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.

+ +

...SCTP?

+

+SCTP 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).

+ +
+ + + +
+ +

Who we are

+ + +

Contacts & feedback

+ + + +
+ + + +
+ + + diff --git a/src/libswift_udp/doc/mfold-article/IEEEtran.bst b/src/libswift_udp/doc/mfold-article/IEEEtran.bst new file mode 100644 index 0000000..90acb4c --- /dev/null +++ b/src/libswift_udp/doc/mfold-article/IEEEtran.bst @@ -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 index 0000000..5681714 --- /dev/null +++ b/src/libswift_udp/doc/mfold-article/IEEEtran.cls @@ -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 index 0000000000000000000000000000000000000000..339ac14d868e6369cd5175fc45db8b51ac063622 GIT binary patch literal 159796 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